Skip to content

Commit

Permalink
Introduce floatwin/popupwin compatible layer
Browse files Browse the repository at this point in the history
  • Loading branch information
hrsh7th committed Nov 20, 2019
1 parent 45d5bff commit 5a93f51
Show file tree
Hide file tree
Showing 10 changed files with 636 additions and 71 deletions.
23 changes: 14 additions & 9 deletions autoload/lsp/ui/vim.vim
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
let s:Floatwin = lsp#ui#vim#floatwin#import()
let s:floatwin = s:Floatwin.new({
\ 'close_on': ['InsertEnter', 'CursorMoved']
\ })
let s:last_req_id = 0

function! s:not_supported(what) abort
Expand Down Expand Up @@ -506,16 +510,17 @@ function! s:handle_location(ctx, server, type, data) abort "ctx = {counter, list
let l:lines = readfile(fnameescape(l:loc['filename']))
if has_key(l:loc,'viewstart') " showing a locationLink
let l:view = l:lines[l:loc['viewstart'] : l:loc['viewend']]
call lsp#ui#vim#output#preview(a:server, l:view, {
\ 'statusline': ' LSP Peek ' . a:type,
\ 'filetype': &filetype
\ })
let l:screenpos = lsp#ui#vim#floatwin#screenpos(line('.'), col('.'))
call s:floatwin.show(l:screenpos, lsp#utils#normalize_markup_content({
\ 'language': &filetype,
\ 'value': join(l:view, "\n")
\ }))
else " showing a location
call lsp#ui#vim#output#preview(a:server, l:lines, {
\ 'statusline': ' LSP Peek ' . a:type,
\ 'cursor': { 'line': l:loc['lnum'], 'col': l:loc['col'], 'align': g:lsp_peek_alignment },
\ 'filetype': &filetype
\ })
let l:screenpos = lsp#ui#vim#floatwin#screenpos(line('.'), col('.'))
call s:floatwin.show(l:screenpos, lsp#utils#normalize_markup_content({
\ 'language': &filetype,
\ 'value': join(l:lines, "\n")
\ }))
endif
endif
endif
Expand Down
183 changes: 183 additions & 0 deletions autoload/lsp/ui/vim/floatwin.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
let s:floatwin_id = 0

let s:namespace = has('nvim') ? 'nvim' : 'vim'

"
" lsp#ui#vim#floatwin#screenpos
"
function! lsp#ui#vim#floatwin#screenpos(line, col) abort
let l:pos = getpos('.')
let l:scroll_x = (l:pos[2] + l:pos[3]) - wincol()
let l:scroll_y = l:pos[1] - winline()
let l:winpos = win_screenpos(win_getid())
return [l:winpos[0] + (a:line - l:scroll_y) - 1, l:winpos[1] + (a:col - l:scroll_x) - 1]
endfunction

"
" lsp#ui#vim#floatwin#import
"
function! lsp#ui#vim#floatwin#import() abort
return s:Floatwin
endfunction

let s:Floatwin = {}

"
" new
"
function! s:Floatwin.new(option) abort
let s:floatwin_id += 1
let l:bufname = printf('lsp_floatwin-%s.lsp_floatwin', s:floatwin_id)
let l:bufnr = bufnr(l:bufname, v:true)
call setbufvar(l:bufnr, '&buflisted', 0)
call setbufvar(l:bufnr, '&buftype', 'nofile')
call setbufvar(l:bufnr, '&filetype', 'lsp_floatwin')
return extend(deepcopy(s:Floatwin), {
\ 'id': s:floatwin_id,
\ 'bufnr': l:bufnr,
\ 'max_width': get(a:option, 'max_width', &columns / 3),
\ 'max_height': get(a:option, 'max_height', &lines / 2),
\ 'close_on': get(a:option, 'close_on', []),
\ 'screenpos': [0, 0],
\ 'contents': []
\ })
endfunction

"
" show_tooltip
"
function! s:Floatwin.show_tooltip(screenpos, contents) abort
let l:width = self.get_width(a:contents)
let l:height = self.get_height(a:contents)

let l:screenpos = copy(a:screenpos)
let l:screenpos[0] -= 1
let l:screenpos[1] -= 1

" fix height.
if l:screenpos[0] - l:height >= 0
let l:screenpos[0] -= l:height
else
let l:screenpos[0] += 1
endif

" fix width.
if &columns < l:screenpos[1] + l:width
let l:screenpos[1] -= l:screenpos[1] + l:width - &columns
endif

call self.show(l:screenpos, a:contents)
endfunction

"
" show
"
function! s:Floatwin.show(screenpos, contents) abort
let self.screenpos = a:screenpos
let self.contents = a:contents

" create lines.
let l:lines = []
for l:content in a:contents
let l:lines += l:content
endfor

" update bufvars.
call setbufvar(self.bufnr, 'lsp_floatwin_lines', l:lines)

" show or move
call lsp#ui#vim#floatwin#{s:namespace}#show(self)
call setwinvar(self.winid(), '&wrap', 1)
call setwinvar(self.winid(), '&conceallevel', 3)

" write lines
call lsp#ui#vim#floatwin#{s:namespace}#write(self, l:lines)

" update syntax highlight.
if has('nvim') && LspFloatwinSyntaxShouldUpdate(self.bufnr)
call lsp#utils#windo(self.winid(), { -> LspFloatwinSyntaxUpdate() })
endif

call self.set_close_events()
endfunction

"
" hide
"
function! s:Floatwin.hide() abort
augroup printf('lsp#ui#vim#floatwin#hide_%s', self.id)
autocmd!
augroup END
call lsp#ui#vim#floatwin#{s:namespace}#hide(self)
endfunction

"
" enter
"
function! s:Floatwin.enter() abort
call lsp#ui#vim#floatwin#{s:namespace}#enter(self)
endfunction

"
" is_showing
"
function! s:Floatwin.is_showing() abort
return lsp#ui#vim#floatwin#{s:namespace}#is_showing(self)
endfunction

"
" winid
"
function! s:Floatwin.winid() abort
return lsp#ui#vim#floatwin#{s:namespace}#winid(self)
endfunction

"
" set_close_events
"
function! s:Floatwin.set_close_events() abort
let l:close_fn = printf('lsp_floatwin_close_%s', self.id)
let b:[l:close_fn] = { -> self.hide() }

augroup printf('lsp#ui#vim#floatwin#hide_%s', self.id)
autocmd!
for l:event in self.close_on
execute printf('autocmd %s <buffer> call b:%s()', l:event, l:close_fn)
endfor
augroup END
endfunction

"
" get_width
"
function! s:Floatwin.get_width(contents) abort
let l:width = 0
for l:content in a:contents
let l:width = max([l:width] + map(copy(l:content), { k, v -> strdisplaywidth(v) }))
endfor

if self.max_width != -1
return max([min([self.max_width, l:width]), 1])
endif
return max([l:width, 1])
endfunction

"
" get_height
"
function! s:Floatwin.get_height(contents) abort
let l:width = self.get_width(a:contents)

let l:height = len(a:contents) - 1
for l:content in a:contents
for l:line in l:content
let l:height += max([1, float2nr(ceil(strdisplaywidth(l:line) / str2float('' . l:width)))])
endfor
endfor

if self.max_height != -1
return max([min([self.max_height, l:height]), 1])
endif
return max([l:height, 1])
endfunction

78 changes: 78 additions & 0 deletions autoload/lsp/ui/vim/floatwin/nvim.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"
" lsp#ui#vim#floatwin#nvim#show
"
function! lsp#ui#vim#floatwin#nvim#show(floatwin) abort
if lsp#ui#vim#floatwin#nvim#is_showing(a:floatwin)
call nvim_win_set_config(a:floatwin.nvim_window, s:get_config(a:floatwin))
else
let a:floatwin.nvim_window = nvim_open_win(a:floatwin.bufnr, v:false, s:get_config(a:floatwin))
endif
endfunction

"
" lsp#ui#vim#floatwin#nvim#hide
"
function! lsp#ui#vim#floatwin#nvim#hide(floatwin) abort
if lsp#ui#vim#floatwin#nvim#is_showing(a:floatwin)
call nvim_win_close(a:floatwin.nvim_window, v:true)
let a:floatwin.nvim_window = v:null
endif
endfunction

"
" lsp#ui#vim#floatwin#nvim#write
"
function! lsp#ui#vim#floatwin#nvim#write(floatwin, lines) abort
call nvim_buf_set_lines(a:floatwin.bufnr, 0, -1, v:true, a:lines)
endfunction

"
" lsp#ui#vim#floatwin#nvim#enter
"
function! lsp#ui#vim#floatwin#nvim#enter(floatwin) abort
if lsp#ui#vim#floatwin#nvim#is_showing(a:floatwin)
execute printf('%swincmd w', win_id2win(lsp#ui#vim#floatwin#nvim#winid(a:floatwin)))
endif
endfunction

"
" lsp#ui#vim#floatwin#nvim#is_showing
"
function! lsp#ui#vim#floatwin#nvim#is_showing(floatwin) abort
if !has_key(a:floatwin,'nvim_window') || a:floatwin.nvim_window is v:null
return v:false
endif

try
return nvim_win_get_number(a:floatwin.nvim_window) != -1
catch /.*/
let a:floatwin.nvim_window = v:null
endtry
return v:false
endfunction

"
" lsp#ui#vim#floatwin#nvim#winid
"
function! lsp#ui#vim#floatwin#nvim#winid(floatwin) abort
if lsp#ui#vim#floatwin#nvim#is_showing(a:floatwin)
return win_getid(nvim_win_get_number(a:floatwin.nvim_window))
endif
return -1
endfunction

"
" s:get_config
"
function! s:get_config(floatwin) abort
return {
\ 'relative': 'editor',
\ 'width': a:floatwin.get_width(a:floatwin.contents),
\ 'height': a:floatwin.get_height(a:floatwin.contents),
\ 'row': a:floatwin.screenpos[0],
\ 'col': a:floatwin.screenpos[1],
\ 'focusable': v:true,
\ 'style': 'minimal'
\ }
endfunction

83 changes: 83 additions & 0 deletions autoload/lsp/ui/vim/floatwin/vim.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"
" lsp#ui#vim#floatwin#vim#show
"
function! lsp#ui#vim#floatwin#vim#show(floatwin) abort
if lsp#ui#vim#floatwin#vim#is_showing(a:floatwin)
call popup_move(a:floatwin.vim_winid, s:get_config(a:floatwin))
else
let a:floatwin.vim_winid = popup_create(a:floatwin.bufnr, s:get_config(a:floatwin))
endif
endfunction

"
" lsp#ui#vim#floatwin#vim#hide
"
function! lsp#ui#vim#floatwin#vim#hide(floatwin) abort
try
call popup_hide(a:floatwin.vim_winid)
catch /.*/
endtry
let a:floatwin.vim_winid = v:null
endfunction

"
" lsp#ui#vim#floatwin#vim#write
"
function! lsp#ui#vim#floatwin#vim#write(floatwin, lines) abort
call deletebufline(a:floatwin.bufnr, '^', '$')
for l:line in reverse(a:lines)
call appendbufline(a:floatwin.bufnr, 0, l:line)
endfor
call deletebufline(a:floatwin.bufnr, '$')
endfunction

"
" lsp#ui#vim#floatwin#vim#enter
"
function! lsp#ui#vim#floatwin#vim#enter(floatwin) abort
" noop
endfunction

"
" lsp#ui#vim#floatwin#vim#is_showing
"
function! lsp#ui#vim#floatwin#vim#is_showing(floatwin) abort
if !has_key(a:floatwin, 'vim_winid') || a:floatwin.vim_winid is v:null
return v:false
endif

if win_id2win(a:floatwin.vim_winid) == -1
let a:floatwin.vim_winid = v:null
return v:false
endif
return v:true
endfunction

"
" lsp#ui#vim#floatwin#vim#winid
"
function! lsp#ui#vim#floatwin#vim#winid(floatwin) abort
if lsp#ui#vim#floatwin#vim#is_showing(a:floatwin)
return a:floatwin.vim_winid
endif
return -1
endfunction

"
" s:get_config
"
function! s:get_config(floatwin) abort
return {
\ 'line': a:floatwin.screenpos[0] + 1,
\ 'col': a:floatwin.screenpos[1] + 1,
\ 'pos': 'topleft',
\ 'moved': [0, 100000],
\ 'scrollbar': 0,
\ 'maxwidth': a:floatwin.get_width(a:floatwin.contents),
\ 'maxheight': a:floatwin.get_height(a:floatwin.contents),
\ 'minwidth': a:floatwin.get_width(a:floatwin.contents),
\ 'minheight': a:floatwin.get_height(a:floatwin.contents),
\ 'tabpage': 0
\ }
endfunction

Loading

0 comments on commit 5a93f51

Please sign in to comment.