diff --git a/README.md b/README.md index 960889f..906815b 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,21 @@ let g:langserver_executables = { \ } ``` +To start the language server, run the command: + +```vim +:LSPStart +``` + +After starting the language server, you should be able to run commands like: + +```vim +:LSPGoto +:LSPHover +``` + +and some more to come. + More configuration to come... ## Plans diff --git a/autoload/langserver/api/textDocument.vim b/autoload/langserver/api/textDocument.vim index 25f5e02..6cf3f09 100644 --- a/autoload/langserver/api/textDocument.vim +++ b/autoload/langserver/api/textDocument.vim @@ -7,3 +7,7 @@ function! langserver#api#textDocument#definition(request) abort \ {}, \ ) endfunction + +function! langserver#api#textDocument#completion(request) abort + +endfunction diff --git a/autoload/langserver/client.vim b/autoload/langserver/client.vim index 020401d..78fd8c1 100644 --- a/autoload/langserver/client.vim +++ b/autoload/langserver/client.vim @@ -1,4 +1,4 @@ -let s:lsp_clients = {} " { id, opts, req_seq, on_notifications: { request, on_notification }, stdout: { max_buffer_size, buffer, next_token, current_content_length, current_content_type } } +let s:lsp_clients = {} let s:lsp_token_type_contentlength = 'content-length' let s:lsp_token_type_contenttype = 'content-type' let s:lsp_token_type_message = 'message' @@ -14,11 +14,14 @@ function! s:_on_lsp_stdout(id, data, event) abort " \ 'id': l:lsp_client_id, " \ 'opts': a:opts, " \ 'req_seq': 0, - " \ 'on_notifications': {}, + " \ 'lock': langserver#lock#semaphore(), + " \ 'request_notifications': {}, " \ 'stdout': { " \ 'max_buffer_size': l:max_buffer_size, " \ 'buffer': '', " \ 'next_token': s:lsp_token_type_contentlength, + " \ 'current_content_length': , + " \ 'current_content_type': , " \ }, " \ } let l:client = s:lsp_clients[a:id] @@ -126,6 +129,7 @@ function! s:lsp_start(opts) abort \ 'id': l:lsp_client_id, \ 'opts': a:opts, \ 'req_seq': 0, + \ 'lock': langserver#lock#semaphore(), \ 'on_notifications': {}, \ 'stdout': { \ 'max_buffer_size': l:max_buffer_size, diff --git a/autoload/langserver/completion.vim b/autoload/langserver/completion.vim new file mode 100644 index 0000000..81b0efa --- /dev/null +++ b/autoload/langserver/completion.vim @@ -0,0 +1,38 @@ +function! langserver#completion#callback(id, data, event) abort + let l:parsed_data = langserver#callbacks#data(a:id, a:data, a:event) + if l:parsed_data == {} + return + endif + + " {'isIncomplete': bool, 'items': [CompletionItems]} + let l:completion_items = l:parsed_data['items'] + +endfunction + +function! langserver#completion#request(...) abort + return langserver#client#send(langserver#util#get_lsp_id(), { + \ 'method': 'textDocument/completion', + \ 'params': langserver#util#get_text_document_position_params(), + \ }) +endfunction + +let s:CompletionItemKind = { + \ 'Text': 1, + \ 'Method': 2, + \ 'Function': 3, + \ 'Constructor': 4, + \ 'Field': 5, + \ 'Variable': 6, + \ 'Class': 7, + \ 'Interface': 8, + \ 'Module': 9, + \ 'Property': 10, + \ 'Unit': 11, + \ 'Value': 12, + \ 'Enum': 13, + \ 'Keyword': 14, + \ 'Snippet': 15, + \ 'Color': 16, + \ 'File': 17, + \ 'Reference': 18 + \ } diff --git a/autoload/langserver/hover.vim b/autoload/langserver/hover.vim index 0d357bb..1d1f8d6 100644 --- a/autoload/langserver/hover.vim +++ b/autoload/langserver/hover.vim @@ -20,6 +20,9 @@ function! langserver#hover#request() abort return langserver#client#send(langserver#util#get_lsp_id(), { \ 'method': 'textDocument/hover', \ 'params': langserver#util#get_text_document_position_params(), + \ 'on_notification': { + \ 'callback': function('langserver#hover#callback'), + \ }, \ }) endfunction @@ -38,7 +41,7 @@ function! langserver#hover#display(range, data) abort echo l:hover_string - return timer_start(5000, function('s:delete_highlight')) + return timer_start(3000, function('s:delete_highlight')) endfunction function! s:delete_highlight() abort diff --git a/autoload/langserver/lock.vim b/autoload/langserver/lock.vim new file mode 100644 index 0000000..3b99682 --- /dev/null +++ b/autoload/langserver/lock.vim @@ -0,0 +1,46 @@ +let s:unlocked_id = -1 + +function! s:dict_lock(job_id) dict + if self.locked == v:false + let self.locked = v:true + let self.id = a:job_id + return v:true + else + return v:false + endif +endfunction + +function! s:dict_unlock() dict + let self.id = s:unlocked_id + let self.locked = v:false +endfunction + +function! s:get_id() dict + return self.id +endfunction + +function! s:take_lock(job_id) dict + " If we're locked, kill the other job + " And set ourselves to be the current job. + if self.locked + " TODO: Don't actually want to stop the whole server... + " I just want to stop the current request. Maybe a send cancel request + " would be good enough. We will see. + " call langserver#job#stop(self.id) + call self.unlock() + endif + + call self.lock(a:job_id) +endfunction + +function! langserver#lock#semaphore() abort + let l:ret = { + \ 'id': s:unlocked_id, + \ 'locked': v:false, + \ } + let l:ret.lock = function('s:dict_lock') + let l:ret.unlock = function('s:dict_unlock') + let l:ret.get_id = function('s:get_id') + let l:ret.take_lock = function('s:take_lock') + return l:ret +endfunction diff --git a/rplugin/python3/deoplete/langserver.py b/rplugin/python3/deoplete/langserver.py new file mode 100644 index 0000000..5a282b4 --- /dev/null +++ b/rplugin/python3/deoplete/langserver.py @@ -0,0 +1,24 @@ +from .base import Base + + +class Source(Base): + def __init__(self, nvim): + super(Source, self).__init__(nvim) + + self.nvim = nvim + self.name = 'langserver' + self.mark = '[LSP]' + + def on_event(self, context, filename=''): + pass + + def gather_candidates(self, context): + user_input = context['input'] + filetype = context.get('filetype', '') + complete_str = context['complete_str'] + + return [ + { + 'word': user_input + '_hello', + } + ] diff --git a/tests/test_lock.vader b/tests/test_lock.vader new file mode 100644 index 0000000..403c256 --- /dev/null +++ b/tests/test_lock.vader @@ -0,0 +1,22 @@ +Execute (Test Lock): + let my_lock = langserver#lock#semaphore() + + AssertEqual v:false, my_lock.locked + + call my_lock.lock(1) + AssertEqual v:true, my_lock.locked + + call my_lock.unlock() + AssertEqual v:false, my_lock.locked + + AssertEqual -1, my_lock.get_id() + + call my_lock.lock(1) + AssertEqual 1, my_lock.get_id() + + let set_result = my_lock.lock(2) + AssertEqual v:false, set_result + AssertEqual 1, my_lock.get_id() + + call my_lock.take_lock(2) + AssertEqual 2, my_lock.get_id()