mirror of
https://github.com/dense-analysis/ale.git
synced 2025-12-06 20:54:26 +08:00
Add eclipselsp jdt:// support for textDocument/definition (#4030)
This patch adds support for opening jdt:// links on "go to definition" requests returned by Java language servers. Co-authored-by: w0rp <devw0rp@gmail.com>
This commit is contained in:
@@ -278,7 +278,7 @@ function! ale#code_action#BuildChangesList(changes_map) abort
|
||||
endfor
|
||||
|
||||
call add(l:changes, {
|
||||
\ 'fileName': ale#path#FromURI(l:file_name),
|
||||
\ 'fileName': ale#util#ToResource(l:file_name),
|
||||
\ 'textChanges': l:text_changes,
|
||||
\})
|
||||
endfor
|
||||
|
||||
@@ -68,18 +68,27 @@ function! ale#definition#HandleLSPResponse(conn_id, response) abort
|
||||
for l:item in l:result
|
||||
if has_key(l:item, 'targetUri')
|
||||
" LocationLink items use targetUri
|
||||
let l:filename = ale#path#FromURI(l:item.targetUri)
|
||||
let l:uri = l:item.targetUri
|
||||
let l:line = l:item.targetRange.start.line + 1
|
||||
let l:column = l:item.targetRange.start.character + 1
|
||||
else
|
||||
" LocationLink items use uri
|
||||
let l:filename = ale#path#FromURI(l:item.uri)
|
||||
let l:uri = l:item.uri
|
||||
let l:line = l:item.range.start.line + 1
|
||||
let l:column = l:item.range.start.character + 1
|
||||
endif
|
||||
|
||||
call ale#definition#UpdateTagStack()
|
||||
call ale#util#Open(l:filename, l:line, l:column, l:options)
|
||||
|
||||
let l:uri_handler = ale#uri#GetURIHandler(l:uri)
|
||||
|
||||
if l:uri_handler is# v:null
|
||||
let l:filename = ale#path#FromFileURI(l:uri)
|
||||
call ale#util#Open(l:filename, l:line, l:column, l:options)
|
||||
else
|
||||
call l:uri_handler.OpenURILink(l:uri, l:line, l:column, l:options, a:conn_id)
|
||||
endif
|
||||
|
||||
break
|
||||
endfor
|
||||
endif
|
||||
|
||||
@@ -156,4 +156,10 @@ function! ale#events#Init() abort
|
||||
endif
|
||||
endif
|
||||
augroup END
|
||||
|
||||
augroup AleURISchemes
|
||||
autocmd!
|
||||
|
||||
autocmd BufNewFile,BufReadPre jdt://** call ale#uri#jdt#ReadJDTLink(expand('<amatch>'))
|
||||
augroup END
|
||||
endfunction
|
||||
|
||||
@@ -35,7 +35,7 @@ function! ale#lsp#message#Initialize(root_path, options, capabilities) abort
|
||||
\ 'rootPath': a:root_path,
|
||||
\ 'capabilities': a:capabilities,
|
||||
\ 'initializationOptions': a:options,
|
||||
\ 'rootUri': ale#path#ToURI(a:root_path),
|
||||
\ 'rootUri': ale#util#ToURI(a:root_path),
|
||||
\}]
|
||||
endfunction
|
||||
|
||||
@@ -56,7 +56,7 @@ function! ale#lsp#message#DidOpen(buffer, language_id) abort
|
||||
|
||||
return [1, 'textDocument/didOpen', {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ 'languageId': a:language_id,
|
||||
\ 'version': ale#lsp#message#GetNextVersionID(),
|
||||
\ 'text': join(l:lines, "\n") . "\n",
|
||||
@@ -70,7 +70,7 @@ function! ale#lsp#message#DidChange(buffer) abort
|
||||
" For changes, we simply send the full text of the document to the server.
|
||||
return [1, 'textDocument/didChange', {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ 'version': ale#lsp#message#GetNextVersionID(),
|
||||
\ },
|
||||
\ 'contentChanges': [{'text': join(l:lines, "\n") . "\n"}]
|
||||
@@ -80,7 +80,7 @@ endfunction
|
||||
function! ale#lsp#message#DidSave(buffer, includeText) abort
|
||||
let l:response = [1, 'textDocument/didSave', {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ },
|
||||
\}]
|
||||
|
||||
@@ -95,7 +95,7 @@ endfunction
|
||||
function! ale#lsp#message#DidClose(buffer) abort
|
||||
return [1, 'textDocument/didClose', {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ },
|
||||
\}]
|
||||
endfunction
|
||||
@@ -106,7 +106,7 @@ let s:COMPLETION_TRIGGER_CHARACTER = 2
|
||||
function! ale#lsp#message#Completion(buffer, line, column, trigger_character) abort
|
||||
let l:message = [0, 'textDocument/completion', {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ },
|
||||
\ 'position': {'line': a:line - 1, 'character': a:column - 1},
|
||||
\}]
|
||||
@@ -124,7 +124,7 @@ endfunction
|
||||
function! ale#lsp#message#Definition(buffer, line, column) abort
|
||||
return [0, 'textDocument/definition', {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ },
|
||||
\ 'position': {'line': a:line - 1, 'character': a:column - 1},
|
||||
\}]
|
||||
@@ -133,7 +133,7 @@ endfunction
|
||||
function! ale#lsp#message#TypeDefinition(buffer, line, column) abort
|
||||
return [0, 'textDocument/typeDefinition', {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ },
|
||||
\ 'position': {'line': a:line - 1, 'character': a:column - 1},
|
||||
\}]
|
||||
@@ -142,7 +142,7 @@ endfunction
|
||||
function! ale#lsp#message#References(buffer, line, column) abort
|
||||
return [0, 'textDocument/references', {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ },
|
||||
\ 'position': {'line': a:line - 1, 'character': a:column - 1},
|
||||
\ 'context': {'includeDeclaration': v:false},
|
||||
@@ -158,7 +158,7 @@ endfunction
|
||||
function! ale#lsp#message#Hover(buffer, line, column) abort
|
||||
return [0, 'textDocument/hover', {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ },
|
||||
\ 'position': {'line': a:line - 1, 'character': a:column - 1},
|
||||
\}]
|
||||
@@ -173,7 +173,7 @@ endfunction
|
||||
function! ale#lsp#message#Rename(buffer, line, column, new_name) abort
|
||||
return [0, 'textDocument/rename', {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ },
|
||||
\ 'position': {'line': a:line - 1, 'character': a:column - 1},
|
||||
\ 'newName': a:new_name,
|
||||
@@ -183,7 +183,7 @@ endfunction
|
||||
function! ale#lsp#message#CodeAction(buffer, line, column, end_line, end_column, diagnostics) abort
|
||||
return [0, 'textDocument/codeAction', {
|
||||
\ 'textDocument': {
|
||||
\ 'uri': ale#path#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ 'uri': ale#util#ToURI(expand('#' . a:buffer . ':p')),
|
||||
\ },
|
||||
\ 'range': {
|
||||
\ 'start': {'line': a:line - 1, 'character': a:column - 1},
|
||||
|
||||
@@ -59,7 +59,7 @@ function! ale#lsp#response#ReadDiagnostics(response) abort
|
||||
\ && l:diagnostic.relatedInformation isnot v:null
|
||||
let l:related = deepcopy(l:diagnostic.relatedInformation)
|
||||
call map(l:related, {key, val ->
|
||||
\ ale#path#FromURI(val.location.uri) .
|
||||
\ ale#util#ToResource(val.location.uri) .
|
||||
\ ':' . (val.location.range.start.line + 1) .
|
||||
\ ':' . (val.location.range.start.character + 1) .
|
||||
\ ":\n\t" . val.message
|
||||
|
||||
@@ -11,6 +11,22 @@ endif
|
||||
" A Dictionary to track one-shot handlers for custom LSP requests
|
||||
let s:custom_handlers_map = get(s:, 'custom_handlers_map', {})
|
||||
|
||||
" Clear LSP linter data for the linting engine.
|
||||
function! ale#lsp_linter#ClearLSPData() abort
|
||||
let s:lsp_linter_map = {}
|
||||
let s:custom_handlers_map = {}
|
||||
endfunction
|
||||
|
||||
" Only for internal use.
|
||||
function! ale#lsp_linter#GetLSPLinterMap() abort
|
||||
return s:lsp_linter_map
|
||||
endfunction
|
||||
|
||||
" Just for tests.
|
||||
function! ale#lsp_linter#SetLSPLinterMap(replacement_map) abort
|
||||
let s:lsp_linter_map = a:replacement_map
|
||||
endfunction
|
||||
|
||||
" Check if diagnostics for a particular linter should be ignored.
|
||||
function! s:ShouldIgnore(buffer, linter_name) abort
|
||||
" Ignore all diagnostics if LSP integration is disabled.
|
||||
@@ -33,7 +49,7 @@ endfunction
|
||||
|
||||
function! s:HandleLSPDiagnostics(conn_id, response) abort
|
||||
let l:linter_name = s:lsp_linter_map[a:conn_id]
|
||||
let l:filename = ale#path#FromURI(a:response.params.uri)
|
||||
let l:filename = ale#util#ToResource(a:response.params.uri)
|
||||
let l:escaped_name = escape(
|
||||
\ fnameescape(l:filename),
|
||||
\ has('win32') ? '^' : '^,}]'
|
||||
@@ -477,17 +493,6 @@ function! ale#lsp_linter#CheckWithLSP(buffer, linter) abort
|
||||
return ale#lsp_linter#StartLSP(a:buffer, a:linter, function('s:CheckWithLSP'))
|
||||
endfunction
|
||||
|
||||
" Clear LSP linter data for the linting engine.
|
||||
function! ale#lsp_linter#ClearLSPData() abort
|
||||
let s:lsp_linter_map = {}
|
||||
let s:custom_handlers_map = {}
|
||||
endfunction
|
||||
|
||||
" Just for tests.
|
||||
function! ale#lsp_linter#SetLSPLinterMap(replacement_map) abort
|
||||
let s:lsp_linter_map = a:replacement_map
|
||||
endfunction
|
||||
|
||||
function! s:HandleLSPResponseToCustomRequests(conn_id, response) abort
|
||||
if has_key(a:response, 'id')
|
||||
\&& has_key(s:custom_handlers_map, a:response.id)
|
||||
|
||||
@@ -218,7 +218,7 @@ endfunction
|
||||
" Convert a filesystem path to a file:// URI
|
||||
" relatives paths will not be prefixed with the protocol.
|
||||
" For Windows paths, the `:` in C:\ etc. will not be percent-encoded.
|
||||
function! ale#path#ToURI(path) abort
|
||||
function! ale#path#ToFileURI(path) abort
|
||||
let l:has_drive_letter = a:path[1:2] is# ':\'
|
||||
|
||||
return substitute(
|
||||
@@ -231,7 +231,7 @@ function! ale#path#ToURI(path) abort
|
||||
\)
|
||||
endfunction
|
||||
|
||||
function! ale#path#FromURI(uri) abort
|
||||
function! ale#path#FromFileURI(uri) abort
|
||||
if a:uri[:6] is? 'file://'
|
||||
let l:encoded_path = a:uri[7:]
|
||||
elseif a:uri[:4] is? 'file:'
|
||||
|
||||
@@ -66,13 +66,13 @@ endfunction
|
||||
function! ale#references#FormatLSPResponseItem(response_item, options) abort
|
||||
if get(a:options, 'open_in') is# 'quickfix'
|
||||
return {
|
||||
\ 'filename': ale#path#FromURI(a:response_item.uri),
|
||||
\ 'filename': ale#util#ToResource(a:response_item.uri),
|
||||
\ 'lnum': a:response_item.range.start.line + 1,
|
||||
\ 'col': a:response_item.range.start.character + 1,
|
||||
\}
|
||||
else
|
||||
return {
|
||||
\ 'filename': ale#path#FromURI(a:response_item.uri),
|
||||
\ 'filename': ale#util#ToResource(a:response_item.uri),
|
||||
\ 'line': a:response_item.range.start.line + 1,
|
||||
\ 'column': a:response_item.range.start.character + 1,
|
||||
\}
|
||||
|
||||
@@ -41,7 +41,7 @@ function! ale#symbol#HandleLSPResponse(conn_id, response) abort
|
||||
let l:location = l:response_item.location
|
||||
|
||||
call add(l:item_list, {
|
||||
\ 'filename': ale#path#FromURI(l:location.uri),
|
||||
\ 'filename': ale#util#ToResource(l:location.uri),
|
||||
\ 'line': l:location.range.start.line + 1,
|
||||
\ 'column': l:location.range.start.character + 1,
|
||||
\ 'match': l:response_item.name,
|
||||
|
||||
@@ -25,3 +25,19 @@ function! ale#uri#Decode(value) abort
|
||||
\ 'g'
|
||||
\)
|
||||
endfunction
|
||||
|
||||
let s:uri_handlers = {
|
||||
\ 'jdt': {
|
||||
\ 'OpenURILink': function('ale#uri#jdt#OpenJDTLink'),
|
||||
\ }
|
||||
\}
|
||||
|
||||
function! ale#uri#GetURIHandler(uri) abort
|
||||
for l:scheme in keys(s:uri_handlers)
|
||||
if a:uri =~# '^'.l:scheme.'://'
|
||||
return s:uri_handlers[scheme]
|
||||
endif
|
||||
endfor
|
||||
|
||||
return v:null
|
||||
endfunction
|
||||
|
||||
106
autoload/ale/uri/jdt.vim
Normal file
106
autoload/ale/uri/jdt.vim
Normal file
@@ -0,0 +1,106 @@
|
||||
" Author: yoshi1123 <yoshi1@tutanota.com>
|
||||
" Description: Functions for working with jdt:// URIs.
|
||||
|
||||
function! s:OpenJDTLink(root, uri, line, column, options, result) abort
|
||||
if has_key(a:result, 'error')
|
||||
execute 'echoerr a:result.error.message'
|
||||
|
||||
return
|
||||
endif
|
||||
|
||||
let l:contents = a:result['result']
|
||||
|
||||
if type(l:contents) is# type(v:null)
|
||||
execute 'echoerr ''File content not found'''
|
||||
endif
|
||||
|
||||
" disable autocmd when opening buffer
|
||||
autocmd! AleURISchemes
|
||||
call ale#util#Open(a:uri, a:line, a:column, a:options)
|
||||
autocmd AleURISchemes BufNewFile,BufReadPre jdt://** call ale#uri#jdt#ReadJDTLink(expand('<amatch>'))
|
||||
|
||||
if !empty(getbufvar(bufnr(''), 'ale_lsp_root', ''))
|
||||
return
|
||||
endif
|
||||
|
||||
let b:ale_lsp_root = a:root
|
||||
set filetype=java
|
||||
|
||||
call setline(1, split(l:contents, '\n'))
|
||||
call cursor(a:line, a:column)
|
||||
normal! zz
|
||||
|
||||
setlocal buftype=nofile nomodified nomodifiable readonly
|
||||
endfunction
|
||||
|
||||
" Load new buffer with jdt:// contents and jump to line and column.
|
||||
function! ale#uri#jdt#OpenJDTLink(encoded_uri, line, column, options, conn_id) abort
|
||||
let l:found_eclipselsp = v:false
|
||||
|
||||
for l:linter in ale#linter#Get('java')
|
||||
if l:linter.name is# 'eclipselsp'
|
||||
let l:found_eclipselsp = v:true
|
||||
endif
|
||||
endfor
|
||||
|
||||
if !l:found_eclipselsp
|
||||
throw 'eclipselsp not running'
|
||||
endif
|
||||
|
||||
let l:root = a:conn_id[stridx(a:conn_id, ':')+1:]
|
||||
let l:uri = a:encoded_uri
|
||||
call ale#lsp_linter#SendRequest(
|
||||
\ bufnr(''),
|
||||
\ 'eclipselsp',
|
||||
\ [0, 'java/classFileContents', {'uri': ale#util#ToURI(l:uri)}],
|
||||
\ function('s:OpenJDTLink', [l:root, l:uri, a:line, a:column, a:options])
|
||||
\)
|
||||
endfunction
|
||||
|
||||
function! s:ReadClassFileContents(uri, result) abort
|
||||
if has_key(a:result, 'error')
|
||||
execute 'echoerr a:result.error.message'
|
||||
|
||||
return
|
||||
endif
|
||||
|
||||
let l:contents = a:result['result']
|
||||
|
||||
if type(l:contents) is# type(v:null)
|
||||
execute 'echoerr ''File content not found'''
|
||||
endif
|
||||
|
||||
call setline(1, split(l:contents, '\n'))
|
||||
|
||||
setlocal buftype=nofile nomodified nomodifiable readonly
|
||||
endfunction
|
||||
|
||||
" Read jdt:// contents, as part of current project, into current buffer.
|
||||
function! ale#uri#jdt#ReadJDTLink(encoded_uri) abort
|
||||
if !empty(getbufvar(bufnr(''), 'ale_lsp_root', ''))
|
||||
return
|
||||
endif
|
||||
|
||||
let l:linter_map = ale#lsp_linter#GetLSPLinterMap()
|
||||
|
||||
for l:conn_id in keys(l:linter_map)
|
||||
if l:linter_map[l:conn_id] is# 'eclipselsp'
|
||||
let l:root = l:conn_id[stridx(l:conn_id, ':')+1:]
|
||||
endif
|
||||
endfor
|
||||
|
||||
if l:root is# v:null
|
||||
throw 'eclipselsp not running'
|
||||
endif
|
||||
|
||||
let l:uri = a:encoded_uri
|
||||
let b:ale_lsp_root = l:root
|
||||
set filetype=java
|
||||
|
||||
call ale#lsp_linter#SendRequest(
|
||||
\ bufnr(''),
|
||||
\ 'eclipselsp',
|
||||
\ [0, 'java/classFileContents', {'uri': ale#util#ToURI(l:uri)}],
|
||||
\ function('s:ReadClassFileContents', [l:uri])
|
||||
\)
|
||||
endfunction
|
||||
@@ -543,3 +543,31 @@ endfunction
|
||||
function! ale#util#GetBufferContents(buffer) abort
|
||||
return join(getbufline(a:buffer, 1, '$'), '\n') . '\n'
|
||||
endfunction
|
||||
|
||||
function! ale#util#ToURI(resource) abort
|
||||
let l:uri_handler = ale#uri#GetURIHandler(a:resource)
|
||||
|
||||
if l:uri_handler is# v:null
|
||||
" resource is a filesystem path
|
||||
let l:uri = ale#path#ToFileURI(a:resource)
|
||||
else
|
||||
" resource is a URI
|
||||
let l:uri = a:resource
|
||||
endif
|
||||
|
||||
return l:uri
|
||||
endfunction
|
||||
|
||||
function! ale#util#ToResource(uri) abort
|
||||
let l:uri_handler = ale#uri#GetURIHandler(a:uri)
|
||||
|
||||
if l:uri_handler is# v:null
|
||||
" resource is a filesystem path
|
||||
let l:resource = ale#path#FromFileURI(a:uri)
|
||||
else
|
||||
" resource is a URI
|
||||
let l:resource = a:uri
|
||||
endif
|
||||
|
||||
return l:resource
|
||||
endfunction
|
||||
|
||||
Reference in New Issue
Block a user