Como listar buffers abertos simplificado
Eu gostaria de usar o lscomando para mostrar buffers abertos.
Eu gostaria de tornar a saída desse comando o mais simples possível. Gostaria de ver apenas os nomes, números e status dos buffers (#,%, +, ..).
Não preciso ver a primeira linha no resultado que mostra o caminho completo do arquivo atual e também não preciso ver os caminhos dos buffers e o número da linha atual em cada buffer. Mostra o caminho apenas se dois buffers tiverem o mesmo nome e vierem de diretórios diferentes.
Minha ideia por trás disso é que eu gostaria de conseguir isso usando os recursos integrados do vim. Existe uma maneira de tornar a lssaída tão simples?
Respostas
Experimente isto:
command -bar -bang Ls call s:ls(<bang>0)
function s:ls(bang) abort
let bufnrs = range(1, bufnr('$')) call filter(bufnrs, a:bang ? {_, v -> bufexists(v)} : {_, v -> buflisted(v)}) let bufnames = copy(bufnrs) \ ->map({_, v -> bufname(v)->fnamemodify(':t')}) let uniq_flags = copy(bufnames)->map({_, v -> count(bufnames, v) == 1}) let items = map(bufnrs, {i, v -> #{ \ bufnr: v, \ text: s:gettext(v, uniq_flags[i]), \ }}) call setloclist(0, [], ' ', #{ \ items: items, \ title: 'ls' .. (a:bang ? '!' : ''), \ quickfixtextfunc: 's:quickfixtextfunc', \ }) lopen nmap <buffer><nowait><expr><silent> <cr> <sid>Cr() endfunction function s:Cr() if w:quickfix_title =~# '^ls!\=$'
let locid = win_getid()
return "\<c-w>\<cr>\<plug>(close-location-window)" .. locid .. "\<cr>\<plug>(verticalize)"
else
return "\<c-w>\<cr>\<plug>(verticalize)"
endif
endfunction
nnoremap <plug>(close-location-window) :<c-u>call <sid>CloseLocationWindow()<cr>
nnoremap <plug>(verticalize) :<c-u>wincmd L<cr>
function s:CloseLocationWindow()
let locid = input('')->str2nr()
call win_execute(locid, 'close')
endfunction
function s:gettext(v, is_uniq) abort
let format = ' %*d%s%s%s%s%s %s'
let bufnr = [bufnr('$')->len(), a:v]
let buflisted = !buflisted(a:v) ? 'u': ' '
let cur_or_alt = a:v == bufnr('%') ? '%' : a:v == bufnr('#') ? '#' : ' '
let active_or_hidden = win_findbuf(a:v)->empty() ? 'h' : 'a'
let modifiable = getbufvar(a:v, '&ma', 0) ? ' ' : '-'
let modified = getbufvar(a:v, '&mod', 0) ? '+' : ' '
let bufname = bufname(a:v)->empty()
\ ? '[No Name]'
\ : bufname(a:v)->fnamemodify(a:is_uniq ? ':t' : ':p')
return call('printf', [format]
\ + bufnr
\ + [buflisted, cur_or_alt, active_or_hidden, modifiable, modified, bufname])
endfunction
function s:quickfixtextfunc(info) abort
let items = getloclist(a:info.winid, #{id : a:info.id, items : 1}).items
let l = []
for idx in range(a:info.start_idx - 1, a:info.end_idx - 1)
call add(l, items[idx].text)
endfor
return l
endfunction
Ele instala um :Lscomando que deve exibir uma lista de buffer simplificada na janela de localização. Pressione Enter em uma entrada para pular para o buffer.
Assim como com o embutido :ls, acrescente um bang ( :Ls!) para incluir os buffers não listados.
Isso requer uma versão recente do Vim que inclui estes patches:
- 8.2.0869 não é possível personalizar o conteúdo da janela de correção rápida
- 8.2.0933 'quickfixtextfunc' não obtém o ID da janela da lista de locais
- 8.2.0959 usando 'quickfixtextfunc' é um pouco lento
Para ganhar eficiência, legibilidade e confiabilidade, você pode reescrever o código no script Vim9:
vim9
command -bar -bang Ls Ls(<bang>0)
def Ls(bang: any)
var bufnrs = range(1, bufnr('$'))
filter(bufnrs, bang ? (_, v) => bufexists(v) : (_, v) => buflisted(v))
var bufnames = copy(bufnrs)
->mapnew((_, v) => bufname(v)->fnamemodify(':t'))
var uniq_flags = copy(bufnames)
->mapnew((_, v) => count(bufnames, v) == 1)
var items = mapnew(bufnrs, (i, v) => ({
bufnr: v,
text: Gettext(v, uniq_flags[i]),
}))
setloclist(0, [], ' ', {
items: items,
title: 'ls' .. (bang ? '!' : ''),
quickfixtextfunc: 'Quickfixtextfunc',
})
lopen
nmap <buffer><nowait><expr><silent> <cr> <sid>Cr()
enddef
def Cr(): string
if w:quickfix_title =~ '^ls!\=$' var locid = win_getid() return "\<c-w>\<cr>\<plug>(close-location-window)" .. locid .. "\<cr>\<plug>(verticalize)" else return "\<c-w>\<cr>\<plug>(verticalize)" endif enddef nnoremap <plug>(close-location-window) :<c-u>call <sid>CloseLocationWindow()<cr> nnoremap <plug>(verticalize) :<c-u>wincmd L<cr> def CloseLocationWindow() var locid = input('')->str2nr() win_execute(locid, 'close') enddef def Gettext(v: number, is_uniq: bool): string var format = ' %*d%s%s%s%s%s %s' var bufnr = [bufnr('$')->len(), v]
var buflisted = !buflisted(v) ? 'u' : ' '
var cur_or_alt = v == bufnr('%') ? '%' : v == bufnr('#') ? '#' : ' '
var active_or_hidden = win_findbuf(v)->empty() ? 'h' : 'a'
var modifiable = getbufvar(v, '&ma', 0) ? ' ' : '-'
var modified = getbufvar(v, '&mod', 0) ? '+' : ' '
var bufname = bufname(v)->empty()
? '[No Name]'
: bufname(v)->fnamemodify(is_uniq ? ':t' : ':p')
return call('printf', [format]
+ bufnr
+ [buflisted, cur_or_alt, active_or_hidden,
modifiable, modified, bufname])
enddef
def Quickfixtextfunc(info: dict<number>): list<any>
var items = getloclist(info.winid, {id: info.id, items: 1}).items
var l = []
for idx in range(info.start_idx - 1, info.end_idx - 1)
add(l, items[idx].text)
endfor
return l
enddef
Isso requer uma versão ainda mais recente do Vim. Ele funciona em 8.2.2332.
Você não precisa escrever o código em seu vimrc, apenas coloque-o ~/.vim/plugin/myls.vim. Teste-o; guarde-o pelo tempo que você achar útil; remova-o assim que encontrar algo melhor.
Para obter mais informações, consulte:
- : h 'quickfixtextfunc'
- : h CTRL-W_
- : h: com
- : h: command-bang
- : h: barra de comando
- : h: função
- : h: func-abort
- : h: def
- : h: lopen
- : h: wincmd
- : h add ()
- : h bufexists ()
- : h buflisted ()
- : h bufname ()
- : h bufnr ()
- : h: ligar
- : h call ()
- : cópia h ()
- : contagem de h ()
- : h vazio ()
- : filtro h ()
- : h fnamemodify ()
- : h getbufvar ()
- : h getloclist ()
- : h setloclist ()
- : entrada h ()
- : h len ()
- : h map ()
- : h printf ()
- : intervalo h ()
- : h str2nr ()
- : h win_execute ()
- : h win_findbuf ()
Em meu plug-in de biblioteca , tenho um :Project lsrecurso que lista apenas os arquivos que pertencem ao projeto atual, como os :lsteria listado.
O código relacionado (GPL com exceção) é o seguinte:
function! s:As_ls(bid) abort " {{{2
let name = bufname(a:bid)
if empty(name)
let name = 'Used to be known as: '.lh#project#__buffer(a:bid)
endif
return printf('%3d%s %s'
\ , a:bid
\ , (buflisted(a:bid) ? ' ' : 'u')
\ . (bufnr('%') == a:bid ? '%' : bufnr('#') == a:bid ? '#' : ' ')
\ . (! bufloaded(a:bid) ? ' ' : bufwinnr(a:bid)<0 ? 'h' : 'a')
\ . (! getbufvar(a:bid, "&modifiable") ? '-' : getbufvar(a:bid, "&readonly") ? '=' : ' ')
\ . (getbufvar(a:bid, "&modified") ? '+' : ' ')
\ , '"'.name.'"')
endfunction
function! s:ls_project(prj) abort " {{{2
if lh#option#is_unset(a:prj)
echo '(no project specified!)'
endif
let lines = map(copy(a:prj.buffers), 's:As_ls(v:val)')
echo "Buffer list of ".get(a:prj, 'name', '(unnamed)')." project:"
echo join(lines, "\n")
endfunction
Em vez de a:prj.buffers, você teria a lista de buffers em range(1, bufnr('$')). Ele então precisaria ser filtrado para manter apenas os buffers existentes ( filter(range(1, bufnr('$')), 'bufexists(v:val)')).
Em seguida, você pode encerrar a chamada para bufname()com fnamemodify()para exibir exatamente o que você precisa.
IOW, a s:ls_projectfunção deve se tornar algo assim (não testado).
function! s:ls() abort " {{{2
let buffers = filter(range(1, bufnr('$')), 'bufexists(v:val)')
let lines = map(copy(buffers), 's:As_ls(v:val)')
echo "Buffer list:"
echo join(lines, "\n")
endfunction
command! -nargs=0 LS call s:ls()
" or with a mapping
nnoremap <leader>r :<c-u>call <sid>ls()<cr>
E então você só terá que brincar com o s:As_ls()código para fazê-lo produzir o que você deseja ver. a:bidé o parâmetro formal que contém o id do buffer, e para saber o que cada função faz, veja por exemplo :h buflisted(), e assim por diante.
Existe uma maneira de tornar a
lssaída tão simples?
Não nativamente ... O Vim realmente não oferece suporte a opções para personalizar ou controlar a saída do :lscomando.
Dito isso, você pode escrever seu próprio comando definido pelo usuário que implementa o que você deseja.
Você pode usar execute('ls')para capturar a saída da string do comando original e, em seguida, pós-processá-la (possivelmente usando expressões regulares e a substitute()função) ou pode consultar os buffers individuais um por um para reunir as informações sobre eles você mesmo. Você pode usar bufnr('$')para encontrar o maior número de buffer (para saber onde parar de procurá-los).
Um comando do usuário precisa começar com maiúsculas, portanto, você só pode fazer :Lsou :LS. Mas uma coisa que você pode fazer é usar um :cnoremappara capturar o uso de :lsna linha de comando Ex e substituí-lo por sua implementação alternativa. Mas tome cuidado, pois :cnoremapnormalmente é muito amplo, ele se aplica a qualquer lugar na linha de comando e também afeta a pesquisa com /, avisando o usuário com input()etc. Portanto, normalmente você deseja usar um <expr>mapeamento que executa várias verificações antes de fazer uma substituição real. Uma boa técnica é fazer um :cnoremap <expr> <CR>que só acione quando você estiver prestes a enviar um comando, então você pode inspecionar todo o comando e fazer uma substituição, se necessário.
Então, sim, é factível ... Mas se você quer minha opinião, é muito trabalhoso. YMMV.