diff --git a/autoload/lsp.vim b/autoload/lsp.vim index ad3f18ed..679636ae 100644 --- a/autoload/lsp.vim +++ b/autoload/lsp.vim @@ -1,7 +1,8 @@ let s:enabled = 0 let s:already_setup = 0 let s:Stream = lsp#callbag#makeSubject() -let s:servers = {} " { lsp_id, server_info, init_callbacks, init_result, buffers: { path: { changed_tick } } +" workspace_folders = { 'uri': { uri, name } } +let s:servers = {} " { lsp_id, server_info, workspace_folders, init_callbacks, init_result, buffers: { path: { changed_tick } } let s:last_command_id = 0 let s:notification_callbacks = [] " { name, callback } @@ -167,10 +168,12 @@ function! lsp#register_server(server_info) abort if has_key(s:servers, l:server_name) call lsp#log('lsp#register_server', 'server already registered', l:server_name) endif + " NOTE: workspace_folders is dict for faster lookup instead of array let s:servers[l:server_name] = { \ 'server_info': a:server_info, \ 'lsp_id': 0, \ 'buffers': {}, + \ 'workspace_folders': {}, \ } call lsp#log('lsp#register_server', 'server registered', l:server_name) doautocmd User lsp_register_server @@ -565,7 +568,8 @@ function! lsp#default_get_supported_capabilities(server_info) abort \ }, \ 'workspace': { \ 'applyEdit': v:true, - \ 'configuration': v:true + \ 'configuration': v:true, + \ 'workspaceFolders': g:lsp_experimental_workspace_folders ? v:true : v:false, \ }, \ } endfunction @@ -598,6 +602,7 @@ function! s:ensure_init(buf, server_name, cb) abort let l:root_uri = lsp#utils#get_default_root_uri() endif let l:server['server_info']['_root_uri_resolved'] = l:root_uri + let l:server['workspace_folders'][l:root_uri] = { 'name': l:root_uri, 'uri': l:root_uri } if has_key(l:server_info, 'capabilities') let l:capabilities = l:server_info['capabilities'] @@ -616,6 +621,15 @@ function! s:ensure_init(buf, server_name, cb) abort \ 'trace': 'off', \ }, \ } + + let l:workspace_capabilities = get(l:capabilities, 'workspace', {}) + if get(l:workspace_capabilities, 'workspaceFolders', v:false) + " TODO: extract folder name for l:root_uri + let l:server_info['workspaceFolders'] = [ + \ { 'uri': l:root_uri, 'name': l:root_uri } + \ ] + let l:request['params']['workspaceFolders'] = l:server_info['workspaceFolders'] + endif if has_key(l:server_info, 'initialization_options') let l:request.params['initializationOptions'] = l:server_info['initialization_options'] @@ -645,7 +659,6 @@ endfunction function! s:text_changes(buf, server_name) abort let l:sync_kind = lsp#capabilities#get_text_document_change_sync_kind(a:server_name) - " When syncKind is None, return null for contentChanges. if l:sync_kind == 0 return v:null @@ -729,6 +742,10 @@ function! s:ensure_open(buf, server_name, cb) abort return endif + if lsp#capabilities#has_workspace_folders_change_notifications(a:server_name) + call s:workspace_add_folder(a:server_name) + endif + call s:update_file_content(a:buf, a:server_name, lsp#utils#buffer#_get_lines(a:buf)) let l:buffer_info = { 'changed_tick': getbufvar(a:buf, 'changedtick'), 'version': 1, 'uri': l:path } @@ -748,6 +765,24 @@ function! s:ensure_open(buf, server_name, cb) abort call a:cb(l:msg) endfunction +function! s:workspace_add_folder(server_name) abort + if !g:lsp_experimental_workspace_folders | return | endif + let l:server = s:servers[a:server_name] + let l:server_info = l:server['server_info'] + let l:root_uri = has_key(l:server_info, 'root_uri') ? l:server_info['root_uri'](l:server_info) : lsp#utils#get_default_root_uri() + if !has_key(l:server['workspace_folders'], l:root_uri) + let l:workspace_folder = { 'name': l:root_uri, 'uri': l:root_uri } + call lsp#log('adding workspace folder', a:server_name, l:workspace_folder) + call s:send_notification(a:server_name, { + \ 'method': 'workspace/didChangeWorkspaceFolders', + \ 'params': { + \ 'added': [l:workspace_folder], + \ } + \ }) + let l:server['workspace_folders'][l:root_uri] = l:workspace_folder + endif +endfunction + function! s:send_request(server_name, data) abort let l:lsp_id = s:servers[a:server_name]['lsp_id'] let l:data = copy(a:data) @@ -789,6 +824,7 @@ function! s:on_exit(server_name, id, data, event) abort if has_key(l:server, 'init_result') unlet l:server['init_result'] endif + let l:server['workspace_folders'] = {} call lsp#stream(1, { 'server': '$vimlsp', \ 'response': { 'method': '$/vimlsp/lsp_server_exit', 'params': { 'server': a:server_name } } }) doautocmd User lsp_server_exit diff --git a/autoload/lsp/capabilities.vim b/autoload/lsp/capabilities.vim index a1e6e55a..ef66f00f 100644 --- a/autoload/lsp/capabilities.vim +++ b/autoload/lsp/capabilities.vim @@ -33,6 +33,22 @@ function! lsp#capabilities#has_rename_prepare_provider(server_name) abort return s:has_provider(a:server_name, 'renameProvider', 'prepareProvider') endfunction +function! lsp#capabilities#has_workspace_folders_change_notifications(server_name) abort + let l:capabilities = lsp#get_server_capabilities(a:server_name) + if type(l:capabilities) == type({}) && !empty(l:capabilities) + let l:workspace = get(l:capabilities, 'workspace', {}) + if type(l:workspace) == type({}) && !empty(l:workspace) + let l:workspace_folders = get(l:workspace, 'workspaceFolders', {}) + if type(l:workspace_folders) == type({}) && !empty(l:workspace_folders) + if get(l:workspace_folders, 'supported', v:false) && get(l:workspace_folders, 'changeNotifications', '') ==# 'workspace/didChangeWorkspaceFolders' + return v:true + endif + endif + endif + endif + return v:false +endfunction + function! lsp#capabilities#has_document_formatting_provider(server_name) abort return s:has_provider(a:server_name, 'documentFormattingProvider') endfunction diff --git a/doc/vim-lsp.txt b/doc/vim-lsp.txt index aacc39ec..6b095d52 100644 --- a/doc/vim-lsp.txt +++ b/doc/vim-lsp.txt @@ -157,6 +157,7 @@ CONTENTS *vim-lsp-contents* Folding |vim-lsp-folding| Semantic highlighting |vim-lsp-semantic| Popup Formatting |vim-lsp-popup-format| + Workspace Folders |vim-lsp-workspace-folders| License |vim-lsp-license| Maintainers |vim-lsp-maintainers| @@ -2023,6 +2024,18 @@ For customization, see |formatprg|. ============================================================================== +Workspace Folders *vim-lsp-workspace-folders* + +Workspace folders is an experimental feature of vim-lsp. To enable workspace +folders set `let g:lsp_experimental_workspace_folders = 1`. In the future this +flag will be removed and workspace folders will be enabled by default. + +When a new buffer is opened, if the server supports workspace folder, it will +call `root_uri` function to detect the workspace folder. If the folder is not +part of workspace folder, it will automatically notify the server to add the +workspace folder. + +============================================================================= License *vim-lsp-license* The MIT License (MIT) diff --git a/plugin/lsp.vim b/plugin/lsp.vim index af5c33fb..7c21871c 100644 --- a/plugin/lsp.vim +++ b/plugin/lsp.vim @@ -73,6 +73,8 @@ let g:lsp_untitled_buffer_enabled = get(g:, 'lsp_untitled_buffer_enabled', 1) let g:lsp_get_supported_capabilities = get(g:, 'lsp_get_supported_capabilities', [function('lsp#default_get_supported_capabilities')]) +let g:lsp_experimental_workspace_folders = get(g:, 'lsp_experimental_workspace_folders', 0) + if g:lsp_auto_enable augroup lsp_auto_enable autocmd!