Vim имеет множество команд для закрытия чего угодно и в каких угодно сочетаниях, но чего он не умеет так это закрывать все одной командой. Человеку который начал использовать Vim недавно, это может показаться довольно странным. Попробуем разобраться, как исправ��ть эту ситуацию.


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

  • :bd[elete] — удалить буфер. На самом деле просто делает буфер невидимым, но
    оставляет его в памяти.
  • :bw[ipeout] — полностью стирает(wipeout) буфер из памяти
  • :bun[load] — удаляет буфер из памяти, но оставляет его в списке буферов.
    Бесполезная команда, кроме тех случаев когда у Вас либо
    гигантских размеров файл либо чрезвычайно мало оперативной памяти.
  • :close — закрыть текущий сплит. Буфер не удаляется
  • :q[uit] — чисто теоретически команда должна закрывать Vim, на практике все немного сложнее


Я настоятельно рекомендую установить в .vimrc опцию set confirm, это позволит
избежать надоедливых сообщений об ошибке при попытке закрыть(удалить) буфер с
не сохраненными изменениями. Вместо ошибки будет появляться подтверждение
закрытия не сохраненного файла.

Для закрытия файла в традиционном смысле(для любого другого редактора)
подходит команда :bw, но к сожалению все команды по работе с буферами
обладают отвратительным свойством закрывать то окно в котором, в данный момент
они отображаются(естественно, если это не единственное окно на экране).
Данная проблема проблема решается установкой плагина bufkill.vim и использованием команды
:BW(:BD, :BUN) вместо :bw(:bd, :bun).

Команда :q должна закрывать Vim, но ее поведение зависит от ситуации и далеко не
интуитивно. Для закрытия окна используется команда :close, но закрыть с её
помощью последнее окно Вы не сможете. Также нельзя закрыть Vim с помощью
команд удаления буфера(даже если буфер последний).
По итогу пустой буфер невозможно удалить, последнее окно
нельзя закрыть и работа с несколькими файлами превращается в игру «угадай какую
команду нужно использовать сейчас — :bw, :BW, :q или :close». После
прочесывания форумов, мне пришлось изобретать велосипед, потому
как нужный мне я так и не нашел. Что мне было нужно так это привычный(по другим
редакторам) и интуитивный способ быстро закрывать файлы, окна и сам Vim.

Первая проблема — определиться с алгоритмом «привычного» способа производить все
вышеописанное. Путем проб и ошибок алгоритм стал вот таким:



На первый взгляд немного сложно, но когда начинаешь использовать — все встает на
свои места. Конечно это не панацея — окна(не буферы) по прежнему необходимо закрывать
с помощью :close.

Вот так выглядит реализация:

function! CountListedBuffers() 
  let cnt = 0 
  for nr in range(1,bufnr("$")) 
	if buflisted(nr) 
	  let cnt += 1 
	endif 
  endfor 
  return cnt 
endfunction 

function! SmartExit()
	let s:BufferToKill = bufnr('%')
	let s:EmptyBuffer = 0

	if bufname('%') == '' && ! &modified && &modifiable
		if &buftype == 'nofile' && &swapfile == 0
			" Is scratch buffer, not empty
		else
			let s:EmptyBuffer = 1
		endif
	endif

	" Get a list of all windows which have this buffer loaded
	let s:WindowListWithBufferLoaded = []
	let i = 1
	let buf = winbufnr(i)
	while buf != -1
		if buf == s:BufferToKill
			let s:WindowListWithBufferLoaded += [i]
		endif
		let i = i + 1
		let buf = winbufnr(i)
	endwhile

	" Check that the buffer is last
	if(CountListedBuffers() < 2)
		let s:LastBuffer = 1
	else
		let s:LastBuffer = 0
	endif

	if s:LastBuffer
		if len(s:WindowListWithBufferLoaded) > 1
			execute "close"
		else
			if ! s:EmptyBuffer
				execute "bw | bw"
			else
				execute "q"
			endif
		endif
	else
		let g:BufKillActionWhenBufferDisplayedInAnotherWindow="kill"
		execute "BW"
		let g:BufKillActionWhenBufferDisplayedInAnotherWindow="confirm"
	endif
endfunction


Для работы необходим установленный bufkill.vim.

В итоге мой меппинг для «закрытия всего и вся» выглядит так:

" «Умное» закрытие
nmap qq :call SmartExit()" Закрыть окно, но не удалять буфер
nmap qw <C-W>c

qq используется мною гораздо чаще, она делает именно то, что в моём понимании
должна была бы делать :q, но почему то не делает.

Закрытие

Итог


Vim'ом я пользуюсь с недавних пор, но у меня сложилось впечатление
что писать подобные костыли и велосипеды мне придется еще не раз! Но что уж
греха таить — это чрезвычайно увлекательно. Надеюсь данный мини-плагин или
информация из этой статьи кому-то пригодится.

P.S. Не холивара ради, но как с этим обстоят дела у Emacs?

P.P.S. Вкладками(табами) я не пользуюсь, мне вполне хватает сплитов в пределах одного экрана,
а проблем при работе с буферами они не решают, только добавляют.