Come elencare i buffer aperti semplificato

Sep 07 2020

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

6 user938271 Sep 07 2020 at 21:36

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 ()
4 LucHermitte Sep 07 2020 at 18:12

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.

1 filbranden Sep 07 2020 at 13:54

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.