summaryrefslogtreecommitdiff
path: root/autoload
diff options
context:
space:
mode:
authorw0rp <devw0rp@gmail.com>2018-10-31 16:13:22 +0000
committerw0rp <devw0rp@gmail.com>2018-10-31 16:13:31 +0000
commit4ef2c81e95529d4175ba8149fbe42e856a36ab10 (patch)
treec1dfef25bfb7b396a7a4696f65d5a91bc970486f /autoload
parent20e4e3f9db1e46306bbe8ba5c33db92950b2e927 (diff)
downloadale-4ef2c81e95529d4175ba8149fbe42e856a36ab10.zip
Implement LSP symbol search
Diffstat (limited to 'autoload')
-rw-r--r--autoload/ale/lsp.vim6
-rw-r--r--autoload/ale/lsp/message.vim6
-rw-r--r--autoload/ale/preview.vim5
-rw-r--r--autoload/ale/symbol.vim109
4 files changed, 125 insertions, 1 deletions
diff --git a/autoload/ale/lsp.vim b/autoload/ale/lsp.vim
index 196cbe80..b7908e74 100644
--- a/autoload/ale/lsp.vim
+++ b/autoload/ale/lsp.vim
@@ -41,6 +41,7 @@ function! ale#lsp#Register(executable_or_address, project, init_options) abort
\ 'completion': 0,
\ 'completion_trigger_characters': [],
\ 'definition': 0,
+ \ 'symbol_search': 0,
\ },
\}
endif
@@ -203,6 +204,10 @@ function! s:UpdateCapabilities(conn, capabilities) abort
if get(a:capabilities, 'definitionProvider') is v:true
let a:conn.capabilities.definition = 1
endif
+
+ if get(a:capabilities, 'workspaceSymbolProvider') is v:true
+ let a:conn.capabilities.symbol_search = 1
+ endif
endfunction
function! ale#lsp#HandleInitResponse(conn, response) abort
@@ -285,6 +290,7 @@ function! ale#lsp#MarkConnectionAsTsserver(conn_id) abort
let l:conn.capabilities.completion = 1
let l:conn.capabilities.completion_trigger_characters = ['.']
let l:conn.capabilities.definition = 1
+ let l:conn.capabilities.symbol_search = 1
endfunction
" Start a program for LSP servers.
diff --git a/autoload/ale/lsp/message.vim b/autoload/ale/lsp/message.vim
index 9ed41ac4..9fffb83a 100644
--- a/autoload/ale/lsp/message.vim
+++ b/autoload/ale/lsp/message.vim
@@ -130,6 +130,12 @@ function! ale#lsp#message#References(buffer, line, column) abort
\}]
endfunction
+function! ale#lsp#message#Symbol(query) abort
+ return [0, 'workspace/symbol', {
+ \ 'query': a:query,
+ \}]
+endfunction
+
function! ale#lsp#message#Hover(buffer, line, column) abort
return [0, 'textDocument/hover', {
\ 'textDocument': {
diff --git a/autoload/ale/preview.vim b/autoload/ale/preview.vim
index 180a37d0..1f50e0ad 100644
--- a/autoload/ale/preview.vim
+++ b/autoload/ale/preview.vim
@@ -46,11 +46,14 @@ function! ale#preview#ShowSelection(item_list) abort
" Create lines to display to users.
for l:item in a:item_list
+ let l:match = get(l:item, 'match', '')
+
call add(
\ l:lines,
\ l:item.filename
\ . ':' . l:item.line
- \ . ':' . l:item.column,
+ \ . ':' . l:item.column
+ \ . (!empty(l:match) ? ' ' . l:match : ''),
\)
endfor
diff --git a/autoload/ale/symbol.vim b/autoload/ale/symbol.vim
new file mode 100644
index 00000000..5180cb86
--- /dev/null
+++ b/autoload/ale/symbol.vim
@@ -0,0 +1,109 @@
+let s:symbol_map = {}
+
+" Used to get the symbol map in tests.
+function! ale#symbol#GetMap() abort
+ return deepcopy(s:symbol_map)
+endfunction
+
+" Used to set the symbol map in tests.
+function! ale#symbol#SetMap(map) abort
+ let s:symbol_map = a:map
+endfunction
+
+function! ale#symbol#ClearLSPData() abort
+ let s:symbol_map = {}
+endfunction
+
+function! ale#symbol#HandleLSPResponse(conn_id, response) abort
+ if has_key(a:response, 'id')
+ \&& has_key(s:symbol_map, a:response.id)
+ let l:options = remove(s:symbol_map, a:response.id)
+
+ let l:result = get(a:response, 'result', v:null)
+ let l:item_list = []
+
+ if type(l:result) is v:t_list
+ " Each item looks like this:
+ " {
+ " 'name': 'foo',
+ " 'kind': 123,
+ " 'deprecated': v:false,
+ " 'location': {
+ " 'uri': 'file://...',
+ " 'range': {
+ " 'start': {'line': 0, 'character': 0},
+ " 'end': {'line': 0, 'character': 0},
+ " },
+ " },
+ " 'containerName': 'SomeContainer',
+ " }
+ for l:response_item in l:result
+ let l:location = l:response_item.location
+
+ call add(l:item_list, {
+ \ 'filename': ale#path#FromURI(l:location.uri),
+ \ 'line': l:location.range.start.line + 1,
+ \ 'column': l:location.range.start.character + 1,
+ \ 'match': l:response_item.name,
+ \})
+ endfor
+ endif
+
+ if empty(l:item_list)
+ call ale#util#Execute('echom ''No symbols found.''')
+ else
+ call ale#preview#ShowSelection(l:item_list)
+ endif
+ endif
+endfunction
+
+function! s:OnReady(linter, lsp_details, query, ...) abort
+ let l:buffer = a:lsp_details.buffer
+
+ " If we already made a request, stop here.
+ if getbufvar(l:buffer, 'ale_symbol_request_made', 0)
+ return
+ endif
+
+ let l:id = a:lsp_details.connection_id
+
+ let l:Callback = function('ale#symbol#HandleLSPResponse')
+ call ale#lsp#RegisterCallback(l:id, l:Callback)
+
+ let l:message = ale#lsp#message#Symbol(a:query)
+ let l:request_id = ale#lsp#Send(l:id, l:message)
+
+ call setbufvar(l:buffer, 'ale_symbol_request_made', 1)
+ let s:symbol_map[l:request_id] = {
+ \ 'buffer': l:buffer,
+ \}
+endfunction
+
+function! s:Search(linter, buffer, query) abort
+ let l:lsp_details = ale#lsp_linter#StartLSP(a:buffer, a:linter)
+
+ if !empty(l:lsp_details)
+ call ale#lsp#WaitForCapability(
+ \ l:lsp_details.connection_id,
+ \ 'symbol_search',
+ \ function('s:OnReady', [a:linter, l:lsp_details, a:query]),
+ \)
+ endif
+endfunction
+
+function! ale#symbol#Search(query) abort
+ if type(a:query) isnot v:t_string || empty(a:query)
+ throw 'A non-empty string must be provided!'
+ endif
+
+ let l:buffer = bufnr('')
+
+ " Set a flag so we only make one request.
+ call setbufvar(l:buffer, 'ale_symbol_request_made', 0)
+
+ for l:linter in ale#linter#Get(getbufvar(l:buffer, '&filetype'))
+ if !empty(l:linter.lsp) && l:linter.lsp isnot# 'tsserver'
+ call s:Search(l:linter, l:buffer, a:query)
+ endif
+ endfor
+endfunction