mirror of
https://github.com/mattn/vim-lsp-settings.git
synced 2025-12-12 20:35:54 +01:00
If server is found, but dependencies are no found, then currently "Server not found" is shown, making user clueless. This commit tries to inform the user that server was found, but dependencies were not satisfied.
560 lines
16 KiB
VimL
560 lines
16 KiB
VimL
let s:settings_dir = expand('<sfile>:h:h') . '/settings'
|
|
let s:checkers_dir = expand('<sfile>:h:h') . '/checkers'
|
|
let s:installer_dir = expand('<sfile>:h:h') . '/installer'
|
|
let s:root_dir = expand('<sfile>:h:h')
|
|
|
|
if has('win32')
|
|
let s:data_dir = expand('$LOCALAPPDATA/vim-lsp-settings')
|
|
elseif $XDG_DATA_HOME !=# ''
|
|
let s:data_dir = expand('$XDG_DATA_HOME/vim-lsp-settings')
|
|
else
|
|
let s:data_dir = expand('~/.local/share/vim-lsp-settings')
|
|
endif
|
|
let s:servers_dir = s:data_dir . '/servers'
|
|
|
|
let s:settings = json_decode(join(readfile(expand('<sfile>:h:h') . '/settings.json'), "\n"))
|
|
call remove(s:settings, '$schema')
|
|
|
|
let s:ftmap = {}
|
|
|
|
function! lsp_settings#installer_dir() abort
|
|
return s:installer_dir
|
|
endfunction
|
|
|
|
function! lsp_settings#servers_dir() abort
|
|
let l:path = fnamemodify(get(g:, 'lsp_settings_servers_dir', s:servers_dir), ':p')
|
|
if has('win32')
|
|
let l:path = substitute(l:path, '/', '\', 'g')
|
|
endif
|
|
return substitute(l:path, '[\/]$', '', '')
|
|
endfunction
|
|
|
|
function! lsp_settings#global_settings_dir() abort
|
|
let l:path = fnamemodify(get(g:, 'lsp_settings_global_settings_dir', s:data_dir), ':p')
|
|
if has('win32')
|
|
let l:path = substitute(l:path, '/', '\', 'g')
|
|
endif
|
|
return substitute(l:path, '[\/]$', '', '')
|
|
endfunction
|
|
|
|
function! lsp_settings#intalled_servers() abort
|
|
let l:servers = []
|
|
for l:ft in sort(keys(s:settings))
|
|
for l:conf in s:settings[l:ft]
|
|
let l:path = lsp_settings#servers_dir() . '/' . l:conf.command . '/' . l:conf.command
|
|
if !executable(l:path)
|
|
continue
|
|
endif
|
|
let l:path = lsp_settings#servers_dir() . '/' . l:conf.command . '/.vim-lsp-settings-version'
|
|
let l:version = ''
|
|
if filereadable(l:path)
|
|
let l:version = trim(join(readfile(l:path), "\n"))
|
|
endif
|
|
call add(l:servers, {'name': l:conf.command, 'version': l:version})
|
|
endfor
|
|
endfor
|
|
return l:servers
|
|
endfunction
|
|
|
|
function! lsp_settings#executable(cmd) abort
|
|
if executable(a:cmd)
|
|
return 1
|
|
endif
|
|
let l:paths = get(g:, 'lsp_settings_extra_paths', '')
|
|
if type(l:paths) == type([])
|
|
let l:paths = join(l:paths, ',')
|
|
endif
|
|
let l:paths .= ',' . lsp_settings#servers_dir() . '/' . a:cmd
|
|
if !has('win32')
|
|
let l:found = globpath(l:paths, a:cmd, 1)
|
|
return !empty(l:found)
|
|
endif
|
|
for l:ext in ['.exe', '.cmd', '.bat']
|
|
if !empty(globpath(l:paths, a:cmd . l:ext, 1))
|
|
return 1
|
|
endif
|
|
endfor
|
|
return 0
|
|
endfunction
|
|
|
|
function! s:vim_lsp_installer(ft, ...) abort
|
|
let l:ft = tolower(get(split(a:ft, '\.'), 0, ''))
|
|
let l:ft = empty(l:ft) ? '_' : l:ft
|
|
if !has_key(s:settings, l:ft)
|
|
return []
|
|
endif
|
|
let l:server = s:settings[l:ft]
|
|
if empty(l:server)
|
|
return []
|
|
endif
|
|
if l:ft !=# '_'
|
|
let l:server += s:settings['_']
|
|
endif
|
|
|
|
let l:name = get(a:000, 0, '')
|
|
for l:conf in l:server
|
|
if !empty(l:name) && l:conf.command != l:name
|
|
continue
|
|
endif
|
|
let l:command = printf('%s/install-%s', s:installer_dir, l:conf.command)
|
|
if has('win32')
|
|
let l:command = substitute(l:command, '/', '\', 'g') . '.cmd'
|
|
else
|
|
let l:command = l:command . '.sh'
|
|
endif
|
|
let l:missing = 0
|
|
for l:require in l:conf.requires
|
|
if !lsp_settings#executable(l:require)
|
|
call lsp_settings#utils#warning(l:conf.command . ' requires ' . l:require)
|
|
let l:missing = 1
|
|
break
|
|
endif
|
|
endfor
|
|
if l:missing !=# 0
|
|
continue
|
|
endif
|
|
if lsp_settings#executable(l:command)
|
|
return [l:conf.command, l:command]
|
|
endif
|
|
endfor
|
|
return [v:false] " placeholder, so that empty() returns false, but len() < 2 returns true
|
|
endfunction
|
|
|
|
function! lsp_settings#server_config(name) abort
|
|
for l:ft in sort(keys(s:settings))
|
|
for l:conf in s:settings[l:ft]
|
|
if l:conf.command ==# a:name && has_key(l:conf, 'config')
|
|
return l:conf['config']
|
|
endif
|
|
endfor
|
|
endfor
|
|
return {}
|
|
endfunction
|
|
|
|
function! lsp_settings#get(name, key, default) abort
|
|
let l:config = get(g:, 'lsp_settings', {})
|
|
if !has_key(l:config, a:name)
|
|
if !has_key(l:config, '*')
|
|
if type(a:default) ==# v:t_func
|
|
return a:default(a:name, a:key)
|
|
endif
|
|
return a:default
|
|
endif
|
|
let l:config = l:config['*']
|
|
else
|
|
let l:config = l:config[a:name]
|
|
endif
|
|
if !has_key(l:config, a:key)
|
|
if type(a:default) ==# v:t_func
|
|
return a:default(a:name, a:key)
|
|
endif
|
|
return a:default
|
|
endif
|
|
if type(l:config[a:key]) == v:t_func
|
|
return l:config[a:key](a:name, a:key)
|
|
endif
|
|
return l:config[a:key]
|
|
endfunction
|
|
|
|
function! lsp_settings#exec_path(cmd) abort
|
|
let l:paths = []
|
|
if has('win32')
|
|
for l:path in split($PATH, ';')
|
|
if l:path[len(l:path) - 1:] == "\\"
|
|
call add(l:paths, l:path[:len(l:path)-2])
|
|
elseif !empty(l:path)
|
|
call add(l:paths, l:path)
|
|
endif
|
|
endfor
|
|
else
|
|
let l:paths = split($PATH, ':')
|
|
endif
|
|
let l:paths = join(l:paths, ',')
|
|
let l:path = globpath(l:paths, a:cmd, 1)
|
|
if !has('win32')
|
|
if !empty(l:path)
|
|
return lsp_settings#utils#first_one(l:path)
|
|
endif
|
|
else
|
|
for l:ext in ['.exe', '.cmd', '.bat']
|
|
let l:path = globpath(l:paths, a:cmd . l:ext, 1)
|
|
if !empty(l:path)
|
|
return lsp_settings#utils#first_one(l:path)
|
|
endif
|
|
endfor
|
|
endif
|
|
|
|
let l:paths = get(g:, 'lsp_settings_extra_paths', '')
|
|
if type(l:paths) == type([])
|
|
let l:paths = join(l:paths, ',') . ','
|
|
endif
|
|
let l:paths .= lsp_settings#servers_dir() . '/' . a:cmd
|
|
if !has('win32')
|
|
return lsp_settings#utils#first_one(globpath(l:paths, a:cmd, 1))
|
|
endif
|
|
for l:ext in ['.exe', '.cmd', '.bat']
|
|
let l:path = globpath(l:paths, a:cmd . l:ext, 1)
|
|
if !empty(l:path)
|
|
return lsp_settings#utils#first_one(l:path)
|
|
endif
|
|
endfor
|
|
return ''
|
|
endfunction
|
|
|
|
function! lsp_settings#root_path(patterns) abort
|
|
let l:patterns = extend(copy(a:patterns), g:lsp_settings_root_markers)
|
|
return lsp#utils#find_nearest_parent_file_directory(lsp#utils#get_buffer_path(), l:patterns)
|
|
endfunction
|
|
|
|
function! lsp_settings#root_uri(name) abort
|
|
let l:patterns = lsp_settings#get(a:name, 'root_uri_patterns', [])
|
|
if empty(l:patterns)
|
|
for l:ft in sort(keys(s:settings))
|
|
for l:conf in s:settings[l:ft]
|
|
if l:conf.command ==# a:name && has_key(l:conf, 'root_uri_patterns')
|
|
let l:patterns = l:conf['root_uri_patterns']
|
|
break
|
|
endif
|
|
endfor
|
|
endfor
|
|
endif
|
|
|
|
let l:dir = lsp#utils#find_nearest_parent_file_directory(lsp#utils#get_buffer_path(), extend(copy(l:patterns), g:lsp_settings_root_markers))
|
|
if empty(l:dir)
|
|
return lsp#utils#get_default_root_uri()
|
|
endif
|
|
return lsp#utils#path_to_uri(l:dir)
|
|
endfunction
|
|
|
|
function! lsp_settings#autocd(server_info) abort
|
|
if !has_key(a:server_info, 'root_uri')
|
|
return
|
|
endif
|
|
if type(a:server_info['root_uri']) ==# v:t_func
|
|
let l:root_uri = a:server_info['root_uri'](a:server_info)
|
|
else
|
|
let l:root_uri = a:server_info['root_uri']
|
|
endif
|
|
let l:path = lsp#utils#uri_to_path(l:root_uri)
|
|
if isdirectory(l:path)
|
|
exe 'cd' l:path
|
|
endif
|
|
endfunction
|
|
|
|
function! lsp_settings#settings() abort
|
|
return s:settings
|
|
endfunction
|
|
|
|
function! lsp_settings#complete_uninstall(arglead, cmdline, cursorpos) abort
|
|
let l:installers = []
|
|
for l:ft in sort(keys(s:settings))
|
|
for l:conf in s:settings[l:ft]
|
|
if !isdirectory(lsp_settings#servers_dir() . '/' . l:conf.command)
|
|
continue
|
|
endif
|
|
call add(l:installers, l:conf.command)
|
|
endfor
|
|
endfor
|
|
return filter(uniq(l:installers), 'stridx(v:val, a:arglead) == 0')
|
|
endfunction
|
|
|
|
function! lsp_settings#complete_install(arglead, cmdline, cursorpos) abort
|
|
let l:installers = []
|
|
|
|
for l:ft in split(&filetype . '.', '\.', 1)
|
|
let l:ft = tolower(empty(l:ft) ? '_' : l:ft)
|
|
if !has_key(s:settings, l:ft)
|
|
continue
|
|
endif
|
|
for l:conf in s:settings[l:ft]
|
|
let l:missing = 0
|
|
for l:require in l:conf.requires
|
|
if !executable(l:require)
|
|
let l:missing = 1
|
|
break
|
|
endif
|
|
endfor
|
|
if l:missing !=# 0
|
|
continue
|
|
endif
|
|
let l:command = printf('%s/install-%s', s:installer_dir, l:conf.command)
|
|
if has('win32')
|
|
let l:command = substitute(l:command, '/', '\', 'g') . '.cmd'
|
|
else
|
|
let l:command = l:command . '.sh'
|
|
endif
|
|
if executable(l:command)
|
|
call add(l:installers, l:conf.command)
|
|
endif
|
|
endfor
|
|
endfor
|
|
return filter(uniq(sort(l:installers)), 'stridx(v:val, a:arglead) == 0')
|
|
endfunction
|
|
|
|
function! s:vim_lsp_uninstall_server(command) abort
|
|
if !lsp_settings#utils#valid_name(a:command)
|
|
call lsp_settings#utils#error('Invalid server name')
|
|
return
|
|
endif
|
|
call lsp_settings#utils#msg('Uninstalling ' . a:command)
|
|
let l:server_install_dir = lsp_settings#servers_dir() . '/' . a:command
|
|
if !isdirectory(l:server_install_dir)
|
|
call lsp_settings#utils#error('Server not found')
|
|
return
|
|
endif
|
|
call delete(l:server_install_dir, 'rf')
|
|
call lsp_settings#utils#msg('Uninstalled ' . a:command)
|
|
endfunction
|
|
|
|
" neovim passes third argument as 'exit' while vim passes only 2 arguments
|
|
function! s:vim_lsp_install_server_post(command, job, code, ...) abort
|
|
if a:code != 0
|
|
return
|
|
endif
|
|
if lsp_settings#executable(a:command)
|
|
let l:script = printf('%s/%s.vim', s:settings_dir, a:command)
|
|
if filereadable(l:script)
|
|
if has('patch-8.1.1113')
|
|
command! -nargs=1 LspRegisterServer autocmd User lsp_setup ++once call lsp#register_server(<args>)
|
|
else
|
|
command! -nargs=1 LspRegisterServer autocmd User lsp_setup call lsp#register_server(<args>)
|
|
endif
|
|
exe 'source' l:script
|
|
delcommand LspRegisterServer
|
|
doautocmd <nomodeline> User lsp_setup
|
|
endif
|
|
endif
|
|
call lsp_settings#utils#msg('Installed ' . a:command)
|
|
endfunction
|
|
|
|
function! s:vim_lsp_install_server(ft, command, bang) abort
|
|
if !empty(a:command) && !lsp_settings#utils#valid_name(a:command)
|
|
call lsp_settings#utils#error('Invalid server name')
|
|
return
|
|
endif
|
|
let l:entry = s:vim_lsp_installer(a:ft, a:command)
|
|
if empty(l:entry)
|
|
call lsp_settings#utils#error('Server not found')
|
|
return
|
|
endif
|
|
if len(l:entry) < 2
|
|
call lsp_settings#utils#error('Server could not be installed. See :messages for details.')
|
|
return
|
|
endif
|
|
if empty(a:bang) && confirm(printf('Install %s ?', l:entry[0]), "&Yes\n&Cancel") !=# 1
|
|
return
|
|
endif
|
|
let l:server_install_dir = lsp_settings#servers_dir() . '/' . l:entry[0]
|
|
if isdirectory(l:server_install_dir)
|
|
call lsp_settings#utils#msg('Uninstalling ' . l:entry[0])
|
|
call delete(l:server_install_dir, 'rf')
|
|
endif
|
|
call mkdir(l:server_install_dir, 'p')
|
|
call lsp_settings#utils#msg('Installing ' . l:entry[0])
|
|
if has('nvim')
|
|
split new
|
|
call termopen(l:entry[1], {'cwd': l:server_install_dir, 'on_exit': function('s:vim_lsp_install_server_post', [l:entry[0]])}) | startinsert
|
|
else
|
|
let l:bufnr = term_start(l:entry[1], {'cwd': l:server_install_dir})
|
|
let l:job = term_getjob(l:bufnr)
|
|
if l:job != v:null
|
|
call job_setoptions(l:job, {'exit_cb': function('s:vim_lsp_install_server_post', [l:entry[0]])})
|
|
endif
|
|
endif
|
|
endfunction
|
|
|
|
function! s:vim_lsp_settings_suggest(ft) abort
|
|
let l:entry = s:vim_lsp_installer(a:ft)
|
|
if empty(l:entry)
|
|
return
|
|
endif
|
|
if lsp_settings#executable(l:entry[0])
|
|
return
|
|
endif
|
|
|
|
redraw!
|
|
echohl Directory
|
|
echomsg 'Please do :LspInstallServer to enable Language Server ' . l:entry[0]
|
|
echohl None
|
|
endfunction
|
|
|
|
function! s:vim_lsp_suggest_plugin() abort
|
|
if &ft != ''
|
|
return
|
|
endif
|
|
let l:ext = expand('%:e')
|
|
for l:ft in keys(s:settings)
|
|
for l:server in s:settings[l:ft]
|
|
if !has_key(l:server, 'vim_plugin')
|
|
continue
|
|
endif
|
|
if index(l:server['vim_plugin']['extensions'], l:ext) == -1
|
|
continue
|
|
endif
|
|
redraw
|
|
echohl Directory
|
|
echomsg printf('Please install vim-plugin "%s" to enable Language Server', l:server['vim_plugin']['name'])
|
|
echohl None
|
|
return
|
|
endfor
|
|
endfor
|
|
endfunction
|
|
|
|
function! s:vim_lsp_load_or_suggest(ft) abort
|
|
if (a:ft !=# '_' && &filetype !=# a:ft) || !has_key(s:settings, a:ft)
|
|
return
|
|
endif
|
|
|
|
if filereadable(lsp_settings#global_settings_dir() . '/settings.json')
|
|
let l:settings = json_decode(join(readfile(lsp_settings#global_settings_dir() . '/settings.json'), "\n"))
|
|
if has_key(g:, 'lsp_settings')
|
|
call extend(g:lsp_settings, l:settings)
|
|
else
|
|
let g:lsp_settings = l:settings
|
|
endif
|
|
endif
|
|
call lsp_settings#profile#load_local()
|
|
|
|
if get(g:, 'lsp_loaded', 0)
|
|
for l:server in s:settings[a:ft]
|
|
let l:config = lsp_settings#server_config(l:server.command)
|
|
let l:refresh_pattern = get(l:config, 'refresh_pattern', '')
|
|
if !empty(l:refresh_pattern)
|
|
let b:asyncomplete_refresh_pattern = l:refresh_pattern
|
|
endif
|
|
endfor
|
|
endif
|
|
|
|
if get(s:ftmap, a:ft, 0)
|
|
return
|
|
endif
|
|
let l:group_name = lsp_settings#utils#group_name(a:ft)
|
|
exe 'augroup' l:group_name
|
|
autocmd!
|
|
augroup END
|
|
exe 'augroup!' l:group_name
|
|
|
|
if has('patch-8.1.1113')
|
|
command! -nargs=1 LspRegisterServer autocmd User lsp_setup ++once call lsp#register_server(<args>)
|
|
else
|
|
command! -nargs=1 LspRegisterServer autocmd User lsp_setup call lsp#register_server(<args>)
|
|
endif
|
|
|
|
let l:default = get(g:, 'lsp_settings_filetype_' . a:ft, '')
|
|
|
|
let l:found = 0
|
|
let l:disabled = 0
|
|
|
|
for l:server in s:settings[a:ft]
|
|
if lsp_settings#get(l:server.command, 'disabled', get(l:server, 'disabled', 0))
|
|
let l:disabled += 1
|
|
continue
|
|
endif
|
|
|
|
if type(l:default) ==# v:t_list
|
|
if len(l:default) ># 0 && index(l:default, l:server.command) == -1
|
|
continue
|
|
endif
|
|
elseif type(l:default) ==# v:t_string
|
|
if !empty(l:default) && l:default != l:server.command
|
|
continue
|
|
endif
|
|
else
|
|
continue
|
|
endif
|
|
|
|
let l:command = lsp_settings#get(l:server.command, 'cmd', [])
|
|
if empty(l:command) && !lsp_settings#executable(l:server.command)
|
|
let l:script = printf('%s/%s.vim', s:checkers_dir, l:server.command)
|
|
if !filereadable(l:script) || has_key(l:server, 'fallback')
|
|
continue
|
|
endif
|
|
let l:server['fallback'] = ''
|
|
try
|
|
exe 'source' l:script
|
|
let l:command = LspCheckCommand()
|
|
let l:server['fallback'] = l:command
|
|
catch
|
|
finally
|
|
if exists('*LspCheckCommand')
|
|
delfunction LspCheckCommand
|
|
endif
|
|
if empty(l:server['fallback'])
|
|
continue
|
|
endif
|
|
endtry
|
|
endif
|
|
let l:script = printf('%s/%s.vim', s:settings_dir, l:server.command)
|
|
if filereadable(l:script)
|
|
exe 'source' l:script
|
|
let l:found += 1
|
|
let s:ftmap[a:ft] = 1
|
|
|
|
" If default server is specified as list, continue to look next.
|
|
if type(l:default) !=# v:t_list
|
|
break
|
|
endif
|
|
endif
|
|
endfor
|
|
|
|
delcommand LspRegisterServer
|
|
|
|
if l:disabled == 0 && l:found ==# 0
|
|
if a:ft !=# '_' && get(g:, 'lsp_settings_enable_suggestions', 1) == 1
|
|
call s:vim_lsp_settings_suggest(a:ft)
|
|
endif
|
|
else
|
|
if exists('#User#lsp_setup') !=# 0
|
|
doautocmd <nomodeline> User lsp_setup
|
|
endif
|
|
endif
|
|
endfunction
|
|
|
|
function! lsp_settings#clear() abort
|
|
let s:ftmap = {}
|
|
endfunction
|
|
|
|
function! lsp_settings#init() abort
|
|
for [l:name, l:server] in items(get(g:, 'lsp_settings', {}))
|
|
if !has_key(l:server, 'allowlist')
|
|
continue
|
|
endif
|
|
for l:ft in l:server['allowlist']
|
|
if !has_key(s:settings, l:ft)
|
|
let s:settings[l:ft] = []
|
|
endif
|
|
let l:found = 0
|
|
for [l:ft2, l:configs] in items(s:settings)
|
|
if l:found || l:ft ==# l:ft2
|
|
continue
|
|
endif
|
|
for l:config in l:configs
|
|
if l:config.command !=# l:name
|
|
continue
|
|
endif
|
|
call add(s:settings[l:ft], l:config)
|
|
let l:found = 1
|
|
break
|
|
endfor
|
|
endfor
|
|
endfor
|
|
endfor
|
|
|
|
for l:ft in keys(s:settings)
|
|
if has_key(g:, 'lsp_settings_allowlist') && index(g:lsp_settings_allowlist, l:ft) == -1 || empty(s:settings[l:ft])
|
|
continue
|
|
endif
|
|
exe 'augroup' lsp_settings#utils#group_name(l:ft)
|
|
autocmd!
|
|
exe 'autocmd FileType' l:ft 'call' printf("s:vim_lsp_load_or_suggest('%s')", l:ft)
|
|
augroup END
|
|
endfor
|
|
augroup vim_lsp_suggest
|
|
autocmd!
|
|
autocmd BufNewFile,BufRead * call s:vim_lsp_suggest_plugin()
|
|
augroup END
|
|
command! -nargs=? -complete=customlist,lsp_settings#complete_uninstall LspUninstallServer call s:vim_lsp_uninstall_server(<q-args>)
|
|
command! -bang -nargs=? -complete=customlist,lsp_settings#complete_install LspInstallServer call s:vim_lsp_install_server(&l:filetype, <q-args>, '<bang>')
|
|
call s:vim_lsp_load_or_suggest('_')
|
|
endfunction
|