401 lines
12 KiB
VimL
401 lines
12 KiB
VimL
" buffer.vim
|
|
" @Author: Tom Link (micathom AT gmail com?subject=[vim])
|
|
" @Website: http://www.vim.org/account/profile.php?user_id=4037
|
|
" @License: GPL (see http://www.gnu.org/licenses/gpl.txt)
|
|
" @Created: 2007-06-30.
|
|
" @Last Change: 2015-11-06.
|
|
" @Revision: 7.1.352
|
|
|
|
|
|
" Where to display the line when using |tlib#buffer#ViewLine|.
|
|
" For possible values for position see |scroll-cursor|.
|
|
TLet g:tlib_viewline_position = 'zz'
|
|
|
|
|
|
let s:bmru = []
|
|
|
|
|
|
function! tlib#buffer#EnableMRU() "{{{3
|
|
call tlib#autocmdgroup#Init()
|
|
autocmd TLib BufEnter * call s:BMRU_Push(bufnr('%'))
|
|
endf
|
|
|
|
|
|
function! tlib#buffer#DisableMRU() "{{{3
|
|
call tlib#autocmdgroup#Init()
|
|
autocmd! TLib BufEnter
|
|
endf
|
|
|
|
|
|
function! s:BMRU_Push(bnr) "{{{3
|
|
let i = index(s:bmru, a:bnr)
|
|
if i >= 0
|
|
call remove(s:bmru, i)
|
|
endif
|
|
call insert(s:bmru, a:bnr)
|
|
endf
|
|
|
|
|
|
function! s:CompareBuffernameByBasename(a, b) "{{{3
|
|
let rx = '"\zs.\{-}\ze" \+\S\+ \+\d\+$'
|
|
let an = matchstr(a:a, rx)
|
|
let an = fnamemodify(an, ':t')
|
|
let bn = matchstr(a:b, rx)
|
|
let bn = fnamemodify(bn, ':t')
|
|
let rv = an == bn ? 0 : an > bn ? 1 : -1
|
|
return rv
|
|
endf
|
|
|
|
|
|
function! s:CompareBufferNrByMRU(a, b) "{{{3
|
|
let an = matchstr(a:a, '\s*\zs\d\+\ze')
|
|
let bn = matchstr(a:b, '\s*\zs\d\+\ze')
|
|
let ai = index(s:bmru, 0 + an)
|
|
if ai == -1
|
|
return 1
|
|
else
|
|
let bi = index(s:bmru, 0 + bn)
|
|
if bi == -1
|
|
return -1
|
|
else
|
|
return ai == bi ? 0 : ai > bi ? 1 : -1
|
|
endif
|
|
endif
|
|
endf
|
|
|
|
|
|
" Set the buffer to buffer and return a command as string that can be
|
|
" evaluated by |:execute| in order to restore the original view.
|
|
function! tlib#buffer#Set(buffer) "{{{3
|
|
let lazyredraw = &lazyredraw
|
|
set lazyredraw
|
|
try
|
|
let cb = bufnr('%')
|
|
let sn = bufnr(a:buffer)
|
|
if sn != cb
|
|
let ws = bufwinnr(sn)
|
|
if ws != -1
|
|
let wb = bufwinnr('%')
|
|
exec ws.'wincmd w'
|
|
return wb.'wincmd w'
|
|
else
|
|
silent exec 'sbuffer! '. sn
|
|
return 'wincmd c'
|
|
endif
|
|
else
|
|
return ''
|
|
endif
|
|
finally
|
|
let &lazyredraw = lazyredraw
|
|
endtry
|
|
endf
|
|
|
|
|
|
" :def: function! tlib#buffer#Eval(buffer, code)
|
|
" Evaluate CODE in BUFFER.
|
|
"
|
|
" EXAMPLES: >
|
|
" call tlib#buffer#Eval('foo.txt', 'echo b:bar')
|
|
function! tlib#buffer#Eval(buffer, code) "{{{3
|
|
" let cb = bufnr('%')
|
|
" let wb = bufwinnr('%')
|
|
" " TLogVAR cb
|
|
" let sn = bufnr(a:buffer)
|
|
" let sb = sn != cb
|
|
let lazyredraw = &lazyredraw
|
|
set lazyredraw
|
|
let restore = tlib#buffer#Set(a:buffer)
|
|
try
|
|
exec a:code
|
|
" if sb
|
|
" let ws = bufwinnr(sn)
|
|
" if ws != -1
|
|
" try
|
|
" exec ws.'wincmd w'
|
|
" exec a:code
|
|
" finally
|
|
" exec wb.'wincmd w'
|
|
" endtry
|
|
" else
|
|
" try
|
|
" silent exec 'sbuffer! '. sn
|
|
" exec a:code
|
|
" finally
|
|
" wincmd c
|
|
" endtry
|
|
" endif
|
|
" else
|
|
" exec a:code
|
|
" endif
|
|
finally
|
|
exec restore
|
|
let &lazyredraw = lazyredraw
|
|
endtry
|
|
endf
|
|
|
|
|
|
" :def: function! tlib#buffer#GetList(?show_hidden=0, ?show_number=0, " ?order='bufnr')
|
|
" Possible values for the "order" argument:
|
|
" bufnr :: Default behaviour
|
|
" mru :: Sort buffers according to most recent use
|
|
" basename :: Sort by the file's basename (last component)
|
|
"
|
|
" NOTE: MRU order works on second invocation only. If you want to always
|
|
" use MRU order, call tlib#buffer#EnableMRU() in your ~/.vimrc file.
|
|
function! tlib#buffer#GetList(...)
|
|
TVarArg ['show_hidden', 0], ['show_number', 0], ['order', '']
|
|
" TLogVAR show_hidden, show_number, order
|
|
let ls_bang = show_hidden ? '!' : ''
|
|
redir => bfs
|
|
exec 'silent ls'. ls_bang
|
|
redir END
|
|
let buffer_list = split(bfs, '\n')
|
|
if order == 'mru'
|
|
if empty(s:bmru)
|
|
call tlib#buffer#EnableMRU()
|
|
echom 'tlib: Installed Buffer MRU logger; disable with: call tlib#buffer#DisableMRU()'
|
|
else
|
|
call sort(buffer_list, function('s:CompareBufferNrByMRU'))
|
|
endif
|
|
elseif order == 'basename'
|
|
call sort(buffer_list, function('s:CompareBuffernameByBasename'))
|
|
endif
|
|
let buffer_nr = map(copy(buffer_list), 'str2nr(matchstr(v:val, ''\s*\zs\d\+\ze''))')
|
|
" TLogVAR buffer_list, buffer_nr
|
|
if show_number
|
|
call map(buffer_list, 'matchstr(v:val, ''^\s*\d\+.\{-}\ze\s\+\S\+ \d\+\s*$'')')
|
|
else
|
|
call map(buffer_list, 'matchstr(v:val, ''^\s*\d\+\zs.\{-}\ze\s\+\S\+ \d\+\s*$'')')
|
|
endif
|
|
" TLogVAR buffer_list
|
|
" call map(buffer_list, 'matchstr(v:val, ''^.\{-}\ze\s\+line \d\+\s*$'')')
|
|
" TLogVAR buffer_list
|
|
call map(buffer_list, 'matchstr(v:val, ''^[^"]\+''). printf("%-20s %s", fnamemodify(matchstr(v:val, ''"\zs.\{-}\ze"$''), ":t"), fnamemodify(matchstr(v:val, ''"\zs.\{-}\ze"$''), ":h"))')
|
|
" TLogVAR buffer_list
|
|
return [buffer_nr, buffer_list]
|
|
endf
|
|
|
|
|
|
" :def: function! tlib#buffer#ViewLine(line, ?position='z')
|
|
" line is either a number or a string that begins with a number.
|
|
" For possible values for position see |scroll-cursor|.
|
|
" See also |g:tlib_viewline_position|.
|
|
function! tlib#buffer#ViewLine(line, ...) "{{{3
|
|
if a:line
|
|
TVarArg 'pos'
|
|
let ln = matchstr(a:line, '^\d\+')
|
|
let lt = matchstr(a:line, '^\d\+: \zs.*')
|
|
" TLogVAR pos, ln, lt
|
|
exec ln
|
|
if empty(pos)
|
|
let pos = tlib#var#Get('tlib_viewline_position', 'wbg')
|
|
endif
|
|
" TLogVAR pos
|
|
if !empty(pos)
|
|
exec 'norm! '. pos
|
|
endif
|
|
call tlib#buffer#HighlightLine(ln)
|
|
" let @/ = '\%'. ln .'l.*'
|
|
endif
|
|
endf
|
|
|
|
|
|
function! s:UndoHighlightLine() "{{{3
|
|
2match none
|
|
autocmd! TLib CursorMoved,CursorMovedI <buffer>
|
|
autocmd! TLib CursorHold,CursorHoldI <buffer>
|
|
autocmd! TLib InsertEnter,InsertChange,InsertLeave <buffer>
|
|
autocmd! TLib BufLeave,BufWinLeave,WinLeave,BufHidden <buffer>
|
|
endf
|
|
|
|
|
|
function! tlib#buffer#HighlightLine(...) "{{{3
|
|
TVarArg ['line', line('.')]
|
|
" exec '2match MatchParen /^\%'. a:line .'l.*/'
|
|
exec '2match Search /^\%'. line .'l.*/'
|
|
call tlib#autocmdgroup#Init()
|
|
exec 'autocmd TLib CursorMoved,CursorMovedI <buffer> if line(".") != '. line .' | call s:UndoHighlightLine() | endif'
|
|
autocmd TLib CursorHold,CursorHoldI <buffer> call s:UndoHighlightLine()
|
|
autocmd TLib InsertEnter <buffer> call s:UndoHighlightLine()
|
|
" autocmd TLib BufLeave,BufWinLeave,WinLeave,BufHidden <buffer> call s:UndoHighlightLine()
|
|
endf
|
|
|
|
|
|
" Delete the lines in the current buffer. Wrapper for |:delete|.
|
|
function! tlib#buffer#DeleteRange(line1, line2) "{{{3
|
|
let r = @t
|
|
try
|
|
exec a:line1.','.a:line2.'delete t'
|
|
finally
|
|
let @t = r
|
|
endtry
|
|
endf
|
|
|
|
|
|
" Replace a range of lines.
|
|
function! tlib#buffer#ReplaceRange(line1, line2, lines)
|
|
call tlib#buffer#DeleteRange(a:line1, a:line2)
|
|
call append(a:line1 - 1, a:lines)
|
|
endf
|
|
|
|
|
|
" Initialize some scratch area at the bottom of the current buffer.
|
|
function! tlib#buffer#ScratchStart() "{{{3
|
|
norm! Go
|
|
let b:tlib_inbuffer_scratch = line('$')
|
|
return b:tlib_inbuffer_scratch
|
|
endf
|
|
|
|
|
|
" Remove the in-buffer scratch area.
|
|
function! tlib#buffer#ScratchEnd() "{{{3
|
|
if !exists('b:tlib_inbuffer_scratch')
|
|
echoerr 'tlib: In-buffer scratch not initalized'
|
|
endif
|
|
call tlib#buffer#DeleteRange(b:tlib_inbuffer_scratch, line('$'))
|
|
unlet b:tlib_inbuffer_scratch
|
|
endf
|
|
|
|
|
|
" Run exec on all buffers via bufdo and return to the original buffer.
|
|
function! tlib#buffer#BufDo(exec) "{{{3
|
|
let bn = bufnr('%')
|
|
exec 'bufdo '. a:exec
|
|
exec 'buffer! '. bn
|
|
endf
|
|
|
|
|
|
" :def: function! tlib#buffer#InsertText(text, keyargs)
|
|
" Keyargs:
|
|
" 'shift': 0|N
|
|
" 'col': col('.')|N
|
|
" 'lineno': line('.')|N
|
|
" 'indent': 0|1
|
|
" 'pos': 'e'|'s' ... Where to locate the cursor (somewhat like s and e in {offset})
|
|
" Insert text (a string) in the buffer.
|
|
function! tlib#buffer#InsertText(text, ...) "{{{3
|
|
TVarArg ['keyargs', {}]
|
|
" TLogVAR a:text, keyargs
|
|
let keyargs = extend({
|
|
\ 'shift': 0, 'col': col('.'), 'lineno': line('.'), 'pos': 'e', 'indent': 0
|
|
\ }, keyargs)
|
|
" TLogVAR keyargs
|
|
let grow = 0
|
|
let post_del_last_line = line('$') == 1
|
|
let line = getline(keyargs.lineno)
|
|
if keyargs.col + keyargs.shift > 0
|
|
let pre = line[0 : (keyargs.col - 1 + keyargs.shift)]
|
|
let post = line[(keyargs.col + keyargs.shift): -1]
|
|
else
|
|
let pre = ''
|
|
let post = line
|
|
endif
|
|
" TLogVAR keyargs.lineno, line, pre, post
|
|
let text0 = pre . a:text . post
|
|
let text = split(text0, '\n', 1)
|
|
" TLogVAR text
|
|
let icol = len(pre)
|
|
" exec 'norm! '. keyargs.lineno .'G'
|
|
call cursor(keyargs.lineno, keyargs.col)
|
|
if keyargs.indent && keyargs.col > 1
|
|
if &fo =~# '[or]'
|
|
" FIXME: Is the simple version sufficient?
|
|
" VERSION 1
|
|
" " This doesn't work because it's not guaranteed that the
|
|
" " cursor is set.
|
|
" let cline = getline('.')
|
|
" norm! a
|
|
" "norm! o
|
|
" " TAssertExec redraw | sleep 3
|
|
" let idt = strpart(getline('.'), 0, keyargs.col('.') + keyargs.shift)
|
|
" " TLogVAR idt
|
|
" let idtl = len(idt)
|
|
" -1,.delete
|
|
" " TAssertExec redraw | sleep 3
|
|
" call append(keyargs.lineno - 1, cline)
|
|
" call cursor(keyargs.lineno, keyargs.col)
|
|
" " TAssertExec redraw | sleep 3
|
|
" if idtl == 0 && icol != 0
|
|
" let idt = matchstr(pre, '^\s\+')
|
|
" let idtl = len(idt)
|
|
" endif
|
|
" VERSION 2
|
|
let idt = matchstr(pre, '^\s\+')
|
|
let idtl = len(idt)
|
|
else
|
|
let [m_0, idt, iline; rest] = matchlist(pre, '^\(\s*\)\(.*\)$')
|
|
let idtl = len(idt)
|
|
endif
|
|
if idtl < icol
|
|
let idt .= repeat(' ', icol - idtl)
|
|
endif
|
|
" TLogVAR idt
|
|
let idtl1 = len(idt)
|
|
for i in range(1, len(text) - 1)
|
|
let text[i] = idt . text[i]
|
|
let grow += idtl1
|
|
endfor
|
|
endif
|
|
" TLogVAR text
|
|
" exec 'norm! '. keyargs.lineno .'Gdd'
|
|
call tlib#normal#WithRegister('"tdd', 't')
|
|
call append(keyargs.lineno - 1, text)
|
|
if post_del_last_line
|
|
call tlib#buffer#KeepCursorPosition('$delete')
|
|
endif
|
|
let tlen = len(text)
|
|
let posshift = matchstr(keyargs.pos, '\d\+')
|
|
" TLogVAR keyargs.pos
|
|
if keyargs.pos =~ '^e'
|
|
exec keyargs.lineno + tlen - 1
|
|
exec 'norm! 0'. (len(text[-1]) - len(post) + posshift - 1) .'l'
|
|
elseif keyargs.pos =~ '^s'
|
|
" TLogVAR keyargs.lineno, pre, posshift
|
|
exec keyargs.lineno
|
|
exec 'norm! '. len(pre) .'|'
|
|
if !empty(posshift)
|
|
exec 'norm! '. posshift .'h'
|
|
endif
|
|
endif
|
|
" TLogDBG getline(keyargs.lineno)
|
|
" TLogDBG string(getline(1, '$'))
|
|
return grow
|
|
endf
|
|
|
|
|
|
function! tlib#buffer#InsertText0(text, ...) "{{{3
|
|
TVarArg ['keyargs', {}]
|
|
let mode = get(keyargs, 'mode', 'i')
|
|
" TLogVAR mode
|
|
if !has_key(keyargs, 'shift')
|
|
let col = col('.')
|
|
" if mode =~ 'i'
|
|
" let col += 1
|
|
" endif
|
|
let keyargs.shift = col >= col('$') ? 0 : -1
|
|
" let keyargs.shift = col('.') >= col('$') ? 0 : -1
|
|
" TLogVAR col
|
|
" TLogDBG col('.') .'-'. col('$') .': '. string(getline('.'))
|
|
endif
|
|
" TLogVAR keyargs.shift
|
|
return tlib#buffer#InsertText(a:text, keyargs)
|
|
endf
|
|
|
|
|
|
function! tlib#buffer#CurrentByte() "{{{3
|
|
return line2byte(line('.')) + col('.')
|
|
endf
|
|
|
|
|
|
" Evaluate cmd while maintaining the cursor position and jump registers.
|
|
function! tlib#buffer#KeepCursorPosition(cmd) "{{{3
|
|
" let pos = getpos('.')
|
|
let view = winsaveview()
|
|
try
|
|
keepjumps exec a:cmd
|
|
finally
|
|
" call setpos('.', pos)
|
|
call winrestview(view)
|
|
endtry
|
|
endf
|
|
|