Come elencare i buffer aperti semplificato
Vorrei usare il lscomando per mostrare i buffer aperti.
Vorrei rendere l'output di questo comando il più semplice possibile. Vorrei vedere solo i nomi, i numeri e lo stato dei buffer (#,%, +, ..).
Non ho bisogno di vedere la prima riga nel risultato che mostra il percorso completo del file corrente e inoltre non ho bisogno di vedere i percorsi dei buffer e il numero di riga corrente in ciascun buffer. Mostra il percorso solo se due buffer hanno lo stesso nome e provengono da directory diverse.
La mia idea alla base di questo è che mi piacerebbe raggiungere questo obiettivo utilizzando le funzionalità integrate di vim. C'è un modo per rendere l' lsoutput così semplice?
Risposte
Prova questo:
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
Installa un :Lscomando che dovrebbe visualizzare un elenco di buffer semplificato nella finestra della posizione. Premere Invio su una voce per passare al buffer.
Proprio come con il builtin :ls, aggiungi un bang ( :Ls!) per includere i buffer non in elenco.
Ciò richiede una versione recente di Vim che includa queste patch:
- 8.2.0869 non è possibile personalizzare il contenuto della finestra di quickfix
- 8.2.0933 'quickfixtextfunc' non ottiene l'ID della finestra dell'elenco delle posizioni
- 8.2.0959 l' uso di "quickfixtextfunc" è un po 'lento
Per guadagnare in efficienza, leggibilità e affidabilità, potresti riscrivere il codice nello 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
Ciò richiede una versione di Vim ancora più recente. Funziona su 8.2.2332.
Non devi scrivere il codice nel tuo vimrc, basta inserirlo ~/.vim/plugin/myls.vim. Provalo; conservalo finché lo trovi utile; rimuovilo una volta trovato qualcosa di meglio.
Per ulteriori informazioni, vedere:
- : h 'quickfixtextfunc'
- : h CTRL-W_
- : h: com
- : h: comando-bang
- : h: barra dei comandi
- : h: funzione
- : h: func-abort
- : h: def
- : h: lopen
- : h: wincmd
- : h aggiungi ()
- : h bufexists ()
- : h buflisted ()
- : h bufname ()
- : h bufnr ()
- : h: chiama
- : h call ()
- : h copia ()
- : h count ()
- : h vuoto ()
- : h filtro ()
- : h fnamemodify ()
- : h getbufvar ()
- : h getloclist ()
- : h setloclist ()
- : h input ()
- : h len ()
- : h map ()
- : h printf ()
- : gamma h ()
- : h str2nr ()
- : h win_execute ()
- : h win_findbuf ()
Nel plugin della mia libreria , ho una :Project lsfunzione che elenca solo i file che appartengono al progetto corrente come :lsli avrei elencati.
Il codice correlato (GPL con eccezione) è il seguente:
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
Invece di a:prj.buffers, avresti l'elenco dei buffer in formato range(1, bufnr('$')). Dovrebbe quindi essere filtrato per mantenere solo i buffer esistenti ( filter(range(1, bufnr('$')), 'bufexists(v:val)')).
Quindi puoi concludere la chiamata bufname()con fnamemodify()per visualizzare esattamente ciò di cui hai bisogno.
IOW, la s:ls_projectfunzione diventerà qualcosa di simile (non testata).
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 poi dovrai solo giocare con il s:As_ls()codice per far sì che produca ciò che desideri vedere. a:bidè il parametro formale che contiene l'id del buffer e per sapere cosa fa ciascuna funzione, vedere ad esempio :h buflisted()e così via.
C'è un modo per rendere l'
lsoutput così semplice?
Non in modo nativo ... Vim non supporta realmente le opzioni per personalizzare o controllare l'output del :lscomando.
Detto questo, puoi scrivere il tuo comando definito dall'utente che implementa ciò che desideri.
Puoi usarlo execute('ls')per catturare l'output della stringa del comando originale e poi post-elaborarlo (possibilmente usando espressioni regolari e la substitute()funzione), oppure puoi interrogare i singoli buffer uno per uno per assemblare tu stesso le informazioni su di essi. Puoi usarlo bufnr('$')per trovare il numero di buffer più alto (così sai dove smettere di cercarli).
Un comando utente deve iniziare con una maiuscola, quindi puoi solo fare :Lso :LS. Ma una cosa che puoi fare è usare a :cnoremapper acquisire l'uso di :lsnella riga di comando Ex e sostituirlo con l'implementazione alternativa. Ma fai attenzione che in :cnoremapgenere è troppo ampio, si applica ovunque nella riga di comando e influisce anche sulla ricerca con /, chiedendo all'utente con input(), ecc. Quindi di solito si desidera utilizzare una <expr>mappatura che esegue diversi controlli prima di eseguire una sostituzione effettiva. Una buona tecnica è quella di eseguire un'operazione :cnoremap <expr> <CR>che si innesca solo quando stai per inviare un comando, quindi puoi ispezionare l'intero comando e fare una sostituzione se necessario.
Quindi, sì, è fattibile ... Ma se vuoi la mia opinione, è troppo lavoro. YMMV.