I broke up with neovim....vim is my best friend now

This commit is contained in:
LinlyBoi
2023-04-30 08:14:07 +03:00
parent 0d185449c5
commit 4a4a6b1e81
5245 changed files with 468325 additions and 25 deletions

View File

@@ -0,0 +1,9 @@
let s:_plugin_name = expand('<sfile>:t:r')
function! vital#{s:_plugin_name}#new() abort
return vital#{s:_plugin_name[1:]}#new()
endfunction
function! vital#{s:_plugin_name}#function(funcname) abort
silent! return function(a:funcname)
endfunction

View File

@@ -0,0 +1,66 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not modify the code nor insert new lines before '" ___vital___'
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_lsp#VS#LSP#MarkupContent#import() abort', printf("return map({'_vital_depends': '', 'normalize': '', '_vital_loaded': ''}, \"vital#_lsp#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
" ___vital___
"
" _vital_loaded
"
function! s:_vital_loaded(V) abort
let s:Text = a:V.import('VS.LSP.Text')
endfunction
"
" _vital_depends
"
function! s:_vital_depends() abort
return ['VS.LSP.Text']
endfunction
"
" normalize
"
function! s:normalize(markup_content, ...) abort
let l:option = get(a:000, 0, {})
let l:option.compact = get(l:option, 'compact', v:true)
let l:normalized = ''
if type(a:markup_content) == type('')
let l:normalized = a:markup_content
elseif type(a:markup_content) == type([])
let l:normalized = join(a:markup_content, "\n")
elseif type(a:markup_content) == type({})
let l:normalized = a:markup_content.value
if has_key(a:markup_content, 'language')
let l:normalized = join([
\ '```' . a:markup_content.language,
\ l:normalized,
\ '```'
\ ], "\n")
endif
endif
let l:normalized = s:Text.normalize_eol(l:normalized)
let l:normalized = s:_format(l:normalized, l:option.compact)
return l:normalized
endfunction
"
" _format
"
function! s:_format(string, compact) abort
let l:string = a:string
if a:compact
let l:string = substitute(l:string, "\\%(\\s\\|\n\\)*```\\s*\\(\\w\\+\\)\\%(\\s\\|\n\\)\\+", "\n\n```\\1 ", 'g')
let l:string = substitute(l:string, "\\%(\\s\\|\n\\)\\+```\\%(\\s*\\%(\\%$\\|\n\\)\\)\\+", " ```\n\n", 'g')
else
let l:string = substitute(l:string, "```\n\\zs\\%(\\s\\|\n\\)\\+", "", 'g')
endif
let l:string = substitute(l:string, "\\%^\\%(\\s\\|\n\\)*", '', 'g')
let l:string = substitute(l:string, "\\%(\\s\\|\n\\)*\\%$", '', 'g')
return l:string
endfunction

View File

@@ -0,0 +1,23 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not modify the code nor insert new lines before '" ___vital___'
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_lsp#VS#LSP#Text#import() abort', printf("return map({'normalize_eol': '', 'split_by_eol': ''}, \"vital#_lsp#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
" ___vital___
"
" normalize_eol
"
function! s:normalize_eol(text) abort
return substitute(a:text, "\r\n\\|\r", "\n", 'g')
endfunction
"
" split_by_eol
"
function! s:split_by_eol(text) abort
return split(a:text, "\r\n\\|\r\\|\n", v:true)
endfunction

View File

@@ -0,0 +1,140 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not modify the code nor insert new lines before '" ___vital___'
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_lsp#VS#Vim#Buffer#import() abort', printf("return map({'add': '', 'do': '', 'create': '', 'get_line_count': '', 'pseudo': '', 'ensure': '', 'load': ''}, \"vital#_lsp#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
" ___vital___
let s:Do = { -> {} }
let g:___VS_Vim_Buffer_id = get(g:, '___VS_Vim_Buffer_id', 0)
"
" get_line_count
"
if exists('*nvim_buf_line_count')
function! s:get_line_count(bufnr) abort
return nvim_buf_line_count(a:bufnr)
endfunction
elseif has('patch-8.2.0019')
function! s:get_line_count(bufnr) abort
return getbufinfo(a:bufnr)[0].linecount
endfunction
else
function! s:get_line_count(bufnr) abort
if bufnr('%') == bufnr(a:bufnr)
return line('$')
endif
return len(getbufline(a:bufnr, '^', '$'))
endfunction
endif
"
" create
"
function! s:create(...) abort
let g:___VS_Vim_Buffer_id += 1
let l:bufname = printf('VS.Vim.Buffer: %s: %s',
\ g:___VS_Vim_Buffer_id,
\ get(a:000, 0, 'VS.Vim.Buffer.Default')
\ )
return s:load(l:bufname)
endfunction
"
" ensure
"
function! s:ensure(expr) abort
if !bufexists(a:expr)
if type(a:expr) == type(0)
throw printf('VS.Vim.Buffer: `%s` is not valid expr.', a:expr)
endif
call s:add(a:expr)
endif
return bufnr(a:expr)
endfunction
"
" add
"
if exists('*bufadd')
function! s:add(name) abort
let l:bufnr = bufadd(a:name)
call setbufvar(l:bufnr, '&buflisted', 1)
endfunction
else
function! s:add(name) abort
badd `=a:name`
endfunction
endif
"
" load
"
if exists('*bufload')
function! s:load(expr) abort
let l:bufnr = s:ensure(a:expr)
if !bufloaded(l:bufnr)
call bufload(l:bufnr)
endif
return l:bufnr
endfunction
else
function! s:load(expr) abort
let l:curr_bufnr = bufnr('%')
try
let l:bufnr = s:ensure(a:expr)
execute printf('keepalt keepjumps silent %sbuffer', l:bufnr)
catch /.*/
echomsg string({ 'exception': v:exception, 'throwpoint': v:throwpoint })
finally
execute printf('noautocmd keepalt keepjumps silent %sbuffer', l:curr_bufnr)
endtry
return l:bufnr
endfunction
endif
"
" do
"
function! s:do(bufnr, func) abort
let l:curr_bufnr = bufnr('%')
if l:curr_bufnr == a:bufnr
call a:func()
return
endif
try
execute printf('noautocmd keepalt keepjumps silent %sbuffer', a:bufnr)
call a:func()
catch /.*/
echomsg string({ 'exception': v:exception, 'throwpoint': v:throwpoint })
finally
execute printf('noautocmd keepalt keepjumps silent %sbuffer', l:curr_bufnr)
endtry
endfunction
"
" pseudo
"
function! s:pseudo(filepath) abort
if !filereadable(a:filepath)
throw printf('VS.Vim.Buffer: `%s` is not valid filepath.', a:filepath)
endif
" create pseudo buffer
let l:bufname = printf('VSVimBufferPseudo://%s', a:filepath)
if bufexists(l:bufname)
return s:ensure(l:bufname)
endif
let l:bufnr = s:ensure(l:bufname)
let l:group = printf('VS_Vim_Buffer_pseudo:%s', l:bufnr)
execute printf('augroup %s', l:group)
execute printf('autocmd BufReadCmd <buffer=%s> call setline(1, readfile(bufname("%")[20 : -1])) | try | filetype detect | catch /.*/ | endtry | augroup %s | autocmd! | augroup END', l:bufnr, l:group)
augroup END
return l:bufnr
endfunction

View File

@@ -0,0 +1,155 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not modify the code nor insert new lines before '" ___vital___'
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_lsp#VS#Vim#Syntax#Markdown#import() abort', printf("return map({'apply': ''}, \"vital#_lsp#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
" ___vital___
"
" apply
"
" TODO: Refactor
"
function! s:apply(...) abort
let l:args = get(a:000, 0, {})
let l:text = has_key(l:args, 'text') ? l:args.text : getbufline('%', 1, '$')
let l:text = type(l:text) == v:t_list ? join(l:text, "\n") : l:text
call s:_execute('syntax sync clear')
if !exists('b:___VS_Vim_Syntax_Markdown')
" Avoid automatic highlighting by built-in runtime syntax.
if !has_key(g:, 'markdown_fenced_languages')
call s:_execute('runtime! syntax/markdown.vim')
else
let l:markdown_fenced_languages = g:markdown_fenced_languages
unlet g:markdown_fenced_languages
call s:_execute('runtime! syntax/markdown.vim')
let g:markdown_fenced_languages = l:markdown_fenced_languages
endif
" Remove markdownCodeBlock because we support it manually.
call s:_clear('markdownCodeBlock') " runtime
call s:_clear('mkdCode') " plasticboy/vim-markdown
" Modify markdownCode (`codes...`)
call s:_clear('markdownCode')
syntax region markdownCode matchgroup=Conceal start=/\%(``\)\@!`/ matchgroup=Conceal end=/\%(``\)\@!`/ containedin=TOP keepend concealends
" Modify markdownEscape (_bold\_text_) @see nvim's syntax/lsp_markdown.vim
call s:_clear('markdownEscape')
syntax region markdownEscape matchgroup=markdownEscape start=/\\\ze[\\\x60*{}\[\]()#+\-,.!_>~|"$%&'\/:;<=?@^ ]/ end=/./ containedin=ALL keepend oneline concealends
" Add syntax for basic html entities.
syntax match vital_vs_vim_syntax_markdown_entities_lt /&lt;/ containedin=ALL conceal cchar=<
syntax match vital_vs_vim_syntax_markdown_entities_gt /&gt;/ containedin=ALL conceal cchar=>
syntax match vital_vs_vim_syntax_markdown_entities_amp /&amp;/ containedin=ALL conceal cchar=&
syntax match vital_vs_vim_syntax_markdown_entities_quot /&quot;/ containedin=ALL conceal cchar="
syntax match vital_vs_vim_syntax_markdown_entities_nbsp /&nbsp;/ containedin=ALL conceal cchar=
let b:___VS_Vim_Syntax_Markdown = {}
let b:___VS_Vim_Syntax_Markdown.marks = {}
let b:___VS_Vim_Syntax_Markdown.filetypes = {}
endif
for [l:mark, l:filetype] in items(s:_get_filetype_map(l:text))
try
let l:mark_group = substitute(toupper(l:mark), '\.', '_', 'g')
if has_key(b:___VS_Vim_Syntax_Markdown.marks, l:mark_group)
continue
endif
let b:___VS_Vim_Syntax_Markdown.marks[l:mark_group] = v:true
let l:filetype_group = substitute(toupper(l:filetype), '\.', '_', 'g')
if !has_key(b:___VS_Vim_Syntax_Markdown.filetypes, l:filetype_group)
call s:_execute('syntax include @%s syntax/%s.vim', l:filetype_group, l:filetype)
let b:___VS_Vim_Syntax_Markdown.filetypes[l:filetype_group] = v:true
endif
call s:_execute('syntax region %s matchgroup=Conceal start=/%s/ matchgroup=Conceal end=/%s/ contains=@%s containedin=TOP keepend concealends',
\ l:mark_group,
\ printf('```\s*%s\s*', l:mark),
\ '```\s*\%(' . "\n" . '\|$\)',
\ l:filetype_group
\ )
catch /.*/
unsilent echomsg printf('Fail to apply "syntax/%s.vim". Add "let g:markdown_fenced_languages = ["%s=$FILETYPE"]" to enable syntax', l:filetype, l:filetype)
endtry
endfor
endfunction
"
" _clear
"
function! s:_clear(group) abort
try
execute printf('silent! syntax clear %s', a:group)
catch /.*/
endtry
endfunction
"
" _execute
"
function! s:_execute(command, ...) abort
let b:current_syntax = ''
unlet b:current_syntax
let g:main_syntax = ''
unlet g:main_syntax
execute call('printf', [a:command] + a:000)
endfunction
"
" _get_filetype_map
"
function! s:_get_filetype_map(text) abort
let l:filetype_map = {}
for l:mark in s:_find_marks(a:text)
let l:filetype_map[l:mark] = s:_get_filetype_from_mark(l:mark)
endfor
return l:filetype_map
endfunction
"
" _find_marks
"
function! s:_find_marks(text) abort
let l:marks = {}
" find from buffer contents.
let l:text = a:text
let l:pos = 0
while 1
let l:match = matchstrpos(l:text, '```\s*\zs\w\+', l:pos, 1)
if empty(l:match[0])
break
endif
let l:marks[l:match[0]] = v:true
let l:pos = l:match[2]
endwhile
return keys(l:marks)
endfunction
"
" _get_filetype_from_mark
"
function! s:_get_filetype_from_mark(mark) abort
for l:config in get(g:, 'markdown_fenced_languages', [])
if l:config !~# '='
if l:config ==# a:mark
return a:mark
endif
else
let l:config = split(l:config, '=')
if l:config[0] ==# a:mark
return l:config[1]
endif
endif
endfor
return a:mark
endfunction

View File

@@ -0,0 +1,157 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not modify the code nor insert new lines before '" ___vital___'
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_lsp#VS#Vim#Window#import() abort', printf("return map({'info': '', 'do': '', 'is_floating': '', 'find': '', 'scroll': '', 'screenpos': ''}, \"vital#_lsp#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
" ___vital___
let s:Do = { -> {} }
"
" do
"
function! s:do(winid, func) abort
let l:curr_winid = win_getid()
if l:curr_winid == a:winid
call a:func()
return
endif
if !has('nvim') && exists('*win_execute')
let s:Do = a:func
try
noautocmd keepalt keepjumps call win_execute(a:winid, 'call s:Do()')
catch /.*/
echomsg string({ 'exception': v:exception, 'throwpoint': v:throwpoint })
endtry
unlet s:Do
return
endif
noautocmd keepalt keepjumps call win_gotoid(a:winid)
try
call a:func()
catch /.*/
echomsg string({ 'exception': v:exception, 'throwpoint': v:throwpoint })
endtry
noautocmd keepalt keepjumps call win_gotoid(l:curr_winid)
endfunction
"
" info
"
if has('nvim')
function! s:info(winid) abort
let l:info = getwininfo(a:winid)[0]
return {
\ 'width': l:info.width,
\ 'height': l:info.height,
\ 'topline': l:info.topline,
\ }
endfunction
else
function! s:info(winid) abort
if s:is_floating(a:winid)
let l:info = popup_getpos(a:winid)
return {
\ 'width': l:info.width,
\ 'height': l:info.height,
\ 'topline': l:info.firstline
\ }
endif
let l:ctx = {}
let l:ctx.info = {}
function! l:ctx.callback() abort
let self.info.width = winwidth(0)
let self.info.height = winheight(0)
let self.info.topline = line('w0')
endfunction
call s:do(a:winid, { -> l:ctx.callback() })
return l:ctx.info
endfunction
endif
"
" find
"
function! s:find(callback) abort
let l:winids = []
let l:winids += map(range(1, tabpagewinnr(tabpagenr(), '$')), 'win_getid(v:val)')
let l:winids += s:_get_visible_popup_winids()
return filter(l:winids, 'a:callback(v:val)')
endfunction
"
" is_floating
"
if has('nvim')
function! s:is_floating(winid) abort
let l:config = nvim_win_get_config(a:winid)
return empty(l:config) || !empty(get(l:config, 'relative', ''))
endfunction
else
function! s:is_floating(winid) abort
return winheight(a:winid) != -1 && win_id2win(a:winid) == 0
endfunction
endif
"
" scroll
"
function! s:scroll(winid, topline) abort
let l:ctx = {}
function! l:ctx.callback(winid, topline) abort
let l:wininfo = s:info(a:winid)
let l:topline = a:topline
let l:topline = min([l:topline, line('$') - l:wininfo.height + 1])
let l:topline = max([l:topline, 1])
if l:topline == l:wininfo.topline
return
endif
if !has('nvim') && s:is_floating(a:winid)
call popup_setoptions(a:winid, {
\ 'firstline': l:topline,
\ })
else
let l:delta = l:topline - l:wininfo.topline
let l:key = l:delta > 0 ? "\<C-e>" : "\<C-y>"
execute printf('noautocmd silent normal! %s', repeat(l:key, abs(l:delta)))
endif
endfunction
call s:do(a:winid, { -> l:ctx.callback(a:winid, a:topline) })
endfunction
"
" screenpos
"
" @param {[number, number]} pos - position on the current buffer.
"
function! s:screenpos(pos) abort
let l:y = a:pos[0]
let l:x = a:pos[1] + get(a:pos, 2, 0)
let l:view = winsaveview()
let l:scroll_x = l:view.leftcol
let l:scroll_y = l:view.topline
let l:winpos = win_screenpos(win_getid())
let l:y = l:winpos[0] + l:y - l:scroll_y
let l:x = l:winpos[1] + l:x - l:scroll_x
return [l:y, l:x + (wincol() - virtcol('.')) - 1]
endfunction
"
" _get_visible_popup_winids
"
function! s:_get_visible_popup_winids() abort
if !exists('*popup_list')
return []
endif
return filter(popup_list(), 'popup_getpos(v:val).visible')
endfunction

View File

@@ -0,0 +1,515 @@
" ___vital___
" NOTE: lines between '" ___vital___' is generated by :Vitalize.
" Do not modify the code nor insert new lines before '" ___vital___'
function! s:_SID() abort
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze__SID$')
endfunction
execute join(['function! vital#_lsp#VS#Vim#Window#FloatingWindow#import() abort', printf("return map({'_vital_depends': '', 'is_available': '', 'new': '', '_vital_loaded': ''}, \"vital#_lsp#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n")
delfunction s:_SID
" ___vital___
"
" _vital_loaded
"
function! s:_vital_loaded(V) abort
let s:Window = a:V.import('VS.Vim.Window')
endfunction
"
" _vital_depends
"
function! s:_vital_depends() abort
return ['VS.Vim.Window']
endfunction
"
" managed floating windows.
"
let s:floating_windows = {}
"
" is_available
"
function! s:is_available() abort
if has('nvim')
return v:true
endif
return exists('*popup_create') && exists('*popup_close') && exists('*popup_move') && exists('*popup_getpos')
endfunction
"
" new
"
function! s:new(...) abort
call s:_init()
return s:FloatingWindow.new(get(a:000, 0, {}))
endfunction
"
" _notify_opened
"
" @param {number} winid
" @param {VS.Vim.Window.FloatingWindow} floating_window
"
function! s:_notify_opened(winid, floating_window) abort
let s:floating_windows[a:winid] = a:floating_window
call a:floating_window._on_opened()
endfunction
"
" _notify_closed
"
function! s:_notify_closed() abort
for [l:winid, l:floating_window] in items(s:floating_windows)
if winheight(l:winid) == -1
call l:floating_window._on_closed()
unlet s:floating_windows[l:winid]
endif
endfor
endfunction
let s:FloatingWindow = {}
"
" new
"
" @param {function?} args.on_opened
" @param {function?} args.on_closed
"
function! s:FloatingWindow.new(args) abort
return extend(deepcopy(s:FloatingWindow), {
\ '_winid': v:null,
\ '_bufnr': v:null,
\ '_vars': {},
\ '_on_opened': get(a:args, 'on_opened', { -> {} }),
\ '_on_closed': get(a:args, 'on_closed', { -> {} }),
\ })
endfunction
"
" get_size
"
" @param {number?} args.minwidth
" @param {number?} args.maxwidth
" @param {number?} args.minheight
" @param {number?} args.maxheight
" @param {boolean?} args.wrap
"
function! s:FloatingWindow.get_size(args) abort
if self._bufnr is# v:null
throw 'VS.Vim.Window.FloatingWindow: Failed to detect bufnr.'
endif
let l:maxwidth = get(a:args, 'maxwidth', -1)
let l:minwidth = get(a:args, 'minwidth', -1)
let l:maxheight = get(a:args, 'maxheight', -1)
let l:minheight = get(a:args, 'minheight', -1)
let l:lines = getbufline(self._bufnr, '^', '$')
" width
let l:width = 0
for l:line in l:lines
let l:width = max([l:width, strdisplaywidth(l:line)])
endfor
let l:width = l:minwidth == -1 ? l:width : max([l:minwidth, l:width])
let l:width = l:maxwidth == -1 ? l:width : min([l:maxwidth, l:width])
" height
if get(a:args, 'wrap', get(self._vars, '&wrap', 0))
let l:height = 0
for l:line in l:lines
let l:height += max([1, float2nr(ceil(strdisplaywidth(l:line) / str2float('' . l:width)))])
endfor
else
let l:height = len(l:lines)
endif
let l:height = l:minheight == -1 ? l:height : max([l:minheight, l:height])
let l:height = l:maxheight == -1 ? l:height : min([l:maxheight, l:height])
return {
\ 'width': max([1, l:width]),
\ 'height': max([1, l:height]),
\ }
endfunction
"
" set_bufnr
"
" @param {number} bufnr
"
function! s:FloatingWindow.set_bufnr(bufnr) abort
let self._bufnr = a:bufnr
endfunction
"
" get_bufnr
"
function! s:FloatingWindow.get_bufnr() abort
return self._bufnr
endfunction
"
" get_winid
"
function! s:FloatingWindow.get_winid() abort
if self.is_visible()
return self._winid
endif
return v:null
endfunction
"
" info
"
function! s:FloatingWindow.info() abort
if self.is_visible()
return s:_info(self._winid)
endif
return v:null
endfunction
"
" set_var
"
" @param {string} key
" @param {unknown} value
"
function! s:FloatingWindow.set_var(key, value) abort
let self._vars[a:key] = a:value
if self.is_visible()
call setwinvar(self._winid, a:key, a:value)
endif
endfunction
"
" get_var
"
" @param {string} key
"
function! s:FloatingWindow.get_var(key) abort
return self._vars[a:key]
endfunction
"
" open
"
" @param {number} args.row 0-based indexing
" @param {number} args.col 0-based indexing
" @param {number} args.width
" @param {number} args.height
" @param {boolean|[string]?} args.border - boolean, or list of characters
" clockwise from top-left (same as nvim_open_win() in neovim)
" @param {number?} args.topline
" @param {string?} args.origin - topleft/topright/botleft/botright
"
function! s:FloatingWindow.open(args) abort
let l:style = {
\ 'row': a:args.row,
\ 'col': a:args.col,
\ 'width': a:args.width,
\ 'height': a:args.height,
\ 'border': get(a:args, 'border', v:false),
\ 'topline': get(a:args, 'topline', 1),
\ 'origin': get(a:args, 'origin', 'topleft'),
\ }
let l:will_move = self.is_visible()
if l:will_move
let self._winid = s:_move(self, self._winid, self._bufnr, l:style)
else
let self._winid = s:_open(self._bufnr, l:style, { -> self._on_closed() })
endif
for [l:key, l:value] in items(self._vars)
call setwinvar(self._winid, l:key, l:value)
endfor
if !l:will_move
call s:_notify_opened(self._winid, self)
endif
endfunction
"
" close
"
function! s:FloatingWindow.close() abort
if self.is_visible()
call s:_close(self._winid)
endif
let self._winid = v:null
endfunction
"
" enter
"
function! s:FloatingWindow.enter() abort
call s:_enter(self._winid)
endfunction
"
" is_visible
"
function! s:FloatingWindow.is_visible() abort
return s:_exists(self._winid) ? v:true : v:false
endfunction
"
" open
"
if has('nvim')
function! s:_open(bufnr, style, callback) abort
let l:winid = nvim_open_win(a:bufnr, v:false, s:_style(a:style))
call s:Window.scroll(l:winid, a:style.topline)
return l:winid
endfunction
else
function! s:_open(bufnr, style, callback) abort
return popup_create(a:bufnr, extend(s:_style(a:style), {
\ 'callback': a:callback,
\ }, 'force'))
endfunction
endif
"
" close
"
if has('nvim')
function! s:_close(winid) abort
call nvim_win_close(a:winid, v:true)
call s:_notify_closed()
endfunction
else
function! s:_close(winid) abort
call popup_close(a:winid)
endfunction
endif
"
" move
"
if has('nvim')
function! s:_move(self, winid, bufnr, style) abort
call nvim_win_set_config(a:winid, s:_style(a:style))
if a:bufnr != nvim_win_get_buf(a:winid)
call nvim_win_set_buf(a:winid, a:bufnr)
endif
call s:Window.scroll(a:winid, a:style.topline)
return a:winid
endfunction
else
function! s:_move(self, winid, bufnr, style) abort
" vim's popup window can't change bufnr so open new popup in here.
if a:bufnr != winbufnr(a:winid)
let l:On_closed = a:self._on_closed
let a:self._on_closed = { -> {} }
call s:_close(a:winid)
let a:self._on_closed = l:On_closed
return s:_open(a:bufnr, a:style, { -> a:self._on_closed() })
endif
let l:style = s:_style(a:style)
call popup_move(a:winid, l:style)
call popup_setoptions(a:winid, l:style)
return a:winid
endfunction
endif
"
" enter
"
if has('nvim')
function! s:_enter(winid) abort
call win_gotoid(a:winid)
endfunction
else
function! s:_enter(winid) abort
" not supported.
endfunction
endif
"
" exists
"
if has('nvim')
function! s:_exists(winid) abort
try
return type(a:winid) == type(0) && nvim_win_is_valid(a:winid) && nvim_win_get_number(a:winid) != -1
catch /.*/
return v:false
endtry
endfunction
else
function! s:_exists(winid) abort
return type(a:winid) == type(0) && winheight(a:winid) != -1
endfunction
endif
"
" info
"
if has('nvim')
function! s:_info(winid) abort
let l:info = getwininfo(a:winid)[0]
return {
\ 'row': l:info.winrow,
\ 'col': l:info.wincol,
\ 'width': l:info.width,
\ 'height': l:info.height,
\ 'topline': l:info.topline,
\ }
endfunction
else
function! s:_info(winid) abort
let l:pos = popup_getpos(a:winid)
return {
\ 'row': l:pos.core_line,
\ 'col': l:pos.core_col,
\ 'width': l:pos.core_width,
\ 'height': l:pos.core_height,
\ 'topline': l:pos.firstline,
\ }
endfunction
endif
"
" style
"
if has('nvim')
function! s:_style(style) abort
let l:style = s:_resolve_origin(a:style)
let l:style = s:_resolve_border(l:style)
let l:style = {
\ 'relative': 'editor',
\ 'row': l:style.row - 1,
\ 'col': l:style.col - 1,
\ 'width': l:style.width,
\ 'height': l:style.height,
\ 'focusable': v:true,
\ 'style': 'minimal',
\ 'border': has_key(l:style, 'border') ? l:style.border : 'none',
\ }
if !exists('*win_execute') " We can't detect neovim features via patch version so we try it by function existence.
unlet l:style.border
endif
return l:style
endfunction
else
function! s:_style(style) abort
let l:style = s:_resolve_origin(a:style)
let l:style = s:_resolve_border(l:style)
return {
\ 'line': l:style.row,
\ 'col': l:style.col,
\ 'pos': 'topleft',
\ 'wrap': v:false,
\ 'moved': [0, 0, 0],
\ 'scrollbar': 0,
\ 'maxwidth': l:style.width,
\ 'maxheight': l:style.height,
\ 'minwidth': l:style.width,
\ 'minheight': l:style.height,
\ 'tabpage': 0,
\ 'firstline': l:style.topline,
\ 'padding': [0, 0, 0, 0],
\ 'border': has_key(l:style, 'border') ? [1, 1, 1, 1] : [0, 0, 0, 0],
\ 'borderchars': get(l:style, 'border', []),
\ 'fixed': v:true,
\ }
endfunction
endif
"
" _resolve_origin
"
function! s:_resolve_origin(style) abort
if index(['topleft', 'topright', 'bottomleft', 'bottomright', 'topcenter', 'bottomcenter'], a:style.origin) == -1
let a:style.origin = 'topleft'
endif
if a:style.origin ==# 'topleft'
let a:style.col = a:style.col
let a:style.row = a:style.row
elseif a:style.origin ==# 'topright'
let a:style.col = a:style.col - (a:style.width - 1)
let a:style.row = a:style.row
elseif a:style.origin ==# 'bottomleft'
let a:style.col = a:style.col
let a:style.row = a:style.row - (a:style.height - 1)
elseif a:style.origin ==# 'bottomright'
let a:style.col = a:style.col - (a:style.width - 1)
let a:style.row = a:style.row - (a:style.height - 1)
elseif a:style.origin ==# 'topcenter'
let a:style.col = a:style.col - float2nr(a:style.width / 2)
let a:style.row = a:style.row
elseif a:style.origin ==# 'bottomcenter'
let a:style.col = a:style.col - float2nr(a:style.width / 2)
let a:style.row = a:style.row - (a:style.height - 1)
elseif a:style.origin ==# 'centercenter'
let a:style.col = a:style.col - float2nr(a:style.width / 2)
let a:style.row = a:style.row - float2nr(a:style.height / 2)
endif
return a:style
endfunction
if has('nvim')
function! s:_resolve_border(style) abort
let l:border = get(a:style, 'border', v:null)
if !empty(l:border)
if type(l:border) != type([])
if &ambiwidth ==# 'single'
let a:style.border = ['┌', '─', '┐', '│', '┘', '─', '└', '│']
else
let a:style.border = ['+', '-', '+', '|', '+', '-', '+', '|']
endif
endif
elseif has_key(a:style, 'border')
unlet a:style.border
endif
return a:style
endfunction
else
function! s:_resolve_border(style) abort
let l:border = get(a:style, 'border', v:null)
if !empty(get(a:style, 'border', v:null))
if type(l:border) != type([])
if &ambiwidth ==# 'single'
let a:style.border = ['─', '│', '─', '│', '┌', '┐', '┘', '└']
else
let a:style.border = ['-', '|', '-', '|', '+', '+', '+', '+']
endif
else
" Emulate nvim behavior for lists of 1/2/4 elements
let l:topleft = l:border[0]
let l:top = get(l:border, 1, l:topleft)
let l:topright = get(l:border, 2, l:topleft)
let l:right = get(l:border, 3, l:top)
let l:bottomright = get(l:border, 4, l:topleft)
let l:bottom = get(l:border, 5, l:top)
let l:bottomleft = get(l:border, 6, l:topright)
let l:left = get(l:border, 7, l:right)
let a:style.border = [
\ l:top, l:right, l:bottom, l:left,
\ l:topleft, l:topright, l:bottomright, l:bottomleft,
\ ]
endif
elseif has_key(a:style, 'border')
unlet a:style.border
endif
return a:style
endfunction
endif
"
" init
"
let s:has_init = v:false
let s:filepath = expand('<sfile>:p')
function! s:_init() abort
if s:has_init || !has('nvim')
return
endif
let s:has_init = v:true
execute printf('augroup VS_Vim_Window_FloatingWindow:%s', s:filepath)
autocmd!
autocmd WinEnter * call <SID>_notify_closed()
augroup END
endfunction

View File

@@ -0,0 +1,334 @@
let s:plugin_name = expand('<sfile>:t:r')
let s:vital_base_dir = expand('<sfile>:h')
let s:project_root = expand('<sfile>:h:h:h')
let s:is_vital_vim = s:plugin_name is# 'vital'
let s:loaded = {}
let s:cache_sid = {}
function! vital#{s:plugin_name}#new() abort
return s:new(s:plugin_name)
endfunction
function! vital#{s:plugin_name}#import(...) abort
if !exists('s:V')
let s:V = s:new(s:plugin_name)
endif
return call(s:V.import, a:000, s:V)
endfunction
let s:Vital = {}
function! s:new(plugin_name) abort
let base = deepcopy(s:Vital)
let base._plugin_name = a:plugin_name
return base
endfunction
function! s:vital_files() abort
if !exists('s:vital_files')
let s:vital_files = map(
\ s:is_vital_vim ? s:_global_vital_files() : s:_self_vital_files(),
\ 'fnamemodify(v:val, ":p:gs?[\\\\/]?/?")')
endif
return copy(s:vital_files)
endfunction
let s:Vital.vital_files = function('s:vital_files')
function! s:import(name, ...) abort dict
let target = {}
let functions = []
for a in a:000
if type(a) == type({})
let target = a
elseif type(a) == type([])
let functions = a
endif
unlet a
endfor
let module = self._import(a:name)
if empty(functions)
call extend(target, module, 'keep')
else
for f in functions
if has_key(module, f) && !has_key(target, f)
let target[f] = module[f]
endif
endfor
endif
return target
endfunction
let s:Vital.import = function('s:import')
function! s:load(...) abort dict
for arg in a:000
let [name; as] = type(arg) == type([]) ? arg[: 1] : [arg, arg]
let target = split(join(as, ''), '\W\+')
let dict = self
let dict_type = type({})
while !empty(target)
let ns = remove(target, 0)
if !has_key(dict, ns)
let dict[ns] = {}
endif
if type(dict[ns]) == dict_type
let dict = dict[ns]
else
unlet dict
break
endif
endwhile
if exists('dict')
call extend(dict, self._import(name))
endif
unlet arg
endfor
return self
endfunction
let s:Vital.load = function('s:load')
function! s:unload() abort dict
let s:loaded = {}
let s:cache_sid = {}
unlet! s:vital_files
endfunction
let s:Vital.unload = function('s:unload')
function! s:exists(name) abort dict
if a:name !~# '\v^\u\w*%(\.\u\w*)*$'
throw 'vital: Invalid module name: ' . a:name
endif
return s:_module_path(a:name) isnot# ''
endfunction
let s:Vital.exists = function('s:exists')
function! s:search(pattern) abort dict
let paths = s:_extract_files(a:pattern, self.vital_files())
let modules = sort(map(paths, 's:_file2module(v:val)'))
return uniq(modules)
endfunction
let s:Vital.search = function('s:search')
function! s:plugin_name() abort dict
return self._plugin_name
endfunction
let s:Vital.plugin_name = function('s:plugin_name')
function! s:_self_vital_files() abort
let builtin = printf('%s/__%s__/', s:vital_base_dir, s:plugin_name)
let installed = printf('%s/_%s/', s:vital_base_dir, s:plugin_name)
let base = builtin . ',' . installed
return split(globpath(base, '**/*.vim', 1), "\n")
endfunction
function! s:_global_vital_files() abort
let pattern = 'autoload/vital/__*__/**/*.vim'
return split(globpath(&runtimepath, pattern, 1), "\n")
endfunction
function! s:_extract_files(pattern, files) abort
let tr = {'.': '/', '*': '[^/]*', '**': '.*'}
let target = substitute(a:pattern, '\.\|\*\*\?', '\=tr[submatch(0)]', 'g')
let regexp = printf('autoload/vital/[^/]\+/%s.vim$', target)
return filter(a:files, 'v:val =~# regexp')
endfunction
function! s:_file2module(file) abort
let filename = fnamemodify(a:file, ':p:gs?[\\/]?/?')
let tail = matchstr(filename, 'autoload/vital/_\w\+/\zs.*\ze\.vim$')
return join(split(tail, '[\\/]\+'), '.')
endfunction
" @param {string} name e.g. Data.List
function! s:_import(name) abort dict
if has_key(s:loaded, a:name)
return copy(s:loaded[a:name])
endif
let module = self._get_module(a:name)
if has_key(module, '_vital_created')
call module._vital_created(module)
endif
let export_module = filter(copy(module), 'v:key =~# "^\\a"')
" Cache module before calling module._vital_loaded() to avoid cyclic
" dependences but remove the cache if module._vital_loaded() fails.
" let s:loaded[a:name] = export_module
let s:loaded[a:name] = export_module
if has_key(module, '_vital_loaded')
try
call module._vital_loaded(vital#{s:plugin_name}#new())
catch
unlet s:loaded[a:name]
throw 'vital: fail to call ._vital_loaded(): ' . v:exception . " from:\n" . s:_format_throwpoint(v:throwpoint)
endtry
endif
return copy(s:loaded[a:name])
endfunction
let s:Vital._import = function('s:_import')
function! s:_format_throwpoint(throwpoint) abort
let funcs = []
let stack = matchstr(a:throwpoint, '^function \zs.*, .\{-} \d\+$')
for line in split(stack, '\.\.')
let m = matchlist(line, '^\(.\+\)\%(\[\(\d\+\)\]\|, .\{-} \(\d\+\)\)$')
if !empty(m)
let [name, lnum, lnum2] = m[1:3]
if empty(lnum)
let lnum = lnum2
endif
let info = s:_get_func_info(name)
if !empty(info)
let attrs = empty(info.attrs) ? '' : join([''] + info.attrs)
let flnum = info.lnum == 0 ? '' : printf(' Line:%d', info.lnum + lnum)
call add(funcs, printf('function %s(...)%s Line:%d (%s%s)',
\ info.funcname, attrs, lnum, info.filename, flnum))
continue
endif
endif
" fallback when function information cannot be detected
call add(funcs, line)
endfor
return join(funcs, "\n")
endfunction
" @vimlint(EVL102, 1, l:_)
" @vimlint(EVL102, 1, l:__)
function! s:_get_func_info(name) abort
let name = a:name
if a:name =~# '^\d\+$' " is anonymous-function
let name = printf('{%s}', a:name)
elseif a:name =~# '^<lambda>\d\+$' " is lambda-function
let name = printf("{'%s'}", a:name)
endif
if !exists('*' . name)
return {}
endif
let body = execute(printf('verbose function %s', name))
let lines = split(body, "\n")
let signature = matchstr(lines[0], '^\s*\zs.*')
let [_, file, lnum; __] = matchlist(lines[1],
\ '^\t\%(Last set from\|.\{-}:\)\s*\zs\(.\{-}\)\%( \S\+ \(\d\+\)\)\?$')
return {
\ 'filename': substitute(file, '[/\\]\+', '/', 'g'),
\ 'lnum': 0 + lnum,
\ 'funcname': a:name,
\ 'arguments': split(matchstr(signature, '(\zs.*\ze)'), '\s*,\s*'),
\ 'attrs': filter(['dict', 'abort', 'range', 'closure'], 'signature =~# (").*" . v:val)'),
\ }
endfunction
" @vimlint(EVL102, 0, l:__)
" @vimlint(EVL102, 0, l:_)
" s:_get_module() returns module object wihch has all script local functions.
function! s:_get_module(name) abort dict
let funcname = s:_import_func_name(self.plugin_name(), a:name)
try
return call(funcname, [])
catch /^Vim\%((\a\+)\)\?:E117:/
return s:_get_builtin_module(a:name)
endtry
endfunction
function! s:_get_builtin_module(name) abort
return s:sid2sfuncs(s:_module_sid(a:name))
endfunction
if s:is_vital_vim
" For vital.vim, we can use s:_get_builtin_module directly
let s:Vital._get_module = function('s:_get_builtin_module')
else
let s:Vital._get_module = function('s:_get_module')
endif
function! s:_import_func_name(plugin_name, module_name) abort
return printf('vital#_%s#%s#import', a:plugin_name, s:_dot_to_sharp(a:module_name))
endfunction
function! s:_module_sid(name) abort
let path = s:_module_path(a:name)
if !filereadable(path)
throw 'vital: module not found: ' . a:name
endif
let vital_dir = s:is_vital_vim ? '__\w\+__' : printf('_\{1,2}%s\%%(__\)\?', s:plugin_name)
let base = join([vital_dir, ''], '[/\\]\+')
let p = base . substitute('' . a:name, '\.', '[/\\\\]\\+', 'g')
let sid = s:_sid(path, p)
if !sid
call s:_source(path)
let sid = s:_sid(path, p)
if !sid
throw printf('vital: cannot get <SID> from path: %s', path)
endif
endif
return sid
endfunction
function! s:_module_path(name) abort
return get(s:_extract_files(a:name, s:vital_files()), 0, '')
endfunction
function! s:_module_sid_base_dir() abort
return s:is_vital_vim ? &rtp : s:project_root
endfunction
function! s:_dot_to_sharp(name) abort
return substitute(a:name, '\.', '#', 'g')
endfunction
function! s:_source(path) abort
execute 'source' fnameescape(a:path)
endfunction
" @vimlint(EVL102, 1, l:_)
" @vimlint(EVL102, 1, l:__)
function! s:_sid(path, filter_pattern) abort
let unified_path = s:_unify_path(a:path)
if has_key(s:cache_sid, unified_path)
return s:cache_sid[unified_path]
endif
for line in filter(split(execute(':scriptnames'), "\n"), 'v:val =~# a:filter_pattern')
let [_, sid, path; __] = matchlist(line, '^\s*\(\d\+\):\s\+\(.\+\)\s*$')
if s:_unify_path(path) is# unified_path
let s:cache_sid[unified_path] = sid
return s:cache_sid[unified_path]
endif
endfor
return 0
endfunction
if filereadable(expand('<sfile>:r') . '.VIM') " is case-insensitive or not
let s:_unify_path_cache = {}
" resolve() is slow, so we cache results.
" Note: On windows, vim can't expand path names from 8.3 formats.
" So if getting full path via <sfile> and $HOME was set as 8.3 format,
" vital load duplicated scripts. Below's :~ avoid this issue.
function! s:_unify_path(path) abort
if has_key(s:_unify_path_cache, a:path)
return s:_unify_path_cache[a:path]
endif
let value = tolower(fnamemodify(resolve(fnamemodify(
\ a:path, ':p')), ':~:gs?[\\/]?/?'))
let s:_unify_path_cache[a:path] = value
return value
endfunction
else
function! s:_unify_path(path) abort
return resolve(fnamemodify(a:path, ':p:gs?[\\/]?/?'))
endfunction
endif
" copied and modified from Vim.ScriptLocal
let s:SNR = join(map(range(len("\<SNR>")), '"[\\x" . printf("%0x", char2nr("\<SNR>"[v:val])) . "]"'), '')
function! s:sid2sfuncs(sid) abort
let fs = split(execute(printf(':function /^%s%s_', s:SNR, a:sid)), "\n")
let r = {}
let pattern = printf('\m^function\s<SNR>%d_\zs\w\{-}\ze(', a:sid)
for fname in map(fs, 'matchstr(v:val, pattern)')
let r[fname] = function(s:_sfuncname(a:sid, fname))
endfor
return r
endfunction
"" Return funcname of script local functions with SID
function! s:_sfuncname(sid, funcname) abort
return printf('<SNR>%s_%s', a:sid, a:funcname)
endfunction

View File

@@ -0,0 +1,8 @@
lsp
b1e91b41f5028d65fa3d31a425ff21591d5d957f
VS.LSP.MarkupContent
VS.Vim.Window.FloatingWindow
VS.Vim.Syntax.Markdown
VS.Vim.Buffer
VS.Vim.Window