summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorw0rp <devw0rp@gmail.com>2019-05-14 00:21:58 +0100
committerw0rp <devw0rp@gmail.com>2019-05-14 00:21:58 +0100
commit381fe1badfe39283f28ca02e0b724e6d49ab23ff (patch)
treec4298819b34c81c8837ae9199dbe8fb2690c816f
parent07b596efb52ef56dda212777c30bad58269ce6d9 (diff)
downloadale-381fe1badfe39283f28ca02e0b724e6d49ab23ff.zip
Close #829 - Close LSP documents when buffers are deleted
-rw-r--r--autoload/ale/engine.vim4
-rw-r--r--autoload/ale/lsp.vim29
-rw-r--r--test/lsp/test_closing_documents.vader176
3 files changed, 209 insertions, 0 deletions
diff --git a/autoload/ale/engine.vim b/autoload/ale/engine.vim
index 7db808d6..491d3c2e 100644
--- a/autoload/ale/engine.vim
+++ b/autoload/ale/engine.vim
@@ -710,6 +710,10 @@ function! ale#engine#Cleanup(buffer) abort
return
endif
+ if exists('*ale#lsp#CloseDocument')
+ call ale#lsp#CloseDocument(a:buffer)
+ endif
+
if !has_key(g:ale_buffer_info, a:buffer)
return
endif
diff --git a/autoload/ale/lsp.vim b/autoload/ale/lsp.vim
index 7186d2a9..986e4c1b 100644
--- a/autoload/ale/lsp.vim
+++ b/autoload/ale/lsp.vim
@@ -484,6 +484,35 @@ function! ale#lsp#OpenDocument(conn_id, buffer, language_id) abort
return l:opened
endfunction
+" Notify LSP servers or tsserver that a document is closed, if opened before.
+" If a document is closed, 1 will be returned, otherwise 0 will be returned.
+"
+" Only the buffer number is required here. A message will be sent to every
+" language server that was notified previously of the document being opened.
+function! ale#lsp#CloseDocument(buffer) abort
+ let l:closed = 0
+
+ " The connection keys are sorted so the messages are easier to test, and
+ " so messages are sent in a consistent order.
+ for l:conn_id in sort(keys(s:connections))
+ let l:conn = s:connections[l:conn_id]
+
+ if l:conn.initialized && has_key(l:conn.open_documents, a:buffer)
+ if l:conn.is_tsserver
+ let l:message = ale#lsp#tsserver_message#Close(a:buffer)
+ else
+ let l:message = ale#lsp#message#DidClose(a:buffer)
+ endif
+
+ call ale#lsp#Send(l:conn_id, l:message)
+ call remove(l:conn.open_documents, a:buffer)
+ let l:closed = 1
+ endif
+ endfor
+
+ return l:closed
+endfunction
+
" Notify LSP servers or tsserver that a document has changed, if needed.
" If a notification is sent, 1 will be returned, otherwise 0 will be returned.
function! ale#lsp#NotifyForChanges(conn_id, buffer) abort
diff --git a/test/lsp/test_closing_documents.vader b/test/lsp/test_closing_documents.vader
new file mode 100644
index 00000000..e6e11415
--- /dev/null
+++ b/test/lsp/test_closing_documents.vader
@@ -0,0 +1,176 @@
+Before:
+ runtime autoload/ale/lsp.vim
+
+ let g:message_list = []
+
+ function! MarkAllConnectionsInitialized() abort
+ for l:conn in values(ale#lsp#GetConnections())
+ let l:conn.initialized = 1
+ endfor
+ endfunction
+
+ function! MarkDocumentOpened() abort
+ for l:conn in values(ale#lsp#GetConnections())
+ let l:conn.open_documents[bufnr('')] = 1
+ endfor
+ endfunction
+
+ function! ale#lsp#Send(conn_id, message) abort
+ let l:connections = ale#lsp#GetConnections()
+
+ if !l:connections[a:conn_id].initialized
+ throw 'LSP server not initialized yet!'
+ endif
+
+ call add(g:message_list, [a:conn_id] + a:message)
+ endfunction
+
+ call ale#lsp#ResetConnections()
+
+After:
+ unlet! g:message_list
+ delfunction MarkAllConnectionsInitialized
+ delfunction MarkDocumentOpened
+
+ call ale#lsp#ResetConnections()
+
+ runtime autoload/ale/lsp.vim
+
+Execute(No errors should be thrown if the connection is not initialized):
+ call ale#lsp#Register('command', '/foo', {})
+ call MarkDocumentOpened()
+
+ call ale#engine#Cleanup(bufnr(''))
+ AssertEqual [], g:message_list
+
+Execute(No messages should be sent if the document wasn't opened):
+ call ale#lsp#Register('command', '/foo', {})
+ call MarkAllConnectionsInitialized()
+
+ call ale#engine#Cleanup(bufnr(''))
+ AssertEqual [], g:message_list
+
+Execute(A message should be sent if the document was opened):
+ call ale#lsp#Register('command', '/foo', {})
+ call MarkAllConnectionsInitialized()
+
+ call ale#lsp#OpenDocument('command:/foo', bufnr(''), 'lang')
+ call ale#engine#Cleanup(bufnr(''))
+ " We should only send the message once.
+ call ale#engine#Cleanup(bufnr(''))
+
+ AssertEqual
+ \ [
+ \ ['command:/foo', 1, 'textDocument/didOpen', {
+ \ 'textDocument': {
+ \ 'uri': ale#path#ToURI(expand('%:p')),
+ \ 'version': g:ale_lsp_next_version_id - 1,
+ \ 'languageId': 'lang',
+ \ 'text': "\n",
+ \ },
+ \ }],
+ \ ['command:/foo', 1, 'textDocument/didClose', {
+ \ 'textDocument': {
+ \ 'uri': ale#path#ToURI(expand('%:p')),
+ \ },
+ \ }],
+ \ ],
+ \ g:message_list
+
+Execute(A message should be sent if the document was opened for tsserver):
+ call ale#lsp#Register('command', '/foo', {})
+ call ale#lsp#MarkConnectionAsTsserver('command:/foo')
+
+ call ale#lsp#OpenDocument('command:/foo', bufnr(''), 'lang')
+ call ale#engine#Cleanup(bufnr(''))
+ " We should only send the message once.
+ call ale#engine#Cleanup(bufnr(''))
+
+ AssertEqual
+ \ [
+ \ ['command:/foo', 1, 'ts@open', {'file': expand('%:p')}],
+ \ ['command:/foo', 1, 'ts@close', {'file': expand('%:p')}],
+ \ ],
+ \ g:message_list
+
+Execute(Re-opening and closing the documents should work):
+ call ale#lsp#Register('command', '/foo', {})
+ call MarkAllConnectionsInitialized()
+
+ call ale#lsp#OpenDocument('command:/foo', bufnr(''), 'lang')
+ call ale#engine#Cleanup(bufnr(''))
+ call ale#lsp#OpenDocument('command:/foo', bufnr(''), 'lang')
+ call ale#engine#Cleanup(bufnr(''))
+
+ AssertEqual
+ \ [
+ \ ['command:/foo', 1, 'textDocument/didOpen', {
+ \ 'textDocument': {
+ \ 'uri': ale#path#ToURI(expand('%:p')),
+ \ 'version': g:ale_lsp_next_version_id - 2,
+ \ 'languageId': 'lang',
+ \ 'text': "\n",
+ \ },
+ \ }],
+ \ ['command:/foo', 1, 'textDocument/didClose', {
+ \ 'textDocument': {
+ \ 'uri': ale#path#ToURI(expand('%:p')),
+ \ },
+ \ }],
+ \ ['command:/foo', 1, 'textDocument/didOpen', {
+ \ 'textDocument': {
+ \ 'uri': ale#path#ToURI(expand('%:p')),
+ \ 'version': g:ale_lsp_next_version_id - 1,
+ \ 'languageId': 'lang',
+ \ 'text': "\n",
+ \ },
+ \ }],
+ \ ['command:/foo', 1, 'textDocument/didClose', {
+ \ 'textDocument': {
+ \ 'uri': ale#path#ToURI(expand('%:p')),
+ \ },
+ \ }],
+ \ ],
+ \ g:message_list
+
+Execute(Messages for closing documents should be sent to each server):
+ call ale#lsp#Register('command', '/foo', {})
+ call ale#lsp#Register('command', '/bar', {})
+ call MarkAllConnectionsInitialized()
+
+ call ale#lsp#OpenDocument('command:/foo', bufnr(''), 'lang')
+ call ale#lsp#OpenDocument('command:/bar', bufnr(''), 'lang')
+ call ale#engine#Cleanup(bufnr(''))
+ " We should only send the message once.
+ call ale#engine#Cleanup(bufnr(''))
+
+ AssertEqual
+ \ [
+ \ ['command:/foo', 1, 'textDocument/didOpen', {
+ \ 'textDocument': {
+ \ 'uri': ale#path#ToURI(expand('%:p')),
+ \ 'version': g:ale_lsp_next_version_id - 2,
+ \ 'languageId': 'lang',
+ \ 'text': "\n",
+ \ },
+ \ }],
+ \ ['command:/bar', 1, 'textDocument/didOpen', {
+ \ 'textDocument': {
+ \ 'uri': ale#path#ToURI(expand('%:p')),
+ \ 'version': g:ale_lsp_next_version_id - 1,
+ \ 'languageId': 'lang',
+ \ 'text': "\n",
+ \ },
+ \ }],
+ \ ['command:/bar', 1, 'textDocument/didClose', {
+ \ 'textDocument': {
+ \ 'uri': ale#path#ToURI(expand('%:p')),
+ \ },
+ \ }],
+ \ ['command:/foo', 1, 'textDocument/didClose', {
+ \ 'textDocument': {
+ \ 'uri': ale#path#ToURI(expand('%:p')),
+ \ },
+ \ }],
+ \ ],
+ \ g:message_list