From f05dd6dd63f6f14cb333abbe347e061667b4ebf9 Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Sun, 20 Nov 2016 15:41:39 -0500 Subject: [PATCH 1/8] Remove stderr debugging statement --- autoload/langserver/callbacks.vim | 2 -- 1 file changed, 2 deletions(-) diff --git a/autoload/langserver/callbacks.vim b/autoload/langserver/callbacks.vim index d09aae7..61911bc 100644 --- a/autoload/langserver/callbacks.vim +++ b/autoload/langserver/callbacks.vim @@ -3,11 +3,9 @@ function! langserver#callbacks#on_stdout(id, data, event) abort endfunction function! langserver#callbacks#on_stderr(id, data, event) abort - echom 'stderr ...' call langserver#log#response(a:id, a:data, a:event) echom string(a:data) - echom '...stderr' endfunction function! langserver#callbacks#on_exit(id, status, event) abort From 8fe864308408482cf8b3511ee935d183edf9afe6 Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Sun, 20 Nov 2016 16:10:49 -0500 Subject: [PATCH 2/8] Start using a more generic mapping Should allow for comma separated lists for a server. Still need to deal with getting the right value of the id to each process. --- autoload/langserver/util.vim | 70 +++++++++++++++++++++++------------- tests/test_defaults.vader | 20 +++++++++++ 2 files changed, 66 insertions(+), 24 deletions(-) create mode 100644 tests/test_defaults.vader diff --git a/autoload/langserver/util.vim b/autoload/langserver/util.vim index ccbaa62..7d5043c 100644 --- a/autoload/langserver/util.vim +++ b/autoload/langserver/util.vim @@ -71,24 +71,24 @@ endfunction " @key code TODO " @key source TODO function! langserver#util#get_diagnostic(start, end, message, options) abort - let return_dict = { + let l:return_dict = { \ 'range': langserver#util#get_range(a:start, a:end), \ 'messsage': a:message \ } if has_key(a:options, 'severity') - let return_dict['severity'] = a:options['severity'] + let l:return_dict['severity'] = a:options['severity'] endif if has_key(a:options, 'code') - let return_dict['code'] = a:options['code'] + let l:return_dict['code'] = a:options['code'] endif if has_key(a:options, 'source') - let return_dict['source'] = a:options['source'] + let l:return_dict['source'] = a:options['source'] endif - return return_dict + return l:return_dict endfunction "" @@ -103,13 +103,13 @@ endfunction " @param command (str): The identifier of the actual command handler " @param arguments (Optional[list]): Optional list of arguments passed to the command function! langserver#util#get_command(title, command, ...) abort - let return_dict = { + let l:return_dict = { \ 'title': a:title, \ 'command': a:command, \ } if a:0 > 1 - let return_dict['arguments'] = a:1 + let l:return_dict['arguments'] = a:1 endif endfunction @@ -137,7 +137,7 @@ endfunction " TODO: Figure out a better way to do this. " @param function! langserver#util#get_workspace_edit(uri, edit) abort - let my_dict = { + let l:my_dict = { \ 'uri': 'edit', \ } endfunction @@ -209,38 +209,60 @@ endfunction " Parse the stdin of a server function! langserver#util#parse_message(message) abort if type(a:message) ==# type([]) - let data = join(a:message, '') + let l:data = join(a:message, '') elseif type(a:message) ==# type('') - let data = a:message + let l:data = a:message else endif - let parsed = {} - if data =~? '--> request' - let parsed['type'] = 'request' - elseif data =~? '<-- result' - let parsed['type'] = 'result' + let l:parsed = {} + if l:data =~? '--> request' + let l:parsed['type'] = 'request' + elseif l:data =~? '<-- result' + let l:parsed['type'] = 'result' else - let parsed['type'] = 'info' + let l:parsed['type'] = 'info' endif - let data = substitute(data, '--> request #\w*: ', '', 'g') - let data = substitute(data, '<-- result #\w*: ', '', 'g') + let l:data = substitute(l:data, '--> request #\w*: ', '', 'g') + let l:data = substitute(l:data, '<-- result #\w*: ', '', 'g') - if parsed['type'] ==# 'request' || parsed['type'] ==# 'result' - let data = substitute(data, '^\(\S*\):', '"\1":', 'g') - let data = '{' . data . '}' - let data = json_decode(data) + if l:parsed['type'] ==# 'request' || l:parsed['type'] ==# 'result' + let l:data = substitute(l:data, '^\(\S*\):', '"\1":', 'g') + let l:data = '{' . l:data . '}' + let l:data = json_decode(l:data) endif - let parsed['data'] = data - return parsed + let l:parsed['data'] = l:data + return l:parsed endfunction function! langserver#util#debug() abort return v:false endfunction +function! langserver#util#get_executable_key(...) abort + if a:0 > 0 + let l:file_type = a:1 + else + let l:file_type = &filetype + endif + + if !exists('g:langserver_executables') + echoerr '`g:langserver_executables` was not defined' + return '' + endif + + for l:k in keys(g:langserver_executables) + if index(split(l:k, ','), l:file_type) >= 0 + return l:k + endif + endfor + + echoerr 'Unsupported filetype: ' . l:file_type + return '' +endfunction + function! langserver#util#get_lsp_id() abort let g:lsp_id_map = get(g:, 'lsp_id_map', {}) diff --git a/tests/test_defaults.vader b/tests/test_defaults.vader new file mode 100644 index 0000000..5d73065 --- /dev/null +++ b/tests/test_defaults.vader @@ -0,0 +1,20 @@ +Execute (Testing executable configuration): + let g:__temp_exec = get(g:, 'langserver_executables', {}) + let g:langserver_executables = { + \ 'go': { + \ 'name': 'sourcegraph/langserver-go', + \ 'cmd': ['langserver-go', '-trace', '-logfile', expand('~/Desktop/langserver-go.log')], + \ }, + \ 'python': { + \ 'name': 'sourcegraph/python-langserver', + \ 'cmd': [expand('~/bin/python-langserver/python-langserver.py')], + \ }, + \ 'javascript,typescript,jsx,tsx': { + \ 'name': 'javascript-typescript', + \ 'cmd': [], + \ }, + \ } + + AssertEqual 'javascript,typescript,jsx,tsx', langserver#util#get_executable_key('jsx') + + let g:langserver_executables = g:__temp_exec From 48521082f76652cdaea876d6494345e0a7ed395a Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Mon, 21 Nov 2016 16:37:37 -0500 Subject: [PATCH 3/8] Some documentation updates --- README.md | 6 ++++++ autoload/langserver/default.vim | 14 ++++++++++++-- doc/langserver.txt | 10 +++++++++- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 58dced6..57d13c7 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,12 @@ Shim for the language server protocol developed by Microsoft. The protocol can b ## Configuration +First you need to install a language server. An example of installing one might be: + +```shell +$ go get github.com/sourcegraph/go-langserver/langserver/cmd/langserver-go +``` + You will need to put this somewhere that is sourced on startup. ```vim diff --git a/autoload/langserver/default.vim b/autoload/langserver/default.vim index 404e4ab..f067131 100644 --- a/autoload/langserver/default.vim +++ b/autoload/langserver/default.vim @@ -3,9 +3,19 @@ let s:langserver_executabe = 'langserver-go' "" " Get the default command for starting the server function! langserver#default#cmd() abort + let l:bad_cmd = [-1] + if has_key(g:langserver_executables, &filetype) - return g:langserver_executables[&filetype]['cmd'] + let l:tmp_cmd = g:langserver_executables[&filetype]['cmd'] + + if type(l:tmp_cmd) != type([]) + echoerr 'Make sure your dictionary is structued like: {"filetype": {"cmd": [cmd, list, here]}}' + return l:bad_cmd + endif + + return l:tmp_cmd + return else - return [-1] + return l:bad_cmd endif endfunction diff --git a/doc/langserver.txt b/doc/langserver.txt index 3389b92..4a3f635 100644 --- a/doc/langserver.txt +++ b/doc/langserver.txt @@ -20,7 +20,10 @@ CONFIGURATION *langserver-configuration* Define a dictionary like this in your vimrc. > let g:langserver_executables = { - \ 'go': ['langserver-go', '-trace', '-logfile', expand('~/Desktop/langserver-go.log')], + \ 'go': { + \ 'name': 'sourcegraph/langserver-go', + \ 'cmd': ['langserver-go', '-trace', '-logfile', expand('~/Desktop/langserver-go.log')], + \ }, \ } TODO: Mappings @@ -33,6 +36,11 @@ Begin with calling: > call langserver#start({}) < +or: > + + :LSPStart +> + TODO: Opening a file TODO: Goto definition TODO: Hover From 443ba4fad162c51d374b8c0cbb7f7ed27a1d4277 Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Mon, 21 Nov 2016 20:34:30 -0500 Subject: [PATCH 4/8] Add support for fs/readFile command It seems to be working. I'm able to get readings for hover. I'm still having some difficulty with some of the Goto definition items, but at least it seems that I can read things and send the base64 encoding to the server. --- autoload/langserver/callbacks.vim | 10 +++------- autoload/langserver/client.vim | 4 ++++ autoload/langserver/extension/command.vim | 7 +++---- autoload/langserver/extension/fs.vim | 9 ++++++++- autoload/langserver/goto.vim | 10 ++++++++-- autoload/langserver/initialize.vim | 8 +++++++- autoload/langserver/log.vim | 4 +++- 7 files changed, 36 insertions(+), 16 deletions(-) diff --git a/autoload/langserver/callbacks.vim b/autoload/langserver/callbacks.vim index 61911bc..05ccde7 100644 --- a/autoload/langserver/callbacks.vim +++ b/autoload/langserver/callbacks.vim @@ -4,8 +4,6 @@ endfunction function! langserver#callbacks#on_stderr(id, data, event) abort call langserver#log#response(a:id, a:data, a:event) - - echom string(a:data) endfunction function! langserver#callbacks#on_exit(id, status, event) abort @@ -29,14 +27,12 @@ function! langserver#callbacks#on_notification(id, data, event) abort call langserver#hover#callback(a:id, a:data, a:event) elseif l:last_topic ==? 'textDocument/didOpen' call langserver#documents#callback_did_open(a:id, a:data, a:event) + elseif l:last_topic ==? 'initialize' + call langserver#initialize#callback(a:id, a:data, a:event) elseif l:last_topic ==? 'workspace/symbol' call langserver#symbol#workspace#callback(a:id, a:data, a:event) else - " if langserver#extension#command#callback(a:id, a:data, a:event) - " call langserver#log#log('debug', 'Handled: ' . l:last_topic) - " else - call langserver#log#log('warning', 'LAST REQUEST: ' . l:last_topic, v:true) - " endif + call langserver#log#log('warning', 'No callback registered for: ' . l:last_topic, v:true) endif elseif has_key(a:data, 'request') echom 'notification...' diff --git a/autoload/langserver/client.vim b/autoload/langserver/client.vim index a6e3371..4ebd774 100644 --- a/autoload/langserver/client.vim +++ b/autoload/langserver/client.vim @@ -146,6 +146,10 @@ function! s:lsp_send_request(id, opts) abort " opts = { method, params?, on_noti let l:msg.params = a:opts.params endif + if has_key(a:opts, 'result') + let l:msg.result = a:opts.result + endif + let l:json = json_encode(l:msg) let l:req_data = 'Content-Length: ' . len(l:json) . "\r\n\r\n" . l:json diff --git a/autoload/langserver/extension/command.vim b/autoload/langserver/extension/command.vim index bed1fe1..6963205 100644 --- a/autoload/langserver/extension/command.vim +++ b/autoload/langserver/extension/command.vim @@ -20,10 +20,9 @@ function! langserver#extension#command#callback(id, data, event) abort call langserver#client#send(a:id, { \ 'req_id': a:data.request.id, - \ 'method': 'fs/readFile', - \ 'params': { - \ 'result': l:response, - \ }, + \ 'method': l:method, + \ 'params': a:data.request.params, + \ 'result': l:response, \ }) return v:true endfunction diff --git a/autoload/langserver/extension/fs.vim b/autoload/langserver/extension/fs.vim index adf4ae6..fec21f9 100644 --- a/autoload/langserver/extension/fs.vim +++ b/autoload/langserver/extension/fs.vim @@ -7,5 +7,12 @@ function! langserver#extension#fs#readFille(filename) abort echom a:filename - return join(readfile(a:filename), "\n") + " let l:text = join(readfile(a:filename), "\n") + " echo system(['base64', join(readfile('/home/tj/test/lsp.py'), "\n")]) + if filereadable(a:filename) + return system(['base64', a:filename]) + else + call langserver#log#log('error', 'Unable to read file: ' . a:filename) + return '' + endif endfunction diff --git a/autoload/langserver/goto.vim b/autoload/langserver/goto.vim index 78ac311..27dde86 100644 --- a/autoload/langserver/goto.vim +++ b/autoload/langserver/goto.vim @@ -8,8 +8,14 @@ function! langserver#goto#callback(id, data, event) abort if has_key(a:data, 'response') if type(a:data['response']['result']) == type([]) let l:parsed_data = a:data['response']['result'][0] - else - let l:parsed_data = a:data['response']['result'] + elseif type(a:data['response']['result']) == type({}) + " Check if we have an empty dictionary + if empty(a:data['response']['result']) + call langserver#log#log('warning', 'No definition found for: ' . string(a:data['request']), v:true) + return + else + let l:parsed_data = a:data['response']['result'] + endif endif else return diff --git a/autoload/langserver/initialize.vim b/autoload/langserver/initialize.vim index 7e7702b..6104dd4 100644 --- a/autoload/langserver/initialize.vim +++ b/autoload/langserver/initialize.vim @@ -30,6 +30,12 @@ function! langserver#initialize#get_client_capabilities() abort return {} endfunction +function! langserver#initialize#callback(id, data, event) abort + call langserver#initialize#response(a:id, a:data.response.result) + + call langserver#log#log('info', 'Succesfully connected to: ' . string(a:id), v:true) +endfunction + "" " Handle the response of the server. " This message details the capabilities of the language server. @@ -47,7 +53,7 @@ function! langserver#initialize#response(name, response) abort if has_key(a:response, 'completionProvider') let l:complete_opt_resolve = get(a:response['completionProvider'], 'resolveProvider', v:false) - let l:complete_opt_trigger = get(a:resposne['completionProvider'], 'triggerCharacters', []) + let l:complete_opt_trigger = get(a:response['completionProvider'], 'triggerCharacters', []) call langserver#capabilities#set_completion_provider(a:name, l:complete_opt_resolve, l:complete_opt_trigger) endif diff --git a/autoload/langserver/log.vim b/autoload/langserver/log.vim index 26186e3..a0d0823 100644 --- a/autoload/langserver/log.vim +++ b/autoload/langserver/log.vim @@ -112,7 +112,9 @@ function! langserver#log#callback(id, data, event) abort \ a:id, \ a:event, \ string(a:data) - \)) + \ ), + \ v:false + \ ) endfunction "" From afacd8da567feb9a77a10bb9d83d275fae49f0a9 Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Tue, 22 Nov 2016 12:25:52 -0500 Subject: [PATCH 5/8] Add initial API --- autoload/langserver/api/textDocument.vim | 9 +++++ autoload/langserver/client.vim | 11 +++++++ autoload/langserver/goto.vim | 7 ++-- tests/test_goto.vader | 42 ++++++++++++++++++++++++ 4 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 autoload/langserver/api/textDocument.vim create mode 100644 tests/test_goto.vader diff --git a/autoload/langserver/api/textDocument.vim b/autoload/langserver/api/textDocument.vim new file mode 100644 index 0000000..25f5e02 --- /dev/null +++ b/autoload/langserver/api/textDocument.vim @@ -0,0 +1,9 @@ +"" +function! langserver#api#textDocument#definition(request) abort + return langserver#goto#goto_defintion( + \ langserver#util#get_lsp_id(), + \ a:request.result.uri, + \ a:request.result.range, + \ {}, + \ ) +endfunction diff --git a/autoload/langserver/client.vim b/autoload/langserver/client.vim index 4ebd774..0cdd4e1 100644 --- a/autoload/langserver/client.vim +++ b/autoload/langserver/client.vim @@ -10,6 +10,17 @@ let s:lsp_text_document_sync_kind_incremental = 2 function! s:_on_lsp_stdout(id, data, event) abort if has_key(s:lsp_clients, a:id) + " let s:lsp_clients[l:lsp_client_id] = { + " \ 'id': l:lsp_client_id, + " \ 'opts': a:opts, + " \ 'req_seq': 0, + " \ 'on_notifications': {}, + " \ 'stdout': { + " \ 'max_buffer_size': l:max_buffer_size, + " \ 'buffer': '', + " \ 'next_token': s:lsp_token_type_contentlength, + " \ }, + " \ } let l:client = s:lsp_clients[a:id] let l:client.stdout.buffer .= join(a:data, "\n") diff --git a/autoload/langserver/goto.vim b/autoload/langserver/goto.vim index 27dde86..8fade52 100644 --- a/autoload/langserver/goto.vim +++ b/autoload/langserver/goto.vim @@ -83,8 +83,11 @@ function! langserver#goto#goto_defintion(name, uri, range_dict, options) abort " \ a:range_dict['start']['line'] + 1, " \ a:range_dict['start']['character'] + 1, " \ ) - execute(printf('norm! %sG%s|', + let l:action = printf('norm! %sG%s|', \ a:range_dict['start']['line'] + 1, \ a:range_dict['start']['character'] + 1, - \ )) + \ ) + execute(l:action) + + return l:action endfunction diff --git a/tests/test_goto.vader b/tests/test_goto.vader new file mode 100644 index 0000000..7e7e705 --- /dev/null +++ b/tests/test_goto.vader @@ -0,0 +1,42 @@ + +Given vim (Goto position): + let this = 'that' + + " A comment here + " A comment there + " Here a comment, there a comment + + " Everywhere a comment + +Do (Move): + 2j + +Execute (Call goto): + let g:test_request = { + \ 'result': { + \ 'uri': 'file://' . expand('%'), + \ 'range': { + \ 'start': {'line': 0, 'character': 4}, + \ 'end': {'line': 0, 'character': 8}, + \ }, + \ }} + + Log g:test_request.result.uri + + let cmd = langserver#api#textDocument#definition(g:test_request) + AssertEqual cmd, 'norm! 1G5|' + +" I Would like to use these but it doesn't seem to work. +" Then (delete character): +" x + +" Expect (Line to be deleted): +" let his = 'that' + +" " A comment here +" " A comment there +" " Here a comment, there a comment + +" " Everywhere a comment + + From c92a91e186438d36ffb335a3febf55eaa3f774ff Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Wed, 23 Nov 2016 09:27:01 -0500 Subject: [PATCH 6/8] Prototype for functions in configuration --- autoload/langserver/default.vim | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/autoload/langserver/default.vim b/autoload/langserver/default.vim index f067131..2c381a0 100644 --- a/autoload/langserver/default.vim +++ b/autoload/langserver/default.vim @@ -6,16 +6,19 @@ function! langserver#default#cmd() abort let l:bad_cmd = [-1] if has_key(g:langserver_executables, &filetype) - let l:tmp_cmd = g:langserver_executables[&filetype]['cmd'] - - if type(l:tmp_cmd) != type([]) - echoerr 'Make sure your dictionary is structued like: {"filetype": {"cmd": [cmd, list, here]}}' - return l:bad_cmd - endif + let l:tmp_cmd = g:langserver_executables[&filetype]['cmd'] + if type(l:tmp_cmd) == type([]) return l:tmp_cmd - return - else - return l:bad_cmd + elseif type(l:tmp_cmd) == type(function('tr')) + let l:result = l:tmp_cmd() + if type(l:result) == type([]) + return l:result + endif + endif endif + + " If we didn't return anything, there was an error. + echoerr 'Please consult the documentation for how to configure the langserver' + return l:bad_cmd endfunction From 6973ffc98bc63491703d843479a943b5d38449b2 Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Wed, 23 Nov 2016 10:05:01 -0500 Subject: [PATCH 7/8] Add reference to implementations --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 57d13c7..960889f 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ First you need to install a language server. An example of installing one might $ go get github.com/sourcegraph/go-langserver/langserver/cmd/langserver-go ``` +A more complete set of language servers can be found here: https://github.com/Microsoft/language-server-protocol/wiki/Protocol-Implementations + You will need to put this somewhere that is sourced on startup. ```vim From 3c946346d5bafe5e1cf7c63c7e51ec9be40bef22 Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Wed, 23 Nov 2016 13:44:35 -0500 Subject: [PATCH 8/8] Add ability to specify command for executable Also added some more logging and debugging items. Added some tests for the configuration dictionary. Updated documentation --- autoload/langserver/client.vim | 8 ++ autoload/langserver/default.vim | 22 +++- autoload/langserver/initialize.vim | 13 +- autoload/langserver/log.vim | 203 +++++++++++++++-------------- doc/langserver.txt | 8 ++ tests/test_defaults.vader | 46 +++++++ 6 files changed, 189 insertions(+), 111 deletions(-) diff --git a/autoload/langserver/client.vim b/autoload/langserver/client.vim index 0cdd4e1..2e64e54 100644 --- a/autoload/langserver/client.vim +++ b/autoload/langserver/client.vim @@ -190,6 +190,14 @@ function! s:lsp_get_last_request_id(id) abort endfunction function! s:lsp_is_error(notification) abort + " if type(a:notification) != type({}) + " return v:true + " endif + + " if !has_key(a:notification, 'response') || !has_key(a:notification.response, 'result') + " return v:true + " endif + return has_key(a:notification, 'error') endfunction diff --git a/autoload/langserver/default.vim b/autoload/langserver/default.vim index 2c381a0..a799a88 100644 --- a/autoload/langserver/default.vim +++ b/autoload/langserver/default.vim @@ -2,16 +2,24 @@ let s:langserver_executabe = 'langserver-go' "" " Get the default command for starting the server -function! langserver#default#cmd() abort +function! langserver#default#cmd(...) abort + if a:0 > 0 + let l:filetype_key = langserver#util#get_executable_key(a:1) + else + let l:filetype_key = langserver#util#get_executable_key(&filetype) + endif + let l:bad_cmd = [-1] - if has_key(g:langserver_executables, &filetype) - let l:tmp_cmd = g:langserver_executables[&filetype]['cmd'] + if has_key(g:langserver_executables, l:filetype_key) + " Has to be uppercase because of function naming + " Sorry for mixed case :/ + let l:TmpCmd = g:langserver_executables[l:filetype_key]['cmd'] - if type(l:tmp_cmd) == type([]) - return l:tmp_cmd - elseif type(l:tmp_cmd) == type(function('tr')) - let l:result = l:tmp_cmd() + if type(l:TmpCmd) == type([]) + return l:TmpCmd + elseif type(l:TmpCmd) == type(function('tr')) + let l:result = l:TmpCmd() if type(l:result) == type([]) return l:result endif diff --git a/autoload/langserver/initialize.vim b/autoload/langserver/initialize.vim index 6104dd4..c4a2326 100644 --- a/autoload/langserver/initialize.vim +++ b/autoload/langserver/initialize.vim @@ -31,9 +31,16 @@ function! langserver#initialize#get_client_capabilities() abort endfunction function! langserver#initialize#callback(id, data, event) abort - call langserver#initialize#response(a:id, a:data.response.result) - - call langserver#log#log('info', 'Succesfully connected to: ' . string(a:id), v:true) + if langserver#client#is_error(a:data.response) + call langserver#log#log('error', + \ 'Could not connect to: ' . string(a:id) . "\n" . + \ 'Message was: ' . string(a:data), + \ v:true, + \ ) + else + call langserver#initialize#response(a:id, a:data.response.result) + call langserver#log#log('info', 'Succesfully connected to: ' . string(a:id), v:true) + endif endfunction "" diff --git a/autoload/langserver/log.vim b/autoload/langserver/log.vim index a0d0823..91c9676 100644 --- a/autoload/langserver/log.vim +++ b/autoload/langserver/log.vim @@ -2,119 +2,120 @@ let s:log_file = expand('~/langserver-vim.log') let s:current_level = 4 let s:log_level_map = { - \ 'error': 0, - \ 'warning': 1, - \ 'info': 2, - \ 'debug': 3, - \ 'micro': 4, - \ } + \ 'error': 0, + \ 'warning': 1, + \ 'info': 2, + \ 'debug': 3, + \ 'micro': 4, + \ } let s:clear_log = v:true "" " Logging helper function! langserver#log#log(level, message, ...) abort - if s:clear_log - call writefile([], s:log_file, '') - let s:clear_log = v:false - endif - - if a:0 > 0 - let l:echo_choice = a:1 - else - let l:echo_choice = v:false - endif - - let l:numeric_level = s:log_level_map[a:level] - - if type(a:message) == type('') - let l:msg = [a:message] - elseif type(a:message) == type({}) - let l:msg = [string(a:message)] - elseif type(a:message) != type([]) - " TODO: Handle other types of messages? - else - let l:msg = a:message - endif - - let l:final_msg = [] - for l:item in l:msg - call add(l:final_msg, printf('%5s: %s', - \ a:level, - \ l:item, - \ )) - endfor - - - if l:numeric_level < s:current_level - if l:echo_choice - echom string(l:final_msg) - endif - - call writefile(l:final_msg, s:log_file, 'a') - endif + if s:clear_log + call writefile([], s:log_file, '') + let s:clear_log = v:false + endif + + if a:0 > 0 + let l:echo_choice = a:1 + else + let l:echo_choice = v:false + endif + + let l:numeric_level = s:log_level_map[a:level] + + if type(a:message) == type('') + let l:msg = [a:message] + elseif type(a:message) == type({}) + let l:msg = [string(a:message)] + elseif type(a:message) != type([]) + " TODO: Handle other types of messages? + else + let l:msg = a:message + endif + + let l:final_msg = [] + for l:item in l:msg + call add(l:final_msg, printf('%5s: %s', + \ a:level, + \ l:item, + \ )) + endfor + + + if l:numeric_level < s:current_level + if l:echo_choice + echom string(l:final_msg) + endif + + call writefile(l:final_msg, s:log_file, 'a') + endif endfunction "" " Log response helper function! langserver#log#response(id, data, event) abort - let g:last_response = a:data - - if type(a:data) != type({}) - call langserver#log#log('debug', - \ printf('(%3s:%15s): %s', - \ a:id, - \ a:event, - \ string(a:data) - \ ), - \ langserver#util#debug(), - \ ) - return - endif - - if has_key(a:data, 'response') - call langserver#log#log('debug', - \ printf('(%3s:%15s): Response -> M(%20s), D(%s)', - \ a:id, - \ a:event, - \ string(a:data.request.method), - \ string(a:data.response.result), - \ ), - \ langserver#util#debug(), - \ ) - elseif has_key(a:data, 'request') - call langserver#log#log('debug', - \ printf('(%3s:%15s): Request -> M(%20s), D(%s)', - \ a:id, - \ a:event, - \ string(a:data.request.method), - \ string(a:data.request.params), - \ ), - \ langserver#util#debug(), - \ ) - else - call langserver#log#log('debug', - \ printf('(%3s:%15s): Unknown -> D(%s)', - \ a:id, - \ a:event, - \ string(a:data), - \ ), - \ langserver#util#debug(), - \ ) - endif + let g:last_response = a:data + + if type(a:data) != type({}) + call langserver#log#log('debug', + \ printf('(%3s:%15s): %s', + \ a:id, + \ a:event, + \ string(a:data) + \ ), + \ langserver#util#debug(), + \ ) + return + endif + + if has_key(a:data, 'response') && has_key(a:data, 'request') + \ && has_key(a:data.response, 'result') && has_key(a:data.request, 'method') + call langserver#log#log('debug', + \ printf('(%3s:%15s): Response -> M(%20s), D(%s)', + \ a:id, + \ a:event, + \ string(a:data.request.method), + \ string(a:data.response.result), + \ ), + \ langserver#util#debug(), + \ ) + elseif has_key(a:data, 'request') && type(a:data.request) == type({}) + call langserver#log#log('debug', + \ printf('(%3s:%15s): Request -> M(%20s), D(%s)', + \ a:id, + \ a:event, + \ string(a:data.request.method), + \ string(a:data.request.params), + \ ), + \ langserver#util#debug(), + \ ) + else + call langserver#log#log('debug', + \ printf('(%3s:%15s): Unknown -> D(%s)', + \ a:id, + \ a:event, + \ string(a:data), + \ ), + \ langserver#util#debug(), + \ ) + endif endfunction "" " Log only at debug level function! langserver#log#callback(id, data, event) abort - call langserver#log#log('debug', - \ printf('(%3s:%15s): %s', - \ a:id, - \ a:event, - \ string(a:data) - \ ), - \ v:false - \ ) + call langserver#log#log('debug', + \ printf('(%3s:%15s): %s', + \ a:id, + \ a:event, + \ string(a:data) + \ ), + \ v:false + \ ) endfunction "" @@ -122,10 +123,10 @@ endfunction function! langserver#log#server_request(id, data, event) abort call langserver#log#log('info', \ printf('(%3s:%15s): %s', - \ a:id, - \ a:event, - \ string(a:data) - \ )) + \ a:id, + \ a:event, + \ string(a:data) + \ )) endfunction function! langserver#log#pretty_print(json_dict) abort diff --git a/doc/langserver.txt b/doc/langserver.txt index 4a3f635..bb7d9fa 100644 --- a/doc/langserver.txt +++ b/doc/langserver.txt @@ -19,11 +19,19 @@ CONFIGURATION *langserver-configuration* Define a dictionary like this in your vimrc. > + function! GetMyJSCommand() abort + return ['executable', 'options'] + endfunction + let g:langserver_executables = { \ 'go': { \ 'name': 'sourcegraph/langserver-go', \ 'cmd': ['langserver-go', '-trace', '-logfile', expand('~/Desktop/langserver-go.log')], \ }, + \ 'javascript,typescript': { + \ 'name': 'myjsserver', + \ 'cmd': function('GetMyJSCommand'), + \ }, \ } TODO: Mappings diff --git a/tests/test_defaults.vader b/tests/test_defaults.vader index 5d73065..565d4cb 100644 --- a/tests/test_defaults.vader +++ b/tests/test_defaults.vader @@ -18,3 +18,49 @@ Execute (Testing executable configuration): AssertEqual 'javascript,typescript,jsx,tsx', langserver#util#get_executable_key('jsx') let g:langserver_executables = g:__temp_exec + + +Execute (Testing executable function as cmd): + let g:__temp_exec = get(g:, 'langserver_executables', {}) + + let g:__vader_test__global_var = 'not updated' + function! s:set_global_var() abort + let g:__vader_test__global_var = 'updated' + return ['executable', 'options'] + endfunction + + let g:langserver_executables = { + \ 'example': { + \ 'name': 'sourcegraph/langserver-go', + \ 'cmd': function('s:set_global_var'), + \ }, + \ } + + AssertEqual ['executable', 'options'], langserver#default#cmd('example') + AssertEqual g:__vader_test__global_var, 'updated' + + let g:langserver_executables = g:__temp_exec + + +Execute (Testing executable function as cmd with cs list): + let g:__temp_exec = get(g:, 'langserver_executables', {}) + + let g:__vader_test__global_var = 'not updated' + function! s:set_global_var() abort + let g:__vader_test__global_var = 'updated' + return ['executable', 'options'] + endfunction + + let g:langserver_executables = { + \ 'example,this,that,the_other': { + \ 'name': 'sourcegraph/langserver-go', + \ 'cmd': function('s:set_global_var'), + \ }, + \ } + + AssertEqual ['executable', 'options'], langserver#default#cmd('example') + AssertEqual g:__vader_test__global_var, 'updated' + + let g:langserver_executables = g:__temp_exec + +