Vim :bw, :bd, :bun, :quit, :close. Что со всем этим делать?

в 15:26, , рубрики: vim, vim tips, метки: ,

Vim :bw, :bd, :bun, :quit, :close. Что со всем этим делать?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.

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

Vim :bw, :bd, :bun, :quit, :close. Что со всем этим делать?

На первый взгляд немного сложно, но когда начинаешь использвать — все встает на
свои места. Конечно это не панацея — окна(не буфферы) попрежнему необходимо закрывать
с помощью :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 SmartClose()" Закрыть окно, но не удалять буффер
nmap qw <C-W>c

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

Закрытие

Итог

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

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

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

Автор: AzzNomad

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js