Найти тему
Блокнот математика

Рубрика "Секреты Вим". Имя редактируемой функции

Привет, коллеги! Такую задачу, наверное, решал каждый программист-вимер: как сделать так, чтобы видеть имя процедуры, в теле которой в данный момент находится курсор. Во всяком случае, я встречал в интернете несколько раз такие решения.

Для языков типа Си это довольно мудрёно, поскольку опознать функцию не так-то просто; с другой стороны, Вим и сам помогает чем может. Я же пишу в основном на Фортране, и мне чуть легче. Далее - мой вариант.

Я сначала я попробовал симулировать действия вручную: поставить метку в позиции курсора через :mark, произвести поиск, запомнить найденное имя, вернуться... Но, во-первых, экран заметно дергается, во-вторых, подтормаживает, а в-третьих, возврат происходит со смещением строк на экране, что раздражает. Должно быть всё незаметно.

Что же. Сначала создаем функцию, которая просто ищет вверх слово subroutine или function:

function! SubName()
let lineN=search('^\s*subroutine\|^\s*\%(pure\|elemental\|recursive\)\{1}\s*function', 'bnW')
if lineN==0
return '--'
endif
let line=getline(lineN)
let m=matchlist(line, '^.\{-}\%(tine\|tion\)\s\+\(\w\+\)')
if len(m)<1
return '---'
endif
let name=m[1]
return name
endfunction

Сначала мы ищем заголовок процедуры функцией search. Она ищет от позиции курсора по указанному регулярному выражению, которое имеет вид

^\s*subroutine\|^\s*\%(pure\|elemental\|recursive\)\{0,1}\s*function

и состоит из двух альтернатив, разделенных символом \|

Первая альтернатива - это "начало строки, пробелы и слово subroutine", вторая тоже "начало строки и пробелы", затем незахватывающие скобки \%(...\) указывают три альтернативы, которых должно быть не более одной (квантификатор \{0,1}).

Можно и усложнять, я специально выбрал простой вариант. Не атомная станция, чай.

Второй аргумент функции содержит флаги, настраивающие поведение. Первый флаг b предписывает вести поиск назад, вверх по тексту. Второй, n, велит не смещать позицию курсора. Третий, W, запрещает переход в конец файла, если выше по тексту искомое не нашлось.

Функция возвращает номер строки, в которой нашлось совпадение, или нуль, если не нашлось. В последнем случае мы возвращаем строку '--' и выходим.

Извлекаем строку по номеру функцией getline.

Ищем в ней имя процедуры функцией matchlist: по выражению

^.\{-}\%(tine\|tion\)\s\+\(\w\+\)

От начала строки (^) может идти что угодно (.) в любом количестве, но чем меньше, тем лучше (\{-}), а затем либо tine, либо tion в незахватывающих скобках \%(...\), после которых пробелы (один или более, \+) и искомое имя (\w\+, то есть словные символы, один или более), уловленное скобками \(...\)

Мы получим список, в котором под номером 0 пойдет всё совпадение, а под номером 1 содержимое скобок. Либо это пустой список, если совпадение не нашлось (это возможно, если текст не на фортране, и после слова function идет что-то не то). В этом случае мы вернем строку '---'

Если же совпадение нашлось, мы его и вернем.

Разумеется, функцию можно сократить и оптимизировать. Я осознанно написал подробно и понятно.

Теперь нам надо, чтобы эта функция вызывалась при каждом смещении курсора. Можно отловить и каждое нажатие на клавишу, но есть способ лучше: строка информации. Всё равно имя функции будем отображать там. Итак:

:setlocal statusline+=\ [IN:%{SubName()}]

Запись += означает, что надо к строке добавить некоторый текст (потом можете убрать через -= и это очень полезно, если в функции допущены ошибки). Пробел экранирован слешем. Квадратные скобки - только для красоты. Поле %{имя_функции()} просто заменяется на результат функции, то есть на найденное имя или строку -- или ---. Последнее позволяет, например, понять, что допущена опечатка в заголовке.

В основном, это будет работать правильно. Вы будете всегда знать, в теле какой функции находится курсор. Это бывает очень полезно!

Но помните, что разработка не учитывает вообще всех нюансов! Если у вас подпрограмма с атрибутом PURE, то функция собьётся, например. Если вы ниже последней процедуры в файле, или в комментариях выше курсора есть слово function. Да мало ли! Не делайте резких движений, основываясь на её показаниях! Это помощь, а не руководство к действию.

Можно настроить распознавание типа файла так, чтобы эта возможность включалась только для файлов на Фортране. Описанную выше функцию надо поместить в файл ~/.vim.ftdetect/fortran.vim (или другой путь из runtimepath) и добавить туда ещё автокоманды, например такие:

au BufRead,BufEnter *.f90 set statusline-=\ %{SubName()} | set statusline+=\ %{SubName()}
au BufLeave *.f90 set statusline-=\ %{SubName()}

Первая будет реагировать на чтение файла или просто вход в буфер и она сначала попробует убрать из строки информации имя функции, а потом добавит. Это позволит избежать удвоения. При выходе из окна (вторая автокоманда) данное поле будет убираться. В итоге для других файлов может отработать своя функция.

Для других языков функция будет, естественно, другая. И для вашего любимого языка сделать такое несложно, используйте мою наработку как образец. В принципе, можно попробовать сделать единую функцию, которая будет работать для всех языков сразу...

И прежде потестируйте функцию через :echo SubName() А потом уже пробуйте строку информации. Потому что если что-то не так, починить можно, но неподготовленного человека вал сообщений об ошибках может и напугать.

Удачи, коллеги!

Научно-популярные каналы на Дзене: путеводитель
Новости популярной науки12 марта 2022