📱 Подписаться
IT и цифровая трансформация

Vim против всех: редактор, в который можно войти, но нельзя выйти

📰 Habr 👁️ 0 просмотров

MainEl11 минут назадОбъяснить с

Vim против всех: редактор, в который можно войти, но нельзя выйти

Уровень сложностиПростойВремя на прочтение10 минОхват и читатели260Текстовые редакторы и IDE**nix*История ITПрограммирование*СофтРетроспективаСценарий повторяется с завидной регулярностью. Человек ставит Git, жмет Next во всех окнах установщика (включая то, где мелким шрифтом предлагается выбрать редактор по умолчанию), через неделю делает коммит, забыв указать сообщение прямо в команде, и оказывается перед черным экраном, вдоль левого края которого сверху вниз тянется столбик тильд. Курсор мигает в пустой первой строке, ниже идут строки с решеткой в начале, а внизу висит имя какого-то служебного файла с количеством строк и байтов. Кнопок нет, меню нет, крестика в углу тоже нет, поэтому пальцы делают то, что делают в любой консоли, и набирают exit.

После этого на экране происходит нечто странное, хотя каждое действие редактора строго логично. Буква e в Vim означает переход в конец следующего слова, и курсор прыгает с пустой строки на решетку во второй строке, потому что одиночный символ пунктуации тоже считается словом. Буква x удаляет символ под курсором, и решетка исчезает. Буква i переключает редактор в режим вставки, а t наконец-то печатается как обычная буква. В итоге служебная строка «# Please enter the commit message for your changes», которую Git собирался выбросить как комментарий, превращается в «t Please enter the commit message for your changes» и становится полноценной частью будущего коммита, а внизу экрана загорается надпись INSERT.

Теперь редактор в режиме вставки, и все, что человек набирает дальше (quit, еще раз exit, :q, отчаянное help), аккуратно ложится в текст коммита. Ctrl+C в старых версиях не делал ничего полезного, закрыть окно терминала можно, но тогда где-то остается висеть swap-файл, о котором Vim вежливо напомнит при следующем запуске. Ловушка захлопывается без единой ошибки в логике программы: редактор с первой до последней клавиши делал ровно то, что ему сказали, просто сказали ему это на языке, который придумали в 1976 году для терминала, давно сданного в музей. С этого терминала и стоит начать, потому что почти все странности Vim объясняются железом, которого уже нет.

Редактор для смертных

Родословная Vim начинается с ed, строчного редактора, который Кен Томпсон написал для ранней Unix в самом конце шестидесятых. Работали тогда на телетайпах, печатающих на бумажной ленте, и никакого «экрана с текстом» не существовало в принципе: человек вслепую отдавал команды вроде «покажи строки с пятой по десятую» или «замени в третьей строке одно слово на другое», а редактор отвечал распечаткой. На любую ошибку ed отвечал одним вопросительным знаком, без пояснений, и среди старых юниксоидов до сих пор ходит шутка, что этот вопросительный знак был самым развернутым сообщением об ошибке в истории ОС.

В 1976 году в Беркли попал редактор em, расширение ed, которое Джордж Кулурис сделал в Лондоне и назвал «editor for mortals», то есть редактор для простых смертных (бессмертным, по замыслу, хватало и ed). Аспиранты Билл Джой и Чак Хейли переделали его в ex, а Джой добавил к ex визуальный режим, в котором текст наконец-то показывался на экране целиком и правился прямо в нем. Этот режим вызывался командой vi, от слова visual, и к концу семидесятых получил собственное имя в дистрибутиве BSD. Отсюда первая разгадка: все команды через двоеточие в Vim (выход, сохранение, поиск с заменой) остались от ex, и набирая их, пользователь на секунду проваливается в редактор, который старше большинства читателей Хабра.

Вторая разгадка лежит в терминале Lear Siegler ADM-3A, за которым Джой писал свой код. На его клавиатуре стрелки были нарисованы прямо на клавишах H, J, K и L, поэтому и в vi курсор гоняют этими буквами. Клавиша Esc стояла там, где у современной клавиатуры находится Tab, то есть под левым мизинцем, и нажимать ее было удобно, а двоеточие набиралось без Shift. Клавиша Home делила место с тильдой, из-за чего тильда в Unix до сих пор обозначает домашний каталог. Через пятьдесят лет Esc уехал в дальний угол клавиатуры, а привычки, рассчитанные на старую раскладку, остались, и большая часть мучений с Vim растет ровно из этого переезда.

Третья разгадка в скорости связи. Джой вспоминал в интервью, что работал через модем на 300 бод. При десяти битах на символ (старт, восемь бит данных, стоп) это 30 символов в секунду, а полный экран терминала 80 на 24 содержит 1920 символов: 1920 / 30 = 64 секунды на одну перерисовку. Пока экран выводится заново, можно сходить за кофе, поэтому редактор старался перерисовывать как можно меньше, а команды делал как можно короче, по одной букве на действие, без модификаторов и подтверждений. А раз каждая буква на клавиатуре уже занята какой-то командой, текст приходится набирать в отдельном режиме, куда нужно специально войти и откуда нужно специально выйти. Так и появилась та самая конструкция, в которой человек с улицы застревает на первой же минуте.

Ты не пройдешь

В привычных редакторах клавиатура всегда делает одно и то же: буквы печатают буквы, а команды висят на сочетаниях с Ctrl. В Vim у клавиатуры несколько личностей. Основная, так называемый нормальный режим, превращает каждую клавишу в команду, режим вставки печатает текст, визуальный режим выделяет, а командная строка после двоеточия принимает команды от ex. Переключение между ними происходит без всякого предупреждения, и хуже всего то, что в нормальном режиме внизу экрана не пишется ничего. Надпись INSERT появляется только в режиме вставки, а когда ее нет, новичок не понимает, где он находится. Представьте Caps Lock, который переназначает всю клавиатуру целиком и у которого вдобавок нет лампочки.

Предположим, человек каким-то чудом нашел Esc, вспомнил :q из какого-то мема, и нажал Enter. Vim отвечает красной строкой с ошибкой E37, в которой сказано, что изменения не сохранены и что для принудительного выхода нужно добавить восклицательный знак, и никуда не выходит. Редактор отказывается закрываться, пока в буфере есть несохраненные изменения, а изменения там есть, потому что все предыдущие попытки выбраться (то самое exit, набор quit и прочая паника) успели что-то удалить и что-то вписать. Ловушка работает по принципу китайской ловушки для пальцев: чем сильнее дергаешь, тем крепче держит, и каждая неудачная попытка выхода добавляет в файл новые правки, из-за которых следующий :q снова откажет.

Отдельного внимания заслуживает буква q, нажатая сама по себе. В нормальном режиме она начинает запись макроса в регистр, имя которого будет следующей нажатой буквой, и внизу появляется загадочная надпись recording. Человек хотел выйти, а редактор начал внимательно записывать каждое его движение, чтобы потом воспроизвести их по команде. Остановить запись можно той же q, но откуда новичку это знать?

Правильных выходов при этом дофига, просто ни один из них не угадывается интуитивно. Восклицательный знак из подсказки к E37 означает «выйти и выбросить изменения», так что :q! закрывает редактор при любом состоянии буфера. Команды :wq и :x сохраняют и выходят, двойная заглавная ZZ делает то же самое прямо из нормального режима без двоеточия, а ZQ выходит без сохранения. Для случая с коммитом разница между ними принципиальная. После :q! Git увидит файл в исходном виде, то есть только с комментариями, сочтет сообщение пустым и отменит коммит, что в такой ситуации и требовалось. После :wq в истории репозитория навсегда останется коммит с сообщением «t Please enter the commit message for your changes», и убрать его оттуда без переписывания истории уже не выйдет.

Добро пожаловать в отель «Stack Overflow»

Масштаб проблемы хорошо виден по Stack Overflow. Вопрос «How do I exit the Vim editor?» появился там в 2012 году, а в 2017-м команда сайта написала в своем блоге отдельный пост о том, что он набрал больше миллиона просмотров, и снабдила его графиками, как будто речь шла о сезонной эпидемии. Получается, что миллион человек, у каждого из которых под рукой был компьютер с интернетом, не смог выйти из текстового редактора без посторонней помощи, и каждый из них пошел гуглить с соседней вкладки или с телефона, оставив Vim терпеливо мигать курсором.

Вокруг этого выросла целая субкультура. Самая известная шутка предлагает генерировать случайные строки, усадив новичка за Vim и попросив его выйти, и с учетом разобранного выше про exit в ней есть заметная доля правды. На GitHub лежит репозиторий с коллекцией способов выхода, где наряду с нормальными командами встречаются принудительное убийство процесса из соседнего терминала, перезагрузка машины и выдергивание вилки из розетки, причем последний способ выглядит самым надежным из всех. Обратная сторона тоже не забыта: опытные пользователи шутят, что выйти из Vim нельзя потому, что незачем.

Есть и менее очевидная ловушка, в которую попадают уже те, кто кое-что знает про Unix. Сочетание Ctrl+Z в терминале приостанавливает текущий процесс и отправляет его в фон, и Vim послушно исчезает с экрана, возвращая привычное приглашение командной строки. Выглядит как успешный выход, однако процесс никуда не делся и спит, держа открытый файл. Через пару таких «выходов» в фоне скапливается несколько спящих редакторов, а при попытке закрыть терминал оболочка предупреждает, что у нее остались остановленные задачи. Вернуть беглеца на экран можно командой fg, после чего выходить придется уже по-настоящему.

Сами разработчики Vim про проблему тоже в курсе. В версиях последних лет нажатие Ctrl+C в нормальном режиме выводит внизу подсказку с предложением набрать :qa! и нажать Enter, чтобы отказаться от всех изменений и выйти, то есть редактор сам объясняет, как из него сбежать. Ушло на это примерно три десятилетия с момента первого релиза, но лучше поздно чем никогда.

Стокгольмский синдром как метод обучения

Если бы история заканчивалась на ловушке, Vim давно лежал бы в одном архиве с ed. Однако те, кто однажды разобрался с режимами, выходить обычно уже не хотят, и причина кроется в том самом нормальном режиме, который так пугает новичков. Команды в нем складываются по правилам, очень похожим на грамматику: есть глаголы, есть указания, куда действовать, есть числительные, и из небольшого словаря собирается огромное количество фраз.

Глаголов в этом языке немного, и основные из них запоминаются за один вечер: d удаляет, c удаляет и сразу переводит в режим вставки, y копирует. Движения тоже однобуквенные: w ведет к началу следующего слова, e к концу слова (та самая буква из exit), а f с любым символом после нее прыгает к этому символу в строке. Склеиваем глагол с движением и получаем фразы вроде dw, то есть «удали до следующего слова». Отдельный класс составляют текстовые объекты, которые описывают кусок текста вокруг курсора, например «внутри кавычек» или «абзац целиком». Поэтому ci" (буквально «измени внутри кавычек») стирает содержимое строки в кавычках, где бы внутри нее ни стоял курсор, и сразу дает вписать новое, а dap выкидывает абзац. Число перед командой повторяет ее, так что 3dw удаляет три слова, а точка повторяет последнее изменение целиком, включая набранный в режиме вставки текст.

Выигрыш от такой схемы легко посчитать на пальцах. Пять глаголов и двадцать движений дают сотню команд, и учить из них приходится только двадцать пять элементов, а дальше комбинации получаются сами. В редакторе с горячими клавишами каждая из этой сотни потребовала бы своего сочетания, и половину из них никто бы так и не назначил. Человек, освоивший такую грамматику, начинает думать о правке в терминах «замени аргумент функции» или «удали этот абзац», и пальцы выполняют задуманное за три-четыре нажатия, не уходя с основного ряда клавиатуры. Сверху надстраиваются макросы: буква q с именем регистра начинает запись, повторное нажатие q ее останавливает, а записанную последовательность можно прогнать хоть сто раз подряд по следующим строкам. Та самая q, которая пугала новичка надписью recording, у опытного пользователя превращается в главный инструмент массовой правки.

До какой степени это затягивает, хорошо показывает VimGolf, сайт с задачами на редактирование текста, где побеждает решение с наименьшим числом нажатий. Люди всерьез соревнуются, кто превратит один кусок текста в другой за 11 клавиш вместо 14. Брам Моленар, автор Vim, еще в 2000 году написал эссе «Seven habits of effective text editing», где предлагал похожий подход без всякого соревновательного азарта: замечать, какие правки повторяются чаще всего, и искать для них более короткий путь. Правда, у этой медали есть и обратная сторона. Порог входа в такую систему высокий, первые недели скорость работы падает, а мышечная память, однажды настроенная на эти комбинации, начинает мешать во всех остальных программах, где те же нажатия просто печатают буквы.

Секта и число зверя

У любого инструмента, который требует таких вложений в обучение, неизбежно появляются фанаты, а у фанатов появляются враги. Главным врагом с восьмидесятых годов был Emacs, и война редакторов между двумя лагерями давно обросла собственной мифологией. Ричард Столлман завел себе альтер эго святого Игнуция из Церкви Emacs, выходил на сцену в мантии и с жестким диском вместо нимба на голове и снисходительно разрешал пастве пользоваться свободными версиями vi, приравнивая это к епитимье. Заодно он обратил внимание, что vi-vi-vi, если прочитать название трижды римскими цифрами, дает 666. Лагерь vi в долгу не оставался и десятилетиями повторял, что Emacs представляет собой прекрасную операционную систему, которой не хватает только приличного текстового редактора, а также намекал на мизинцы, которые эмаксеры стирают об Ctrl.

Чем закончилась эта война, можно понять по одному факту: среди самых популярных способов пользоваться Emacs есть пакет Evil, который превращает Emacs в Vim, с нормальным режимом, текстовыми объектами и всем остальным. На нем построены целые сборки вроде Spacemacs. Враг, таким образом, сдался изнутри, сохранив флаг на здании.

С другими соперниками история похожая. VS Code, который в опросах Stack Overflow уже много лет занимает первое место среди сред разработки, обзавелся расширением с Vim-режимом, набравшим миллионы установок, у JetBrains есть IdeaVim, у Jupyter, Obsidian и даже у браузера (через расширения вроде Vimium) есть свои версии тех же клавиш. Командные оболочки bash и zsh умеют переключаться в режим vi, программы для просмотра документации листают текст клавишами j и k, а Gmail использует те же j и k для перехода между письмами. Сам Vim в такой ситуации можно вообще не запускать, его грамматика давно живет отдельно от программы, и многие разработчики пользуются ей годами, ни разу не открыв настоящий Vim.

Наиболее наглядно влияние этой маленькой армии проявилось в 2016 году, когда Apple выпустила MacBook Pro с сенсорной полосой Touch Bar вместо верхнего ряда клавиш. Вместе с функциональными клавишами пропал и физический Esc, который превратился в нарисованную кнопку без тактильного отклика. Для человека, который жмет Esc сотни раз в час, это примерно как убрать педаль тормоза и предложить тормозить голосом. Вимеры массово переназначали Esc на Caps Lock (многие, впрочем, сделали это еще задолго до Apple), писали гневные посты, а шутка про то, что Apple решила проблему выхода из Vim самым радикальным способом, напрашивалась сама собой. В 2019 году в 16-дюймовой модели физический Esc вернулся, а в 2021-м Apple отказалась от Touch Bar совсем. Связывать эти решения исключительно с пользователями Vim было бы натяжкой, однако громче всех по поводу пропавшей клавиши возмущались именно они.

На другом конце спектра находится nano, у которого внизу экрана постоянно висит шпаргалка с сочетаниями, и одна из первых подсказок в ней сообщает, что выход висит на Ctrl+X. В Debian и Ubuntu именно nano запускается по умолчанию, когда системе нужен редактор, и отчасти поэтому пользователи этих дистрибутивов попадают в историю с коммитом реже, чем пользователи Git for Windows. Nano прямо и без затей решает ровно ту задачу, на которой спотыкаются новички, и поэтому вимеры относятся к нему со смесью снисхождения и легкой зависти.

Табличка на двери

Напоследок стоит еще раз взглянуть на стартовый экран. Он появляется, когда Vim запускают без имени файла: по центру написано название редактора и номер версии, ниже имя Брама, строчка про открытый исходный код и просьба про Уганду, а под ней отдельной, аккуратно выровненной строкой стоит инструкция набрать :q и нажать Enter, чтобы выйти.

Инструкция по выходу, получается, с самого начала висела на двери. Загвоздка в том, что заставку Vim показывает только при запуске с пустым буфером, то есть тем, кто открыл редактор в терминале сам и по доброй воле. Тот, кого в Vim забросил Git, сразу попадает в открытый файл с сообщением коммита и подсказку не видит ни разу, так что миллион просмотров на Stack Overflow набрали ровно те люди, которым табличку на двери так и не показали.Теги:• Vim
• vi
• ed
• Emacs
• текстовый редактор
• модальный редактор
• Bram Moolenaar
• Neovim
• история IT
• UnixХабы:• Текстовые редакторы и IDE
• *nix
• История IT
• Программирование
• Софт

Получайте больше инсайтов о систематизации бизнеса

Подписывайтесь на Telegram-канал Business Operations — ежедневные материалы о бизнес-процессах, операционном управлении и повышении эффективности

💬 Подписаться на канал