mirror of
https://github.com/SpaceVim/SpaceVim.git
synced 2025-03-25 19:32:20 +08:00
351 lines
10 KiB
VimL
351 lines
10 KiB
VimL
"=============================================================================
|
|
" buffer.vim --- SpaceVim buffer API
|
|
" Copyright (c) 2016-2023 Wang Shidong & Contributors
|
|
" Author: Wang Shidong < wsdjeg@outlook.com >
|
|
" URL: https://spacevim.org
|
|
" License: GPLv3
|
|
"=============================================================================
|
|
|
|
""
|
|
" @section vim#buffer, api-vim-buffer
|
|
" @parentsection api
|
|
" @subsection Intro
|
|
"
|
|
" vim#buffer API provides some basic functions for setting and getting config
|
|
" of vim buffer.
|
|
"
|
|
" @subsection Functions
|
|
"
|
|
" is_cmdwin()
|
|
"
|
|
" Check if current windows is command line windows.
|
|
"
|
|
" open(opt)
|
|
"
|
|
" Open a new buffer with specifice options, return the buffer number, the {opt}
|
|
" is a dict with following keys:
|
|
"
|
|
" bufname : the buffer name of the new buffer
|
|
"
|
|
" mode: how to open the new buffer, default is vertical topleft split
|
|
"
|
|
" initfunc: the function which will be call after creating buffer
|
|
"
|
|
" cmd: the ex command which will be run after the new buffer is created
|
|
|
|
|
|
let s:self = {}
|
|
|
|
if exists('*getcmdwintype')
|
|
function! s:self.is_cmdwin() abort
|
|
return getcmdwintype() !=# ''
|
|
endfunction
|
|
else
|
|
function! s:self.is_cmdwin() abort
|
|
return bufname('%') ==# '[Command Line]'
|
|
endfunction
|
|
endif
|
|
|
|
function! s:self.set_var(buf, var, val) abort
|
|
return setbufvar(a:buf, a:var, a:val)
|
|
endfunction
|
|
|
|
function! s:self.get_var(buf, var) abort
|
|
return getbufvar(a:buf, a:var)
|
|
endfunction
|
|
|
|
" bufnr needs atleast one argv before patch-8.1.1924 has('patch-8.1.1924')
|
|
function! s:self.bufnr(...) abort
|
|
if has('patch-8.1.1924')
|
|
return call('bufnr', a:000)
|
|
else
|
|
if a:0 ==# 0
|
|
return bufnr('%')
|
|
else
|
|
return call('bufnr', a:000)
|
|
endif
|
|
endif
|
|
endfunction
|
|
|
|
|
|
function! s:self.bufadd(name) abort
|
|
if exists('*bufadd')
|
|
return bufadd(a:name)
|
|
elseif get(g:, '_spacevim_if_lua', 0) && empty(a:name)
|
|
let nr = float2nr(luaeval('vim.open().number'))
|
|
call setbufvar(nr, '&buflisted', 0)
|
|
return nr
|
|
elseif empty(a:name)
|
|
" create an no-named buffer
|
|
noautocmd 1new
|
|
" bufnr needs atleast one argv before patch-8.1.1924 has('patch-8.1.1924')
|
|
let nr = self.bufnr()
|
|
setl nobuflisted
|
|
noautocmd q
|
|
return nr
|
|
elseif bufexists(a:name)
|
|
return bufnr(a:name)
|
|
else
|
|
exe 'noautocmd 1split ' . a:name
|
|
let nr = self.bufnr()
|
|
setl nobuflisted
|
|
noautocmd q
|
|
return nr
|
|
endif
|
|
endfunction
|
|
if exists('*nvim_create_buf')
|
|
function! s:self.create_buf(listed, scratch) abort
|
|
return nvim_create_buf(a:listed, a:scratch)
|
|
endfunction
|
|
else
|
|
function! s:self.create_buf(listed, scratch) abort
|
|
let bufnr = self.bufadd('')
|
|
if exists('*bufloaded')
|
|
\ && exists('*bufload')
|
|
\ && !bufloaded(bufnr)
|
|
call bufload(bufnr)
|
|
endif
|
|
call setbufvar(bufnr, '&buflisted', a:listed ? 1 : 0)
|
|
if a:scratch
|
|
call setbufvar(bufnr, '&swapfile', 0)
|
|
call setbufvar(bufnr, '&bufhidden', 'hide')
|
|
call setbufvar(bufnr, '&buftype', 'nofile')
|
|
endif
|
|
return bufnr
|
|
endfunction
|
|
endif
|
|
|
|
function! s:self.open(opts) abort
|
|
let buf = get(a:opts, 'bufname', '')
|
|
let mode = get(a:opts, 'mode', 'vertical topleft split')
|
|
let Initfunc = get(a:opts, 'initfunc', '')
|
|
let cmd = get(a:opts, 'cmd', '')
|
|
if empty(buf)
|
|
exe mode | enew
|
|
else
|
|
exe mode buf
|
|
endif
|
|
if !empty(Initfunc)
|
|
call call(Initfunc, [])
|
|
endif
|
|
|
|
if !empty(cmd)
|
|
exe cmd
|
|
endif
|
|
return bufnr('%')
|
|
endfunction
|
|
|
|
|
|
func! s:self.resize(size, ...) abort
|
|
let cmd = get(a:000, 0, 'vertical')
|
|
exe cmd 'resize' a:size
|
|
endf
|
|
|
|
function! s:self.listed_buffers() abort
|
|
return filter(range(1, bufnr('$')), 'buflisted(v:val)')
|
|
endfunction
|
|
|
|
|
|
function! s:self.filter_do(expr) abort
|
|
let buffers = range(1, bufnr('$'))
|
|
for f_expr in a:expr.expr
|
|
let buffers = filter(buffers, f_expr)
|
|
endfor
|
|
for b in buffers
|
|
exe printf(a:expr.do, b)
|
|
endfor
|
|
endfunction
|
|
|
|
" define self.line_count(buf)
|
|
" use nvim_buf_line_count if possible
|
|
if exists('*nvim_buf_line_count') " {{{
|
|
function! s:self.line_count(buf) abort
|
|
return nvim_buf_line_count(a:buf)
|
|
endfunction " }}}
|
|
" if +lua is enabled.
|
|
elseif get(g:, '_spacevim_if_lua', 0) " {{{
|
|
" @vimlint(EVL103, 1, a:buf)
|
|
function! s:self.line_count(buf) abort
|
|
" lua numbers are floats, so use float2nr
|
|
return float2nr(luaeval('#vim.buffer(vim.eval("a:buf"))'))
|
|
endfunction
|
|
" @vimlint(EVL103, 0, a:buf) }}}
|
|
else
|
|
function! s:self.line_count(buf) abort
|
|
return len(getbufline(a:buf, 1, '$'))
|
|
endfunction
|
|
endif
|
|
|
|
|
|
" buffer.buf_set_lines(buffer, start, end, strict_indexing, replacement)
|
|
"
|
|
" this function is just same as nvim_buf_set_lines.
|
|
function! s:self.buf_set_lines(buffer, start, end, strict_indexing, replacement) abort
|
|
if !bufexists(a:buffer)
|
|
return
|
|
endif
|
|
let ma = getbufvar(a:buffer, '&ma')
|
|
call setbufvar(a:buffer,'&ma', 1)
|
|
" if the function `nvim_buf_set_lines` exists
|
|
if exists('*nvim_buf_set_lines') " {{{
|
|
call nvim_buf_set_lines(a:buffer, a:start, a:end, a:strict_indexing, a:replacement)
|
|
" }}}
|
|
elseif exists('*deletebufline') && exists('*bufload')
|
|
" patch-8.1.0039 deletebufline()
|
|
" patch-8.1.0037 appendbufline()
|
|
" patch-8.0.1039 setbufline()
|
|
" patch-8.1.1610 bufadd() bufload()
|
|
|
|
" in vim, setbufline will not load buffer automatically
|
|
" but in neovim, nvim_buf_set_lines will do it.
|
|
" @fixme vim issue #5044
|
|
" https://github.com/vim/vim/issues/5044
|
|
if !bufloaded(a:buffer)
|
|
call bufload(a:buffer)
|
|
endif
|
|
|
|
let lct = self.line_count(a:buffer)
|
|
let start = a:start + (a:start < 0 ? lct + 1 : 0)
|
|
let end = a:end + (a:end < 0 ? lct + 1 : 0)
|
|
|
|
if start < 0 || end < 0 || start > lct || end > lct
|
|
if a:strict_indexing
|
|
" @fixme raise an error
|
|
return
|
|
else
|
|
let start = start < 0 ? 0 : start > lct ? lct : start
|
|
let end = end < 0 ? 0 : end > lct ? lct : end
|
|
endif
|
|
endif
|
|
if start > end
|
|
" @fixme raise an error
|
|
return
|
|
endif
|
|
|
|
" in neovim, indexing is zero-based, end-exclusive
|
|
" but in vim, indexing is one-based, end-inclusive
|
|
let tochange = min([len(a:replacement), end - start])
|
|
if tochange > 0
|
|
call setbufline(a:buffer, start + 1, a:replacement[:tochange-1])
|
|
endif
|
|
if end > start + tochange
|
|
call deletebufline(a:buffer, start + 1 + tochange, end)
|
|
else
|
|
call appendbufline(a:buffer, start + tochange, a:replacement[tochange:])
|
|
endif
|
|
elseif has('python')
|
|
py << EOF
|
|
import vim
|
|
import string
|
|
bufnr = int(vim.eval("a:buffer"))
|
|
start_line = int(vim.eval("a:start"))
|
|
if start_line < 0:
|
|
start_line = len(vim.buffers[bufnr]) + 1 + start_line
|
|
end_line = int(vim.eval("a:end"))
|
|
if end_line < 0:
|
|
end_line = len(vim.buffers[bufnr]) + 1 + end_line
|
|
lines = vim.eval("a:replacement")
|
|
vim.buffers[bufnr][start_line:end_line] = lines
|
|
EOF
|
|
elseif has('python3')
|
|
" https://github.com/vim/vim/issues/3117
|
|
" https://github.com/Azure/WALinuxAgent/issues/2326
|
|
" https://github.com/powerline/powerline/issues/1925#issuecomment-402635097
|
|
silent! python3 1
|
|
py3 << EOF
|
|
import vim
|
|
import string
|
|
bufnr = int(vim.eval("a:buffer"))
|
|
start_line = int(vim.eval("a:start"))
|
|
if start_line < 0:
|
|
start_line = len(vim.buffers[bufnr]) + 1 + start_line
|
|
end_line = int(vim.eval("a:end"))
|
|
if end_line < 0:
|
|
end_line = len(vim.buffers[bufnr]) + 1 + end_line
|
|
lines = vim.eval("a:replacement")
|
|
vim.buffers[bufnr][start_line:end_line] = lines
|
|
EOF
|
|
elseif get(g:, '_spacevim_if_lua', 0) == 1
|
|
" @todo add lua support
|
|
silent! noautocmd lua require("spacevim.api.vim.buffer").set_lines(
|
|
\ vim.eval("a:buffer"),
|
|
\ vim.eval("a:start"),
|
|
\ vim.eval("a:end"),
|
|
\ vim.eval("a:replacement")
|
|
\ )
|
|
else
|
|
exe 'b' . a:buffer
|
|
let lct = line('$')
|
|
if a:start > lct
|
|
return
|
|
elseif a:start >= 0 && a:end > a:start
|
|
let endtext = a:end > lct ? [] : getline(a:end + 1, '$')
|
|
" 0 start end $
|
|
if len(a:replacement) == a:end - a:start
|
|
for i in range(a:start, len(a:replacement) + a:start - 1)
|
|
call setline(i + 1, a:replacement[i - a:start])
|
|
endfor
|
|
else
|
|
let replacement = a:replacement + endtext
|
|
for i in range(a:start, len(replacement) + a:start - 1)
|
|
call setline(i + 1, replacement[i - a:start])
|
|
endfor
|
|
endif
|
|
elseif a:start >= 0 && a:end < 0 && lct + a:end > a:start
|
|
call self.buf_set_lines(a:buffer, a:start, lct + a:end + 1, a:strict_indexing, a:replacement)
|
|
elseif a:start <= 0 && a:end > a:start && a:end < 0 && lct + a:start >= 0
|
|
call self.buf_set_lines(a:buffer, lct + a:start + 1, lct + a:end + 1, a:strict_indexing, a:replacement)
|
|
endif
|
|
endif
|
|
call setbufvar(a:buffer,'&ma', ma)
|
|
endfunction
|
|
|
|
|
|
function! s:self.displayArea() abort
|
|
return [
|
|
\ line('w0'), line('w$')
|
|
\ ]
|
|
endfunction
|
|
|
|
function! s:self.open_pos(cmd, file, line, col) abort
|
|
exe 'silent ' . a:cmd . ' ' . a:file
|
|
call cursor(a:line, a:col)
|
|
endfunction
|
|
|
|
function! s:self.add_highlight(bufnr, hl, line, col, long) abort
|
|
if exists('*nvim_buf_add_highlight')
|
|
call nvim_buf_add_highlight(a:bufnr, 0, a:hl, a:line, a:col, a:col + a:long)
|
|
else
|
|
call SpaceVim#logger#warn('vim#buffer.add_highlight api only support neovim')
|
|
endif
|
|
endfunction
|
|
|
|
function! s:self.buf_get_lines(bufnr, start, end, strict_indexing) abort
|
|
if exists('*nvim_buf_get_lines')
|
|
return nvim_buf_get_lines(a:bufnr, a:start, a:end, a:strict_indexing)
|
|
elseif exists('*getbufline') && exists('*bufload') && exists('*bufloaded')
|
|
let lct = self.line_count(a:bufnr)
|
|
if a:start > lct
|
|
return
|
|
elseif a:start >= 0 && a:end > a:start
|
|
" in vim, getbufline will not load buffer automatically
|
|
" but in neovim, nvim_buf_set_lines will do it.
|
|
" @fixme vim issue #5044
|
|
" https://github.com/vim/vim/issues/5044
|
|
if !bufloaded(a:bufnr)
|
|
call bufload(a:bufnr)
|
|
endif
|
|
return getbufline(a:bufnr, a:start + 1, a:end)
|
|
elseif a:start >= 0 && a:end < 0 && lct + a:end >= a:start
|
|
return self.buf_get_lines(a:bufnr, a:start, lct + a:end + 1, a:strict_indexing)
|
|
elseif a:start <= 0 && a:end > a:start && a:end < 0 && lct + a:start >= 0
|
|
return self.buf_get_lines(a:bufnr, lct + a:start + 1, lct + a:end + 2, a:strict_indexing)
|
|
endif
|
|
endif
|
|
endfunction
|
|
|
|
|
|
fu! SpaceVim#api#vim#buffer#get() abort
|
|
return deepcopy(s:self)
|
|
endf
|