Cara membuat daftar buffer terbuka yang disederhanakan

Sep 07 2020

Saya ingin menggunakan lsperintah untuk menampilkan buffer yang dibuka.

Saya ingin membuat keluaran dari perintah ini sesederhana mungkin. Saya hanya ingin melihat nama, angka, dan status buffer (#,%, +, ..).

Saya tidak perlu melihat baris pertama dalam hasil yang menunjukkan jalur lengkap dari file saat ini dan juga tidak perlu melihat jalur buffer dan nomor baris saat ini di setiap buffer. Hanya tampilkan jalur jika dua buffer memiliki nama yang sama dan berasal dari direktori yang berbeda.

Ide saya di balik ini adalah saya ingin mencapai ini dengan menggunakan fitur bawaan vim. Apakah ada cara untuk membuat lskeluaran sesederhana itu?

Jawaban

6 user938271 Sep 07 2020 at 21:36

Coba ini:

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

Ini menginstal :Lsperintah yang seharusnya menampilkan daftar buffer yang disederhanakan di jendela lokasi. Tekan Enter pada entri untuk melompat ke buffer.

Sama seperti dengan builtin :ls, tambahkan bang ( :Ls!) untuk menyertakan buffer tidak terdaftar.

Ini membutuhkan versi Vim terbaru yang menyertakan tambalan berikut:

  • 8.2.0869 tidak mungkin menyesuaikan konten jendela quickfix
  • 8.2.0933 'quickfixtextfunc' tidak mendapatkan ID jendela dari daftar lokasi
  • 8.2.0959 menggunakan 'quickfixtextfunc' agak lambat

Untuk mendapatkan efisiensi, keterbacaan, dan keandalan, Anda dapat menulis ulang kode dalam skrip 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

Ini membutuhkan versi Vim yang lebih baru. Ia bekerja pada 8.2.2332.

Anda tidak perlu menulis kode di vimrc Anda, cukup masukkan ~/.vim/plugin/myls.vim. Menguji; simpan selama Anda merasa berguna; keluarkan setelah Anda menemukan sesuatu yang lebih baik.


Untuk info lebih lanjut, lihat:

  • : h 'quickfixtextfunc'
  • : h CTRL-W_
  • : h: com
  • : h: command-bang
  • : h: bilah perintah
  • : h: function
  • : h: func-abort
  • : h: def
  • : h: lopen
  • : h: wincmd
  • : h tambahkan ()
  • : h bufexists ()
  • : h buflisted ()
  • : h bufname ()
  • : h bufnr ()
  • : h: panggilan
  • : h panggilan ()
  • : h copy ()
  • : h hitung ()
  • : h kosong ()
  • : h filter ()
  • : h fnamemodify ()
  • : h getbufvar ()
  • : h getloclist ()
  • : h setloclist ()
  • : h masukan ()
  • : h len ()
  • : h peta ()
  • : h printf ()
  • : rentang h ()
  • : h str2nr ()
  • : h win_execute ()
  • : h win_findbuf ()
4 LucHermitte Sep 07 2020 at 18:12

Di plugin perpustakaan saya , saya memiliki :Project lsfitur yang hanya mencantumkan file yang dimiliki proyek saat ini seperti yang :lsakan mencantumkannya.

Kode terkait (GPL w / pengecualian) adalah sebagai berikut:

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

Alih-alih a:prj.buffers, Anda akan memiliki daftar buffer di range(1, bufnr('$')). Ini kemudian perlu difilter untuk mempertahankan hanya buffer yang ada ( filter(range(1, bufnr('$')), 'bufexists(v:val)')).

Kemudian Anda dapat menutup panggilan ke bufname()dengan fnamemodify()untuk menampilkan apa yang Anda butuhkan.

IOW, s:ls_projectfungsinya akan menjadi seperti ini (belum teruji).

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>

Dan kemudian Anda hanya perlu bermain dengan s:As_ls()kode untuk membuatnya menghasilkan apa yang ingin Anda lihat. a:bidadalah parameter formal yang menyimpan id buffer, dan untuk mengetahui apa yang dilakukan setiap fungsi, lihat misalnya :h buflisted(), dan seterusnya.

1 filbranden Sep 07 2020 at 13:54

Apakah ada cara untuk membuat lskeluaran sesederhana itu?

Tidak secara asli ... Vim tidak benar-benar mendukung opsi untuk menyesuaikan atau mengontrol keluaran :lsperintah.

Karena itu, Anda dapat menulis perintah yang ditentukan pengguna Anda sendiri yang mengimplementasikan apa yang Anda inginkan.

Anda dapat menggunakan execute('ls')untuk menangkap output string dari perintah asli dan kemudian memprosesnya (kemungkinan menggunakan ekspresi reguler dan substitute()fungsi), atau Anda dapat meminta buffer individual satu per satu untuk mengumpulkan informasi tentangnya sendiri. Anda dapat menggunakan bufnr('$')untuk menemukan nomor buffer tertinggi (sehingga Anda tahu di mana harus berhenti mencari mereka.)

Perintah pengguna harus dimulai dengan huruf besar, jadi Anda hanya dapat melakukan :Lsatau :LS. Tapi satu hal yang dapat Anda lakukan adalah menggunakan a :cnoremapuntuk menangkap penggunaan :lsdi baris perintah Ex dan menggantinya dengan implementasi alternatif Anda. Namun berhati-hatilah karena :cnoremapbiasanya terlalu luas, ini berlaku di mana saja di baris perintah dan juga memengaruhi penelusuran dengan /, mendorong pengguna dengan input(), dll. Jadi biasanya Anda ingin menggunakan <expr>pemetaan yang melakukan beberapa pemeriksaan sebelum melakukan penggantian yang sebenarnya. Teknik yang bagus adalah melakukan :cnoremap <expr> <CR>yang hanya memicu saat Anda akan mengirimkan perintah, lalu Anda dapat memeriksa seluruh perintah dan melakukan penggantian jika diperlukan.

Jadi, ya, itu bisa dilakukan ... Tapi jika Anda menginginkan pendapat saya, itu terlalu banyak pekerjaan. YMMV.