Привет, коллеги. Поговорим об исключениях. Концепция в программировании хорошо известная, и в Виме тоже есть.
Исключение — это сообщение; в частности, об ошибках или прерываниях (когда пользователь нажал, скажем, ctrl+C), но не обязательно: это могут быть любые сообщения. Эти исключения можно ловить и обрабатывать; в частности, так можно перехватить ошибку и исправить ее. Например, запись на диск не удалась, но это же не повод завершать работу скрипта: попробуйте еще раз или другой диск. Кроме того, можно выполнить "перед смертью" некоторый код, подчистив за собой. Например, восстановив значения глобальных опций. Исключения можно бросать, часто это делает Вим, но можно сделать это и самому, о чем далее.
Имеется команда try, после которой идет какой угодно код, и вся конструкция завершается endtry. Между try и endtry, но после вашего кода, могут идти блоки catch и, последним, блок finally. Команда catch может быть снабжена регулярным выражением в //.
Тогда, если произойдет исключение, блок catch позволит его обработать. Блок catch без выражения ловит всё. А блок finally выполняется всегда, аварийная остановка будет уже после его завершения.
Пример:
try
w existing.file
catch /E139/
w! existing.file
endtry
Да, здесь мы могли сразу перезаписывать файл; но можно же запросить новое имя, сделать копию, предупредить пользователя...
E139 — это код ошибки. Вообще же при ошибках бросается исключение вида Vim(имя команды):сообщение или Vim:сообщение.
Ловить текст сообщения (регулярным выражением) можно, но надо учитывать, что при локализациях текст может быть переведен. Код ошибки как часть сообщения — хороший вариант. Но можно ловить и по имени команды, только имя будет полное:
catch /write/
Исключения можно "кидать" командой throw, которая принимает выражение. Значение этого выражения и будет тем текстом, которое можно ловить командой catch. Например, можно "кинуть" номер строки, на которой вызвана функция, и поймать его вне функции:
throw line('.')
Чтобы определить, что именно вы поймали, можно добавить немного информации:
throw "Shit happened at line " . line('.')
Нельзя только, чтобы текст исключения начинался с "Vim" — это для системных вимовских исключений. Можно разобраться, что именно вы поймали, уже на этапе обработки: см. далее.
Можно просто использовать конструкцию как множественный выбор:
try
throw x
catch /1.*/
обработка случая 1
catch /2.*/
обработка случая 2
catch
если ничего не подошло
endtry
Блоки catch проверяются по-очереди, выполняется только один, первый подошедший. Блок catch без выражения подходит всегда, поэтому его разумно располагать последним. Те, что после него, никогда не совпадут.
Поскольку регулярное выражение может быть весьма общим, то точное значение совпавшего исключения доступно в переменной v:exception. Например,
catch /\d\+/
поймает число, но какое именно? Само число и будет в этой переменной. Где исключение было брошено — подскажет переменная v:throwpoint. Это может быть полезно для отладки. Узнаете функцию и строку, на которой произошла ошибка или вручную брошено исключение.
Деление на нуль не является ошибкой в Вим! Если хотя бы одно число вещественное, это inf (если делите не-нуль на нуль) или nan (если деление нуля на нуль); а если оба целые, то просто максимальное число, допустимое типом int. У меня это 2³¹-1 (0/0 дает отрицательное число -2³¹).
Блок finally выполняется и тогда, когда блок try или catch заканчивается командой continue, break, return или finish. Это бывает полезно: речь о том, что блок finally подчищает за собой перед тем, как уйти. Уйти насовсем, прервав скрипт, или уйти из функции, или из цикла. Не столь важно, откуда именно: главное, прибрать за собой.
Помимо локального использования, все эти конструкции могут использоваться глобально, например, для отлова ошибок в вызываемой функции. Исключение брошено внутри, а поймано снаружи. Это очень полезно!
Ловить можно и прерывания: это когда пользователь нажал <C-C>. Оно превращается в исключение "Vim:Interrupt" и ловится как и любое другое, скрипт при этом не прекращает работу. Так можно сделать неубиваемый скрипт (но я вам этого не говорил!). Но на самом деле, эта возможность нужна для другого. Например, подчистить за собой или сохранить что-то ценное.
Отлов исключений полезен и для отладки:
catch
echo "Internal error (" . v:exception . ")"
echo " - occurred at " . v:throwpoint
И вы узнаете немного больше, чем вам сообщил бы Вим. Об отладке будет отдельный материал.
Удачи, коллеги!
Что уже было по скриптовому языку Вим: