|
| 1 | +let s:floatwin_id = 0 |
| 2 | + |
| 3 | +let s:namespace = has('nvim') ? 'nvim' : 'vim' |
| 4 | + |
| 5 | +" |
| 6 | +" lsp#ui#vim#floatwin#screenpos |
| 7 | +" |
| 8 | +function! lsp#ui#vim#floatwin#screenpos(line, col) abort |
| 9 | + let l:pos = getpos('.') |
| 10 | + let l:scroll_x = (l:pos[2] + l:pos[3]) - wincol() |
| 11 | + let l:scroll_y = l:pos[1] - winline() |
| 12 | + let l:winpos = win_screenpos(win_getid()) |
| 13 | + return [l:winpos[0] + (a:line - l:scroll_y) - 1, l:winpos[1] + (a:col - l:scroll_x) - 1] |
| 14 | +endfunction |
| 15 | + |
| 16 | +" |
| 17 | +" lsp#ui#vim#floatwin#import |
| 18 | +" |
| 19 | +function! lsp#ui#vim#floatwin#import() abort |
| 20 | + return s:Floatwin |
| 21 | +endfunction |
| 22 | + |
| 23 | +" |
| 24 | +" lsp#ui#vim#floatwin#normalize_markup_content |
| 25 | +" |
| 26 | +function! lsp#ui#vim#floatwin#normalize_markup_content(markup_content) abort |
| 27 | + let l:normalized = [] |
| 28 | + for l:markup_content in type(a:markup_content) == type([]) ? a:markup_content : [a:markup_content] |
| 29 | + |
| 30 | + " { 'language': ..., 'value': ... } |
| 31 | + if type(l:markup_content) == type({}) |
| 32 | + let l:string = get(l:markup_content, 'value', '') |
| 33 | + if has_key(l:markup_content, 'language') |
| 34 | + let l:string = printf('```%s' . "\n" . '%s' . "\n" . '```', l:markup_content.language, l:string) |
| 35 | + endif |
| 36 | + |
| 37 | + " just string. |
| 38 | + elseif type(l:markup_content) == type('') |
| 39 | + let l:string = l:markup_content |
| 40 | + endif |
| 41 | + |
| 42 | + call add(l:normalized, { 'lines': split(l:string, "\n") }) |
| 43 | + endfor |
| 44 | + |
| 45 | + return l:normalized |
| 46 | +endfunction |
| 47 | + |
| 48 | +let s:Floatwin = {} |
| 49 | + |
| 50 | +" |
| 51 | +" new |
| 52 | +" |
| 53 | +function! s:Floatwin.new(option) abort |
| 54 | + let s:floatwin_id += 1 |
| 55 | + let l:bufname = printf('lsp-hover-%s.lsp-hover', s:floatwin_id) |
| 56 | + let l:bufnr = bufnr(l:bufname, v:true) |
| 57 | + call setbufvar(l:bufnr, '&buflisted', 0) |
| 58 | + call setbufvar(l:bufnr, '&buftype', 'nofile') |
| 59 | + call setbufvar(l:bufnr, '&filetype', 'lsp-hover') |
| 60 | + return extend(deepcopy(s:Floatwin), { |
| 61 | + \ 'id': s:floatwin_id, |
| 62 | + \ 'bufnr': l:bufnr, |
| 63 | + \ 'max_width': get(a:option, 'max_width', &columns / 3), |
| 64 | + \ 'max_height': get(a:option, 'max_height', &lines / 2), |
| 65 | + \ 'close_on': get(a:option, 'close_on', []), |
| 66 | + \ 'screenpos': [0, 0], |
| 67 | + \ 'contents': [] |
| 68 | + \ }) |
| 69 | +endfunction |
| 70 | + |
| 71 | +" |
| 72 | +" show_tooltip |
| 73 | +" |
| 74 | +function! s:Floatwin.show_tooltip(screenpos, contents) abort |
| 75 | + let l:width = self.get_width(a:contents) |
| 76 | + let l:height = self.get_height(a:contents) |
| 77 | + |
| 78 | + let l:screenpos = copy(a:screenpos) |
| 79 | + let l:screenpos[0] -= 1 |
| 80 | + let l:screenpos[1] -= 1 |
| 81 | + |
| 82 | + " fix height. |
| 83 | + if l:screenpos[0] - l:height >= 0 |
| 84 | + let l:screenpos[0] -= l:height |
| 85 | + else |
| 86 | + let l:screenpos[0] += 1 |
| 87 | + endif |
| 88 | + |
| 89 | + " fix width. |
| 90 | + if &columns < l:screenpos[1] + l:width |
| 91 | + let l:screenpos[1] -= l:screenpos[1] + l:width - &columns |
| 92 | + endif |
| 93 | + |
| 94 | + call self.show(l:screenpos, a:contents) |
| 95 | +endfunction |
| 96 | + |
| 97 | +" |
| 98 | +" show |
| 99 | +" |
| 100 | +function! s:Floatwin.show(screenpos, contents) abort |
| 101 | + let self.screenpos = a:screenpos |
| 102 | + let self.contents = a:contents |
| 103 | + |
| 104 | + " create lines. |
| 105 | + let l:lines = [] |
| 106 | + for l:content in a:contents |
| 107 | + let l:lines += l:content.lines |
| 108 | + endfor |
| 109 | + |
| 110 | + " update bufvars. |
| 111 | + call setbufvar(self.bufnr, 'lsp_do_conceal', 1) |
| 112 | + call setbufvar(self.bufnr, 'lsp_floatwin_lines', l:lines) |
| 113 | + |
| 114 | + " show or move |
| 115 | + call lsp#ui#vim#floatwin#{s:namespace}#show(self) |
| 116 | + |
| 117 | + " write lines |
| 118 | + call lsp#ui#vim#floatwin#{s:namespace}#write(self, l:lines) |
| 119 | + |
| 120 | + call self.set_close_events() |
| 121 | +endfunction |
| 122 | + |
| 123 | +" |
| 124 | +" hide |
| 125 | +" |
| 126 | +function! s:Floatwin.hide() abort |
| 127 | + augroup printf('lsp#ui#vim#floatwin#hide_%s', self.id) |
| 128 | + autocmd! |
| 129 | + augroup END |
| 130 | + call lsp#ui#vim#floatwin#{s:namespace}#hide(self) |
| 131 | +endfunction |
| 132 | + |
| 133 | +" |
| 134 | +" enter |
| 135 | +" |
| 136 | +function! s:Floatwin.enter() abort |
| 137 | + call lsp#ui#vim#floatwin#{s:namespace}#enter(self) |
| 138 | +endfunction |
| 139 | + |
| 140 | +" |
| 141 | +" is_showing |
| 142 | +" |
| 143 | +function! s:Floatwin.is_showing() abort |
| 144 | + return lsp#ui#vim#floatwin#{s:namespace}#is_showing(self) |
| 145 | +endfunction |
| 146 | + |
| 147 | +" |
| 148 | +" winid |
| 149 | +" |
| 150 | +function! s:Floatwin.winid() abort |
| 151 | + return lsp#ui#vim#floatwin#{s:namespace}#winid(self) |
| 152 | +endfunction |
| 153 | + |
| 154 | +" |
| 155 | +" set_close_events |
| 156 | +" |
| 157 | +function! s:Floatwin.set_close_events() abort |
| 158 | + let l:close_fn = printf('lsp_floatwin_close_%s', self.id) |
| 159 | + let b:[l:close_fn] = { -> self.hide() } |
| 160 | + |
| 161 | + augroup printf('lsp#ui#vim#floatwin#hide_%s', self.id) |
| 162 | + autocmd! |
| 163 | + for l:event in self.close_on |
| 164 | + execute printf('autocmd %s <buffer> call b:%s()', l:event, l:close_fn) |
| 165 | + endfor |
| 166 | + augroup END |
| 167 | +endfunction |
| 168 | + |
| 169 | +" |
| 170 | +" get_width |
| 171 | +" |
| 172 | +function! s:Floatwin.get_width(contents) abort |
| 173 | + let l:width = 0 |
| 174 | + for l:content in a:contents |
| 175 | + let l:width = max([l:width] + map(copy(l:content.lines), { k, v -> strdisplaywidth(v) })) |
| 176 | + endfor |
| 177 | + |
| 178 | + if self.max_width != -1 |
| 179 | + return max([min([self.max_width, l:width]), 1]) |
| 180 | + endif |
| 181 | + return max([l:width, 1]) |
| 182 | +endfunction |
| 183 | + |
| 184 | +" |
| 185 | +" get_height |
| 186 | +" |
| 187 | +function! s:Floatwin.get_height(contents) abort |
| 188 | + let l:width = self.get_width(a:contents) |
| 189 | + |
| 190 | + let l:height = len(a:contents) - 1 |
| 191 | + for l:content in a:contents |
| 192 | + for l:line in l:content.lines |
| 193 | + let l:height += max([1, float2nr(ceil(strdisplaywidth(l:line) / str2float('' . l:width)))]) |
| 194 | + endfor |
| 195 | + endfor |
| 196 | + |
| 197 | + if self.max_height != -1 |
| 198 | + return max([min([self.max_height, l:height]), 1]) |
| 199 | + endif |
| 200 | + return max([l:height, 1]) |
| 201 | +endfunction |
| 202 | + |
0 commit comments