diff --git a/CHANGELOG.md b/CHANGELOG.md index f223dcbf..de2b39d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # 0.2.1-dev -- Clean up local state when a language server exits. +- Handle language server restarts: + - Clean up local state when a language server exits. + - Call `didOpen` for all open files when a language server (re)starts. # 0.2.0 diff --git a/autoload/lsc/file.vim b/autoload/lsc/file.vim index f279fa70..d407564e 100644 --- a/autoload/lsc/file.vim +++ b/autoload/lsc/file.vim @@ -1,17 +1,50 @@ +if !exists('s:initialized') + " file path -> file version + let s:file_versions = {} +endif + +" Send a 'didOpen' message for all open files of type `filetype` if they aren't +" already tracked. +function! lsc#file#trackAll(filetype) abort + for buffer in getbufinfo({'loaded': v:true}) + if getbufvar(buffer.bufnr, '&filetype') != a:filetype | continue | endif + call s:DidOpen(buffer.name) + endfor +endfunction + " Run language servers for this filetype if they aren't already running and send " the 'didOpen' message. function! lsc#file#onOpen() abort call lsc#server#start(&filetype) - let file_path = expand('%:p') - let buffer_content = join(getline(1, '$'), "\n") + call s:DidOpen(expand('%:p')) +endfunction + +" Send the 'didOpen' message for a file if it isn't already tracked. +function! s:DidOpen(file_path) abort + if has_key(s:file_versions, a:file_path) | return | endif + let bufnr = bufnr(a:file_path) + if !bufloaded(bufnr) | return | endif + let s:file_versions[a:file_path] = 1 + let buffer_content = join(getbufline(bufnr, 1, '$'), "\n") + let filetype = getbufvar(bufnr, '&filetype') let params = {'textDocument': - \ {'uri': lsc#util#documentUri(), - \ 'languageId': &filetype, - \ 'version': FileVersion(file_path), + \ {'uri': lsc#util#documentUri(a:file_path), + \ 'languageId': filetype, + \ 'version': s:file_versions[a:file_path], \ 'text': buffer_content \ } \ } - call lsc#server#call(&filetype, 'textDocument/didOpen', params) + call lsc#server#call(filetype, 'textDocument/didOpen', params) +endfunction + +" Mark all files of type `filetype` as untracked. +function! lsc#file#clean(filetype) abort + for buffer in getbufinfo({'loaded': v:true}) + if getbufvar(buffer.bufnr, '&filetype') != a:filetype | continue | endif + if has_key(s:file_versions, buffer.name) + unlet s:file_versions[buffer.name] + endif + endfor endfunction function! lsc#file#onChange() abort @@ -27,24 +60,21 @@ function! lsc#file#flushChanges(...) abort if !exists('b:lsc_flush_timer') return endif + let file_path = expand('%:p') + if !has_key(s:file_versions, file_path) | return | endif + let s:file_versions[file_path] += 1 call timer_stop(b:lsc_flush_timer) unlet b:lsc_flush_timer - let file_path = expand('%:p') let buffer_content = join(getline(1, '$'), "\n") let params = {'textDocument': \ {'uri': lsc#util#documentUri(), - \ 'version': FileVersion(file_path), + \ 'version': s:file_versions[file_path], \ }, \ 'contentChanges': [{'text': buffer_content}], \ } call lsc#server#call(&filetype, 'textDocument/didChange', params) endfunction -" file path -> file version -let s:file_versions = {} - -" A monotonically increasing number for each open file. -function! s:FileVersion(file_path) - let s:file_versions[a:file_path] = get(s:file_versions, a:file_path, 0) + 1 - return s:file_versions[a:file_path] +function! lsc#file#version() abort + return get(s:file_versions, expand('%:p'), '') endfunction diff --git a/autoload/lsc/server.vim b/autoload/lsc/server.vim index 27a4dd97..17cd3324 100644 --- a/autoload/lsc/server.vim +++ b/autoload/lsc/server.vim @@ -10,7 +10,9 @@ if !exists('s:initialized') endif function! lsc#server#start(filetype) abort - call RunCommand(g:lsc_server_commands[a:filetype]) + if RunCommand(g:lsc_server_commands[a:filetype]) + call lsc#file#trackAll(a:filetype) + endif endfunction function! lsc#server#kill(file_type) abort @@ -54,11 +56,13 @@ function! lsc#server#setBuffer(ch_id, message) abort let s:channel_buffers[a:ch_id] = a:message endfunction +" Start a language server using `command` if it isn't already running. +" +" Returns v:true if the server was started, or v:false if it was already +" running. function! s:RunCommand(command) abort - if has_key(s:running_servers, a:command) - " Server is already running - return - endif + if has_key(s:running_servers, a:command) | return v:false | endif + let job_options = {'in_io': 'pipe', 'in_mode': 'raw', \ 'out_io': 'pipe', 'out_mode': 'raw', \ 'out_cb': 'lsc#server#channelCallback', 'exit_cb': 'lsc#server#onExit'} @@ -85,6 +89,7 @@ function! s:RunCommand(command) abort \} call lsc#server#call(&filetype, 'initialize', \ params, data.onInitialize, v:true) + return v:true endfunction " Find the command for `job` and clean up it's state @@ -111,6 +116,7 @@ function! s:OnCommandExit(command) abort if g:lsc_server_commands[filetype] != a:command | continue | endif call lsc#complete#clean(filetype) call lsc#diagnostics#clean(filetype) + call lsc#file#clean(filetype) endfor endfunction diff --git a/autoload/lsc/util.vim b/autoload/lsc/util.vim index 96ca4cbd..9fa52d49 100644 --- a/autoload/lsc/util.vim +++ b/autoload/lsc/util.vim @@ -5,8 +5,13 @@ function! lsc#util#winDo(command) abort execute 'keepjumps noautocmd '.current_window.'wincmd w' endfunction -function! lsc#util#documentUri() abort - return 'file://'.expand('%:p') +function! lsc#util#documentUri(...) abort + if a:0 >= 1 + let file_path = a:1 + else + let file_path = expand('%:p') + endif + return 'file://'.file_path endfunction function! lsc#util#documentPath(uri) abort