Помню свое первое впечатление от знакомства с vi - случайно запущенный по неосторожности на сервере редактор оказался ловушкой: попробуй-ка выйти!
Знакомые по DOS/Windows-редакторам кнопки не работают, ужас, кошмар, плохой vi! И зачем он такой нужен?!
Но стало интересно, зачем же.
К счастью, если запустить vim без аргументов, без редактируемого файла - первое же, что он пишет - предложение почитать документацию, буквально RTFM.
Почитал, попробовал, понял - и вот уже много лет с тех пор не понимаю, как можно пользоваться другими, совершенно неудобными редакторами...
(даже не буду пытаться описывать все возможности - их слишком много, и постоянно до сих пор всплывает что-то новое и удобное)
Во-первых, самая мозголомающая концепция - это то, что редактор работает в двух разных режимах, официально они называются иначе, но назову их "командный режим" и "режим редактирования". И когда вы открываете файл - сначала это именно командный режим.
То есть, в отличии от привычных редакторов, где вы сразу попадаете в единственный "режим редактирования" - здесь нельзя просто так взять и начать набивать буковки.
Вместо этого вы сначала видите файл как бы в просмотре, потом вносите в него какую-то правку, и снова переходите в просмотр (опционально - сохраняете изменения).
Вот эта концепция "отдельных правок" - второе, что ломает привычный способ работы с текстом или кодом.
Можно, конечно, просто "начать редактирование", потом закончить, сохраниться и выйти - но тогда незачем использовать vim. Удобным он становится как раз при сочетании этих двух режимов.
Почему так? Зачем это? А для этого надо немного вспомнить историю техники (в манах этого нет, кстати):
Вообще Vim - это vi-improved, улучшенный vi, а первоначальный vi создавался в староглиняные времена, когда терминалы были большими и железными, и работали через serial line: не по ssh, не по telnet, и даже вообще не по сети, а через RS232 или какой-нибудь RS485.
По проводам передавались закодированные ASCII- (или даже не ASCII) байты символов, например 0x41 'A', 0x33 '3', 0x1b 'ESC'.
Привычных кнопок Ctrl-Alt на терминале могло не существовать вообще, а если и существовали - они могли передавать разное, по разному кодированное, в зависимости от типа терминала.
Даже привычные сейчас стрелки - под вопросом. Но буквы, цифры и несколько спецкодов типа ESC, CR (Enter) - были всегда.
Кроме того, помехи на физических линиях, потерянные байты - сейчас связь есть и вы редактируете файл, а вот пропадание связи и теперь неизвестно, что там успело сохраниться и в каком виде.
И вот в этих условиях как раз и работал vi, позволяя с одной странной железяки подключиться к другой - и успешно что-то там отредактировать.
Вместо стрелок могли использоваться буквы h,j,k,l,|,$ которые позволяли подвести курсор в нужное место, буква i включала режим редактирования, кнопка ESC выключала, а символ ':' позволял ввести и выполнить команды, например w - write и q - quit.
Сокращался обьем отредактированного несохраненного текста - потенциальная проблема при нестабильном соединении.
Вот, кстати, и ответ на главный вопрос: "как из него выйти?" - ESC, :q - без сохранения, или ESC, :wq - с сохранением того, чего там наменяли.
А если наменяли, и он не хочет просто так выйти без сохранения - добавить '!' - "я настаиваю!": ESC,:q!
Всё, страшный монстр побежден.
Пока вы не выполнили команду :w - изменения не сохраняются.
Написали что-то не то, и хотите всё отменить - не страшно, ESC:q!
Сбой на линии, и "написался какой-то мусор"? - ESC:q!
Связь тормозит, глючит, и вместо 1 строки вы случайно удалили 101? - ESC:q!
Большинство сейчас с такими проблемами не столкнется, но если вдруг вам приходится с мобильного телефона посреди поля через Termuis пробиваться к заблокированному серверу с 50% потерь пакетов - vi самое то, что надо.
А вот в более благоприятных условиях особенности постепенно начинают превращаться в удобства.
Во-первых, любой файл изначально у вас в режиме просмотра (а стрелки в vim в нормальной терминальной программе прекрасно работают): пока вы не начнете его редактировать - он не меняется (и всегда можно всё отменить - ESC:q!).
Во-вторых, довольно удобная система этих самых команд, у нее есть своя внутренняя логика.
Конечно, можно просто сразу нажать i, перейти в режим редактирования - и дальше как в простом текстовом редакторе, единственное отличие в том, что в конце работы нажать ESC, выйти в командный режим и сохранить :wq
Но команды действительно удобны.
Их можно условно разделить на несколько видов: односимвольные, двухсимвольнные и "сложные".
Пример односимвольных - h,j,k,l,|,$ - движение курсора (замена стрелок), w,W,e,E - переходы между словами, {,} - переходы между абзацами и так далее.
Команды перехода в режим редактирования - i,I,a,A,o,O - тоже можно отнести к односимвольным, всего одно нажатие - и вы переходите в режим редактора, хоть и немного по разному.
Отдельно можно вспомнить про команды . - повтор последней операции, u - отмена последней операции.
Просто начать что-то писать - i, начать с начала строки - I.
Начать писать после текущей буквы - a, дописать в конец строки - A.
Начать со строки ниже - o, со строки выше - O.
Удалить букву и начать прямо отсюда - s, удалить всё с начала строки и начать писать заново - S.
Заменить букву или цифру на месте - r, писать поверх сущестующего текта - R.
Удалить одну букву - x, удалить букву перед ней - X.
Заменить регистр на БОЛЬШОЙ или маленький - ~.
Символ p - вставка после, P - вставка до.
Навигация по файлу : gg - в начало, G - в конец (gg - 2 два символа, но это команда-исключение, да и вообще это всё условно).
Буква e/E - переход к концу слова, w/W - к началу следующего (разный регистр - немного разное поведение в оценке символов).
Начало строки - |, конец - $, абзацы вверх-вниз - { }.
Если тексте блок заключен в скобки, типа { ... } или ( ... ) - например, функция в C - '%' поможет перейти с одной на другую.
Символ # на слове - находит и выделяет в тексте все такие слова.
Двухсимвольные - ну например dd, удаление строки, или yy - копирование строки.
Их можно модифицировать: например, есть команда перехода к следующему слову - w, и команда перехода к концу файла G, так вот, если команда dd удаляет строку, то dw - удаляет слово, а dG - всё до конца файла.
Аналогично с yy - копирует строку, yw - копирует слово, yG - копирует всё до конца файла.
То есть, второй символ тут как раз модифицирует команду удаления или копирования.
Очень удобная команда cc (и ее модификации cw,ce,c$,cG) - удаляем текущую строку (слово, всё до конца строки или до конца файла) и пишем новое.
Здорово экономит время.
Также можно модифицировать команды, вводя перед ними количество повторов: 10dd - удалить 10 строк, например.
От команды 5j - перейти на 5 строк ниже - польза неочевидна, но для 5dw уже другое дело, когда нужно поправить несколько однотипных записей, убрав из них как раз по 5 слов.
Или если понадобилось вставить 100500 одинаковых записей (иногда и такое надо) - это будет буквально 100500p
Еще есть "визуальное выделение": v выделяет участок строки, V - строку целиком, Ctrl-v - блок.
Останется скопировать выделенное yy, и потом сделать вставку, p - после или P - перед текущей позицией, в этом случае так же, как выделялось - строкой или блоком.
Сложные команды сложны потому что они очень разные.
Переход к ним - :, затем сама команда, например: w - записать, q - выйти, q! - настоятельно выйти.
Команда :w filename - запишет в файл filename
Команда :sp - разделит экран пополам горизонтально, и в каждой половине будет текущий файл. Переключение между ними - Ctrl-w w (или Ctrl-w Ctrl-w).
Команда :sp filename - сделает то же самое, но вторым файлом будет filename, удобно когда надо вносить правки в оба файла, или копировать из одного в другой.
Команда :vsp работает аналогично, но разделяет экран вертикально.
Команда :ter откроет во второй половине терминал, например чтобы ввести какие-то команды в шелле: make, git, rsync, tar - мало ли что можно делать.
Команда :!command сразу выполнит команду в шелле ОС.
Команды типа :set something / :set nosomething позволяют включать и выключать разные фичи, типа как set number включит нумерацию строк, set tabstop=2 установит размер TAB в 2 символа вместо стандартных 8, и так далее.
Отдельный вид команды - переход по известному номеру строки: помните ошибки компилятора типа "error at line 313434" - вот, :313434 - эта самая строка, смотри, исправляй.
Есть поиск строк: /string - находим все строки "string", перемещаемся между ними - n.
Если нужно в коде заменить "primary" на "secondary", особенно с визуальным контролем чтобы не изменить лишнего: /primary, ce "secondary", ESC, n, ., n, ., n, ., ...
Есть такая штука как макросы: запись последовательности некоторых действий, которую можно повторять неоднократно.
Макросы именуются буквами и цифрами, запись начинается как q<буква>, заканчивается повторным нажатием q.
Записанный макрос вызывается как @<буква>
Иногда такое может быть удобно, чтобы вместо "аккордов на клавиатуре" сделать это один раз, и потом повторять через @x и .
Например, нужно зайти в код и временно закомментировать несколько строк, не блоком:
Можно, конечно, аккордами в режиме редактирования: Home "// " Down, Home "// " Down - но это напрягает, если строк достаточно много.
Можно чуть ускорить: |, i, "// ", ESC - закомментировали эту строку, а потом просто Down, . - стрелка вниз, повтор команды.
Можно еще ускорить, сделать макрос: qa Home i "// " ESC Down q - записали не только вставку комментария, но и переход на следующую строку. Затем вызываем его @a, @a, или сразу 10@a - комментируем блок 10 строк.
Есть проставление меток: например, вы разбираете ошибку в коде, и где-то в функции func1 нашли вызов func2, которая вызывается где-то еще в 100500 местах, хотите быстро посмотреть что именно она делает, а потом ВЕРНУТЬСЯ сюда:
Ставим метку m<буква>, ищем описание функции /func2, n, n, n - где-то нашлось, посмотрели, поняли, возвращаемся - '<буква>
Хотя можно и иначе: открыть этот же файл во втором окне :vsp, точно так же найти описание функции /func2, n, n, n - посмотреть, закрыть это окно :q
Есть такая фича как визуальный diff.
Вообще, для этого есть специальная команда vimdiff file1 file2, но поскольку это всё vim - можно сделать это из режима редактирования:
Допустим, мы правим file 1, и нужно сравнить его с file2: Открываем второй :vsp file2, задаем команду сравнения :windo diffthis
И вот у нас четко видно, где что так, а что не так.
Конечно, вспомнить сходу вот это "windo diffthis" малореально, но на этот случай у vim есть .vimrc, конфиг-файл.
Конфиг может храниться в домашнем каталоге - он будет применяться всегда у юзера, а может в локальном - конкретно для этого каталога.
В нем можно писывать настройки, типа syntax on, set number и прочее, и можно определять функции, вызываемые по желанию, например так:
nnoremap <F3> :windo diffthis<CR>
При нажатии F3 включится vimdiff для открытых окон. Чтобы отменить этот режим нужно ввести команду :windo nodiffthis, а чтобы не делать это ручками - пишем команду-близнеца:
nnoremap <S-F3> :windo nodiffthis<CR>
В данном случае S-F3 - это Shift-F3. Посмотреть, какие модификаторы как выглядят очень просто: войти в режим редактирования и просто нажать Shift-F3, Ctrl-F3 и т.д. - оно напишет.
Возможно, где-нибудь на Ctrl-F4 может оказаться закрытие окна терминала на уровне графической системы - но это уже не вина vim.
Подобных функций может быть много разных, например можно автоматизировать простановку текущего времени, что может пригодиться при комментировании типа "внесено изменение такое-то, дата такая-то"
nnoremap <F5> "=strftime("%Y-%m-%d %H:%M")<CR>p
Можно создать функцию, выполняющую внешнюю команду, например, отправку текущего файла по scp (или запуск компилятора, или еще что-то):
nnoremap <F2> :call SendFile()<CR> function! SendFile() abort let l:file = expand('%:p') let l:cmd = 'scp ' . shellescape(l:file) . ' remote_server:path/' let l:ans = toupper(input('Send file? Y/N: ')) if l:ans !=# 'Y' echo 'Aborted' return endif echo ' Running: ' . l:cmd call system(l:cmd) redraw! endfunction
Или усложнить, добавив выбор вариантов:
function! SendFile() abort let l:file = expand('%:p') echo 'Select server: ' let l:urlsstr = ['1 Alfa', '2 Bravo', '3 Charlie'] let l:urls = ['server1', 'server2', 'server3'] let l:idx = inputlist(l:urlsstr) echo "selected " . l:idx let l:idx = l:idx - 1 echo "selected " . l:idx if l:idx == -1 return endif let l:serv = l:urls[l:idx] let l:cmd = 'scp ' . shellescape(l:file) . ' '. l:serv . ':path/' echo ' Running: ' . l:cmd call system(l:cmd) redraw! endfunction
Это бывает удобно, когда делаешь какие-то изменения, а потом выполняешь типовую команду чтобы их применить, и всё это неоднократно.
Тем более что .vimrc можно не делать с нуля, а просто скопировать старый, чуть подправив под новые задачи.
А если даже копировать ради штучных задач лениво - можно просто открыть терминал :ter - в нем можно выполнить какие-то операции, и закрыть его когда больше не нужен.
Ну и наконец, никто не заставляет сидеть в черно-белом терминале.
У vim есть встроенные цветовые схемы, которые в разных случаях для разной подсветки синтаксиса могут быть по-разному удобны.
Например, можно сделать так:
map <F4> :execute RotateColor()<CR>:colorscheme<CR> let b:colindex=0 function! RotateColor() let y = -1 while y == -1 let colstring="#darkblue#default#industry#murphy#peachpuff#torte#" let x=match(colstring,"#",b:colindex) let y=match(colstring,"#",x+1) let b:colindex=x+1 if y == -1 let b:colindex=0 else let str=strpart(colstring,x+1,y-x-1) return ":colorscheme " .str endif endwhile endfunction
и нажатием одной кнопки менять вид окна в зависимости от языка или времени суток.
Мелочь, но иногда удобно.
Конечно, “а вот в IDE есть …” и “можно просто открыть еще одно окно!” - но напомню, что vim создавался прежде всего как инструмент администратра, поэтому он почти наверняка есть на каком-нибудь удаленном сервере, или его можно там быстро установить, не заморачиваясь с графикой, тасканием туда-сюда картинок - и работать он будет так же, как локальный.
Текстовые .vimrc, если надо, элементарно копируются.
Да, управление неочевидно, нет наглядного меню, это надо прочитать в документации, попробовать и запомнить.
Но с другой стороны, освоить всё это не сложнее, чем управление в каком-нибудь авиасимуляторе - а с этим справляются даже дети…
В общем, возможностей у vim много. И это еще почти не упомянута подсветка синтаксиса, проверка орфографии, смена кодировок на лету, и т.д. и т.п.
Armann
Автор, горячо уважаю ваш труд, но почему то все эти мануалы по vim только напоминают анекдот по секс в гамаке