Помню свое первое впечатление от знакомства с 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=0function! 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 endwhileendfunction
и нажатием одной кнопки менять вид окна в зависимости от языка или времени суток.
Мелочь, но иногда удобно.
Конечно, “а вот в IDE есть …” и “можно просто открыть еще одно окно!” — но напомню, что vim создавался прежде всего как инструмент администратра, поэтому он почти наверняка есть на каком-нибудь удаленном сервере, или его можно там быстро установить, не заморачиваясь с графикой, тасканием туда-сюда картинок — и работать он будет так же, как локальный.
Текстовые .vimrc, если надо, элементарно копируются.
Да, управление неочевидно, нет наглядного меню, это надо прочитать в документации, попробовать и запомнить.
Но с другой стороны, освоить всё это не сложнее, чем управление в каком-нибудь авиасимуляторе — а с этим справляются даже дети…
В общем, возможностей у vim много. И это еще почти не упомянута подсветка синтаксиса, проверка орфографии, смена кодировок на лету, и т.д. и т.п.
ссылка на оригинал статьи https://habr.com/ru/articles/1066250/