├── doc └── vim-lsp-settings-adapter.txt ├── README.md ├── LICENSE └── autoload ├── lsp.vim └── lsp └── utils.vim /doc/vim-lsp-settings-adapter.txt: -------------------------------------------------------------------------------- 1 | *vim-lsp-settings-adapter.txt* Use mattn/vim-lsp-settings with yegappan/lsp 2 | 3 | VIM LSP SETTINGS ADAPTER 4 | 5 | Author: Normen Hansen 6 | License: MIT 7 | 8 | INTRODUCTION *vim-lsp-settings-adapter* 9 | 10 | This plugin allows using mattn/|vim-lsp-settings| with yegappan/|lsp| to easily 11 | install and manage LSP servers for Vim 9. 12 | 13 | CAVEATS 14 | 15 | Some servers don't seem to work that well, the workspace info isn't currently 16 | transferred, not sure if that would be needed. 17 | 18 | ABOUT *vim-lsp-settings-adapter-about* 19 | 20 | Grab the latest version or report a bug on GitHub: 21 | 22 | https://github.com/normen/vim-lsp-settings-adapter 23 | 24 | vim:tw=78:et:ft=help:norl: 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## vim-lsp-settings-adapter 2 | 3 | #### What? 4 | This vim plugin allows using [mattn/vim-lsp-settings](https://github.com/mattn/vim-lsp-settings) with [yegappan/lsp](https://github.com/yegappan/lsp) to easily install and manage LSP servers. 5 | 6 | Its basically emulating the parts of `vim-lsp` that are required by `vim-lsp-settings` and sending the commands to `lsp` instead. 7 | 8 | #### How? 9 | To use it, simply install all three plugins together. E.g. using `Plug`, add this to your `vimrc` Plug section: 10 | 11 | ``` 12 | Plug 'yegappan/lsp' 13 | Plug 'mattn/vim-lsp-settings' 14 | Plug 'normen/vim-lsp-settings-adapter' 15 | ``` 16 | 17 | Now you can install and run the language server for the current file type by running `:LspInstallServer` - easy as that. 18 | 19 | #### Caveats 20 | Quick and dirty hack, some servers don't work that well, some status features of `vim-lsp-settings` don't work. 21 | 22 | Confirmed to work: 23 | - c/cpp (clang/ccls) 24 | - typescript/javascript (typescript-language-server) 25 | - golang (gopls) 26 | - rust (rust-analyzer) 27 | - vimscript (vim-language-server) 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Normen Hansen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /autoload/lsp.vim: -------------------------------------------------------------------------------- 1 | let g:lsp_log_file = '' 2 | let g:lsp_log_verbose = 0 3 | 4 | " makes vim-lsp-settings work with lsp(vim9) plugin 5 | function! lsp#register_server(server_info) abort 6 | "let l:server_name = a:server_info['name'] 7 | "let l:project_root = a:server_info['root_uri']("abc") 8 | if type(a:server_info["initialization_options"]) == v:t_dict && has_key(a:server_info, 'initialization_options') 9 | let lspServers = [ 10 | \ #{ 11 | \ filetype: a:server_info["allowlist"], 12 | \ path: a:server_info["cmd"]("")[0], 13 | \ args: a:server_info["cmd"]("")[1:], 14 | \ initializationOptions: a:server_info["initialization_options"], 15 | \ } 16 | \ ] 17 | call lsp#lsp#AddServer(lspServers) 18 | else 19 | let lspServers = [ 20 | \ #{ 21 | \ filetype: a:server_info["allowlist"], 22 | \ path: a:server_info["cmd"]("")[0], 23 | \ args: a:server_info["cmd"]("")[1:], 24 | \ } 25 | \ ] 26 | call lsp#lsp#AddServer(lspServers) 27 | endif 28 | endfunction 29 | 30 | function! lsp#register_command(command_name, callback) abort 31 | endfunction 32 | 33 | function! lsp#register_notifications(name, callback) abort 34 | endfunction 35 | 36 | function! lsp#unregister_notifications(name) abort 37 | endfunction 38 | 39 | function! lsp#stop_server(server_name) abort 40 | endfunction 41 | 42 | function! lsp#get_allowed_servers(...) abort 43 | return [] 44 | endfunction 45 | 46 | function! lsp#log_verbose(...) abort 47 | if g:lsp_log_verbose 48 | call call(function('lsp#log'), a:000) 49 | endif 50 | endfunction 51 | 52 | function! lsp#log(...) abort 53 | if !empty(g:lsp_log_file) 54 | call writefile([strftime('%c') . ':' . json_encode(a:000)], g:lsp_log_file, 'a') 55 | endif 56 | endfunction 57 | 58 | function! lsp#ensure_flush_all(buf, server_names) abort 59 | endfunction 60 | 61 | " Returns the current status of all servers (if called with no arguments) or 62 | " the given server (if given an argument). Can be one of "unknown server", 63 | " "exited", "starting", "failed", "running", "not running" 64 | function! lsp#get_server_status(...) abort 65 | return "unknown server" 66 | endfunction 67 | -------------------------------------------------------------------------------- /autoload/lsp/utils.vim: -------------------------------------------------------------------------------- 1 | "The MIT License (MIT) 2 | " 3 | "Copyright (c) 2019 Yasuhiro Matsumoto 4 | " 5 | "Permission is hereby granted, free of charge, to any person obtaining a copy 6 | "of this software and associated documentation files (the "Software"), to deal 7 | "in the Software without restriction, including without limitation the rights 8 | "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | "copies of the Software, and to permit persons to whom the Software is 10 | "furnished to do so, subject to the following conditions: 11 | " 12 | "The above copyright notice and this permission notice shall be included in 13 | "all 14 | "copies or substantial portions of the Software. 15 | " 16 | "THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | "SOFTWARE. 23 | 24 | let s:has_lua = has('nvim-0.4.0') || (has('lua') && has('patch-8.2.0775')) 25 | function! lsp#utils#has_lua() abort 26 | return s:has_lua 27 | endfunction 28 | 29 | let s:has_virtual_text = exists('*nvim_buf_set_virtual_text') && exists('*nvim_create_namespace') 30 | function! lsp#utils#_has_nvim_virtual_text() abort 31 | return s:has_virtual_text 32 | endfunction 33 | 34 | let s:has_signs = exists('*sign_define') && (has('nvim') || has('patch-8.1.0772')) 35 | function! lsp#utils#_has_signs() abort 36 | return s:has_signs 37 | endfunction 38 | 39 | let s:has_nvim_buf_highlight = exists('*nvim_buf_add_highlight') && has('nvim') 40 | function! lsp#utils#_has_nvim_buf_highlight() abort 41 | return s:has_nvim_buf_highlight 42 | endfunction 43 | 44 | " https://github.com/prabirshrestha/vim-lsp/issues/399#issuecomment-500585549 45 | let s:has_textprops = exists('*prop_add') && has('patch-8.1.1035') 46 | function! lsp#utils#_has_textprops() abort 47 | return s:has_textprops 48 | endfunction 49 | 50 | let s:has_vim9textprops = exists('*prop_add') && has('patch-9.0.0178') 51 | function! lsp#utils#_has_vim_virtual_text() abort 52 | return s:has_vim9textprops 53 | endfunction 54 | 55 | let s:has_higlights = has('nvim') ? lsp#utils#_has_nvim_buf_highlight() : lsp#utils#_has_textprops() 56 | function! lsp#utils#_has_highlights() abort 57 | return s:has_higlights 58 | endfunction 59 | 60 | let s:has_popup_menu = exists('*popup_menu') 61 | function! lsp#utils#_has_popup_menu() abort 62 | return s:has_popup_menu 63 | endfunction 64 | 65 | function! lsp#utils#is_file_uri(uri) abort 66 | return stridx(a:uri, 'file:///') == 0 67 | endfunction 68 | 69 | function! lsp#utils#is_remote_uri(uri) abort 70 | return a:uri =~# '^\w\+::' || a:uri =~# '^[a-z][a-z0-9+.-]*://' 71 | endfunction 72 | 73 | function! s:decode_uri(uri) abort 74 | let l:ret = substitute(a:uri, '[?#].*', '', '') 75 | return substitute(l:ret, '%\(\x\x\)', '\=printf("%c", str2nr(submatch(1), 16))', 'g') 76 | endfunction 77 | 78 | function! s:urlencode_char(c) abort 79 | return printf('%%%02X', char2nr(a:c)) 80 | endfunction 81 | 82 | function! s:get_prefix(path) abort 83 | return matchstr(a:path, '\(^\w\+::\|^\w\+://\)') 84 | endfunction 85 | 86 | function! s:encode_uri(path, start_pos_encode, default_prefix) abort 87 | let l:prefix = s:get_prefix(a:path) 88 | let l:path = a:path[len(l:prefix):] 89 | if len(l:prefix) == 0 90 | let l:prefix = a:default_prefix 91 | endif 92 | 93 | let l:result = strpart(a:path, 0, a:start_pos_encode) 94 | 95 | for l:i in range(a:start_pos_encode, len(l:path) - 1) 96 | " Don't encode '/' here, `path` is expected to be a valid path. 97 | if l:path[l:i] =~# '^[a-zA-Z0-9_.~/-]$' 98 | let l:result .= l:path[l:i] 99 | else 100 | let l:result .= s:urlencode_char(l:path[l:i]) 101 | endif 102 | endfor 103 | 104 | return l:prefix . l:result 105 | endfunction 106 | 107 | let s:path_to_uri_cache = {} 108 | if has('win32') || has('win64') || has('win32unix') 109 | function! lsp#utils#path_to_uri(path) abort 110 | if has_key(s:path_to_uri_cache, a:path) 111 | return s:path_to_uri_cache[a:path] 112 | endif 113 | 114 | if empty(a:path) || lsp#utils#is_remote_uri(a:path) 115 | let s:path_to_uri_cache[a:path] = a:path 116 | return s:path_to_uri_cache[a:path] 117 | else 118 | " Transform cygwin paths to windows paths 119 | let l:path = a:path 120 | if has('win32unix') 121 | let l:path = substitute(a:path, '\c^/\([a-z]\)/', '\U\1:/', '') 122 | endif 123 | 124 | " You must not encode the volume information on the path if 125 | " present 126 | let l:end_pos_volume = matchstrpos(l:path, '\c[A-Z]:')[2] 127 | 128 | if l:end_pos_volume == -1 129 | let l:end_pos_volume = 0 130 | endif 131 | 132 | let s:path_to_uri_cache[l:path] = s:encode_uri(substitute(l:path, '\', '/', 'g'), l:end_pos_volume, 'file:///') 133 | return s:path_to_uri_cache[l:path] 134 | endif 135 | endfunction 136 | else 137 | function! lsp#utils#path_to_uri(path) abort 138 | if has_key(s:path_to_uri_cache, a:path) 139 | return s:path_to_uri_cache[a:path] 140 | endif 141 | 142 | if empty(a:path) || lsp#utils#is_remote_uri(a:path) 143 | let s:path_to_uri_cache[a:path] = a:path 144 | return s:path_to_uri_cache[a:path] 145 | else 146 | let s:path_to_uri_cache[a:path] = s:encode_uri(a:path, 0, 'file://') 147 | return s:path_to_uri_cache[a:path] 148 | endif 149 | endfunction 150 | endif 151 | 152 | let s:uri_to_path_cache = {} 153 | if has('win32') || has('win64') || has('win32unix') 154 | function! lsp#utils#uri_to_path(uri) abort 155 | if has_key(s:uri_to_path_cache, a:uri) 156 | return s:uri_to_path_cache[a:uri] 157 | endif 158 | 159 | let l:path = substitute(s:decode_uri(a:uri[len('file:///'):]), '/', '\\', 'g') 160 | 161 | " Transform windows paths to cygwin paths 162 | if has('win32unix') 163 | let l:path = substitute(l:path, '\c^\([A-Z]\):\\', '/\l\1/', '') 164 | let l:path = substitute(l:path, '\\', '/', 'g') 165 | endif 166 | 167 | let s:uri_to_path_cache[a:uri] = l:path 168 | return s:uri_to_path_cache[a:uri] 169 | endfunction 170 | else 171 | function! lsp#utils#uri_to_path(uri) abort 172 | if has_key(s:uri_to_path_cache, a:uri) 173 | return s:uri_to_path_cache[a:uri] 174 | endif 175 | 176 | let s:uri_to_path_cache[a:uri] = s:decode_uri(a:uri[len('file://'):]) 177 | return s:uri_to_path_cache[a:uri] 178 | endfunction 179 | endif 180 | 181 | if has('win32') || has('win64') 182 | function! lsp#utils#normalize_uri(uri) abort 183 | " Refer to https://github.com/microsoft/language-server-protocol/pull/1019 on normalization of urls. 184 | " TODO: after the discussion is settled, modify this function. 185 | let l:ret = substitute(a:uri, '^file:///[a-zA-Z]\zs%3[aA]', ':', '') 186 | return substitute(l:ret, '^file:///\zs\([A-Z]\)', "\\=tolower(submatch(1))", '') 187 | endfunction 188 | else 189 | function! lsp#utils#normalize_uri(uri) abort 190 | return a:uri 191 | endfunction 192 | endif 193 | 194 | function! lsp#utils#get_default_root_uri() abort 195 | return lsp#utils#path_to_uri(getcwd()) 196 | endfunction 197 | 198 | function! lsp#utils#get_buffer_path(...) abort 199 | return expand((a:0 > 0 ? '#' . a:1 : '%') . ':p') 200 | endfunction 201 | 202 | function! lsp#utils#get_buffer_uri(...) abort 203 | let l:name = a:0 > 0 ? bufname(a:1) : expand('%') 204 | if empty(l:name) 205 | let l:nr = a:0 > 0 ? a:1 : bufnr('%') 206 | let l:name = printf('%s/__NO_NAME_%d__', getcwd(), l:nr) 207 | endif 208 | return lsp#utils#path_to_uri(fnamemodify(l:name, ':p')) 209 | endfunction 210 | 211 | " Find a nearest to a `path` parent directory `directoryname` by traversing the filesystem upwards 212 | function! lsp#utils#find_nearest_parent_directory(path, directoryname) abort 213 | let l:relative_path = finddir(a:directoryname, a:path . ';') 214 | 215 | if !empty(l:relative_path) 216 | return fnamemodify(l:relative_path, ':p') 217 | else 218 | return '' 219 | endif 220 | endfunction 221 | 222 | " Find a nearest to a `path` parent filename `filename` by traversing the filesystem upwards 223 | function! lsp#utils#find_nearest_parent_file(path, filename) abort 224 | let l:relative_path = findfile(a:filename, a:path . ';') 225 | 226 | if !empty(l:relative_path) 227 | return fnamemodify(l:relative_path, ':p') 228 | else 229 | return '' 230 | endif 231 | endfunction 232 | 233 | function! lsp#utils#_compare_nearest_path(matches, lhs, rhs) abort 234 | let l:llhs = len(a:lhs) 235 | let l:lrhs = len(a:rhs) 236 | if l:llhs ># l:lrhs 237 | return -1 238 | elseif l:llhs <# l:lrhs 239 | return 1 240 | endif 241 | if a:matches[a:lhs] ># a:matches[a:rhs] 242 | return -1 243 | elseif a:matches[a:lhs] <# a:matches[a:rhs] 244 | return 1 245 | endif 246 | return 0 247 | endfunction 248 | 249 | function! lsp#utils#_nearest_path(matches) abort 250 | return empty(a:matches) ? 251 | \ '' : 252 | \ sort(keys(a:matches), function('lsp#utils#_compare_nearest_path', [a:matches]))[0] 253 | endfunction 254 | 255 | " Find a nearest to a `path` parent filename `filename` by traversing the filesystem upwards 256 | " The filename ending with '/' or '\' will be regarded as directory name, 257 | " otherwith as file name 258 | function! lsp#utils#find_nearest_parent_file_directory(path, filename) abort 259 | if type(a:filename) == 3 260 | let l:matched_paths = {} 261 | for l:current_name in a:filename 262 | let l:path = lsp#utils#find_nearest_parent_file_directory(a:path, l:current_name) 263 | 264 | if !empty(l:path) 265 | if has_key(l:matched_paths, l:path) 266 | let l:matched_paths[l:path] += 1 267 | else 268 | let l:matched_paths[l:path] = 1 269 | endif 270 | endif 271 | endfor 272 | 273 | return lsp#utils#_nearest_path(l:matched_paths) 274 | elseif type(a:filename) == 1 275 | if a:filename[-1:] ==# '/' || a:filename[-1:] ==# '\' 276 | let l:modify_str = ':p:h:h' 277 | let l:path = lsp#utils#find_nearest_parent_directory(a:path, a:filename[:-2]) 278 | else 279 | let l:modify_str = ':p:h' 280 | let l:path = lsp#utils#find_nearest_parent_file(a:path, a:filename) 281 | endif 282 | 283 | return empty(l:path) ? '' : fnamemodify(l:path, l:modify_str) 284 | else 285 | echoerr "The type of argument \"filename\" must be String or List" 286 | endif 287 | endfunction 288 | 289 | if exists('*matchstrpos') 290 | function! lsp#utils#matchstrpos(expr, pattern) abort 291 | return matchstrpos(a:expr, a:pattern) 292 | endfunction 293 | else 294 | function! lsp#utils#matchstrpos(expr, pattern) abort 295 | return [matchstr(a:expr, a:pattern), match(a:expr, a:pattern), matchend(a:expr, a:pattern)] 296 | endfunction 297 | endif 298 | 299 | function! lsp#utils#empty_complete(...) abort 300 | return [] 301 | endfunction 302 | 303 | function! lsp#utils#error(msg) abort 304 | echohl ErrorMsg 305 | echom a:msg 306 | echohl NONE 307 | endfunction 308 | 309 | function! lsp#utils#echo_with_truncation(msg) abort 310 | let l:msg = a:msg 311 | 312 | if &laststatus == 0 || (&laststatus == 1 && tabpagewinnr(tabpagenr(), '$') == 1) 313 | let l:winwidth = winwidth(0) 314 | 315 | if &ruler 316 | let l:winwidth -= 18 317 | endif 318 | else 319 | let l:winwidth = &columns 320 | endif 321 | 322 | if &showcmd 323 | let l:winwidth -= 12 324 | endif 325 | 326 | if l:winwidth > 5 && l:winwidth < strdisplaywidth(l:msg) 327 | let l:msg = l:msg[:l:winwidth - 5] . '...' 328 | endif 329 | 330 | exec 'echo l:msg' 331 | endfunction 332 | 333 | " Convert a byte-index (1-based) to a character-index (0-based) 334 | " This function requires a buffer specifier (expr, see :help bufname()), 335 | " a line number (lnum, 1-based), and a byte-index (char, 1-based). 336 | function! lsp#utils#to_char(expr, lnum, col) abort 337 | let l:lines = getbufline(a:expr, a:lnum) 338 | if l:lines == [] 339 | if type(a:expr) != v:t_string || !filereadable(a:expr) 340 | " invalid a:expr 341 | return a:col - 1 342 | endif 343 | " a:expr is a file that is not yet loaded as a buffer 344 | let l:lines = readfile(a:expr, '', a:lnum) 345 | endif 346 | let l:linestr = l:lines[-1] 347 | return strchars(strpart(l:linestr, 0, a:col - 1)) 348 | endfunction 349 | 350 | function! s:get_base64_alphabet() abort 351 | let l:alphabet = [] 352 | 353 | " Uppercase letters 354 | for l:c in range(char2nr('A'), char2nr('Z')) 355 | call add(l:alphabet, nr2char(l:c)) 356 | endfor 357 | 358 | " Lowercase letters 359 | for l:c in range(char2nr('a'), char2nr('z')) 360 | call add(l:alphabet, nr2char(l:c)) 361 | endfor 362 | 363 | " Numbers 364 | for l:c in range(char2nr('0'), char2nr('9')) 365 | call add(l:alphabet, nr2char(l:c)) 366 | endfor 367 | 368 | " Symbols 369 | call add(l:alphabet, '+') 370 | call add(l:alphabet, '/') 371 | 372 | return l:alphabet 373 | endfunction 374 | 375 | if exists('*trim') 376 | function! lsp#utils#_trim(string) abort 377 | return trim(a:string) 378 | endfunction 379 | else 380 | function! lsp#utils#_trim(string) abort 381 | return substitute(a:string, '^\s*\|\s*$', '', 'g') 382 | endfunction 383 | endif 384 | 385 | function! lsp#utils#_get_before_line() abort 386 | let l:text = getline('.') 387 | let l:idx = min([strlen(l:text), col('.') - 2]) 388 | let l:idx = max([l:idx, -1]) 389 | if l:idx == -1 390 | return '' 391 | endif 392 | return l:text[0 : l:idx] 393 | endfunction 394 | 395 | function! lsp#utils#_get_before_char_skip_white() abort 396 | let l:current_lnum = line('.') 397 | 398 | let l:lnum = l:current_lnum 399 | while l:lnum > 0 400 | if l:lnum == l:current_lnum 401 | let l:text = lsp#utils#_get_before_line() 402 | else 403 | let l:text = getline(l:lnum) 404 | endif 405 | let l:match = matchlist(l:text, '\([^[:blank:]]\)\s*$') 406 | if get(l:match, 1, v:null) isnot v:null 407 | return l:match[1] 408 | endif 409 | let l:lnum -= 1 410 | endwhile 411 | 412 | return '' 413 | endfunction 414 | 415 | let s:alphabet = s:get_base64_alphabet() 416 | 417 | function! lsp#utils#base64_decode(data) abort 418 | let l:ret = [] 419 | 420 | " Process base64 string in chunks of 4 chars 421 | for l:group in split(a:data, '.\{4}\zs') 422 | let l:group_dec = 0 423 | 424 | " Convert 4 chars to 3 octets 425 | for l:char in split(l:group, '\zs') 426 | let l:group_dec = l:group_dec * 64 427 | let l:group_dec += max([index(s:alphabet, l:char), 0]) 428 | endfor 429 | 430 | " Split the number representing the 3 octets into the individual 431 | " octets 432 | let l:octets = [] 433 | let l:i = 0 434 | while l:i < 3 435 | call add(l:octets, l:group_dec % 256) 436 | let l:group_dec = l:group_dec / 256 437 | let l:i += 1 438 | endwhile 439 | 440 | call extend(l:ret, reverse(l:octets)) 441 | endfor 442 | 443 | " Handle padding 444 | if len(a:data) >= 2 445 | if strpart(a:data, len(a:data) - 2) ==# '==' 446 | call remove(l:ret, -2, -1) 447 | elseif strpart(a:data, len(a:data) - 1) ==# '=' 448 | call remove(l:ret, -1, -1) 449 | endif 450 | endif 451 | 452 | return l:ret 453 | endfunction 454 | 455 | function! lsp#utils#make_valid_word(str) abort 456 | let l:str = substitute(a:str, '\$[0-9]\+\|\${\%(\\.\|[^}]\)\+}', '', 'g') 457 | let l:str = substitute(l:str, '\\\(.\)', '\1', 'g') 458 | let l:valid = matchstr(l:str, '^[^"'' (<{\[\t\r\n]\+') 459 | if empty(l:valid) 460 | return l:str 461 | endif 462 | if l:valid =~# ':$' 463 | return l:valid[:-2] 464 | endif 465 | return l:valid 466 | endfunction 467 | 468 | function! lsp#utils#_split_by_eol(text) abort 469 | return split(a:text, '\r\n\|\r\|\n', v:true) 470 | endfunction 471 | 472 | " parse command options like "-key" or "-key=value" 473 | function! lsp#utils#parse_command_options(params) abort 474 | let l:result = {} 475 | for l:param in a:params 476 | let l:match = matchlist(l:param, '-\{1,2}\zs\([^=]*\)\(=\(.*\)\)\?\m') 477 | let l:result[l:match[1]] = l:match[3] 478 | endfor 479 | return l:result 480 | endfunction 481 | 482 | function! lsp#utils#is_large_window(winid) abort 483 | let l:buffer_size = line2byte(line('$', a:winid)) 484 | return g:lsp_max_buffer_size >= 0 && l:buffer_size >= g:lsp_max_buffer_size 485 | endfunction 486 | 487 | " polyfill for the neovim wait function 488 | if exists('*wait') 489 | function! lsp#utils#_wait(timeout, condition, ...) abort 490 | if type(a:timeout) != type(0) 491 | return -3 492 | endif 493 | if type(get(a:000, 0, 0)) != type(0) 494 | return -3 495 | endif 496 | while 1 497 | let l:result=call('wait', extend([a:timeout, a:condition], a:000)) 498 | if l:result != -3 " ignore spurious errors 499 | return l:result 500 | endif 501 | endwhile 502 | endfunction 503 | else 504 | function! lsp#utils#_wait(timeout, condition, ...) abort 505 | try 506 | let l:timeout = a:timeout / 1000.0 507 | let l:interval = get(a:000, 0, 200) 508 | let l:Condition = a:condition 509 | if type(l:Condition) != type(function('eval')) 510 | let l:Condition = function('eval', l:Condition) 511 | endif 512 | let l:start = reltime() 513 | while l:timeout < 0 || reltimefloat(reltime(l:start)) < l:timeout 514 | if l:Condition() 515 | return 0 516 | endif 517 | 518 | execute 'sleep ' . l:interval . 'm' 519 | endwhile 520 | return -1 521 | catch /^Vim:Interrupt$/ 522 | return -2 523 | endtry 524 | endfunction 525 | endif 526 | 527 | function! lsp#utils#iteratable(list) abort 528 | return type(a:list) !=# v:t_list ? [] : a:list 529 | endfunction 530 | --------------------------------------------------------------------------------