]> git.madduck.net Git - etc/vim.git/blob - autoload/lsp/tag.vim

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Squashed '.vim/bundle/vim-lsp/' content from commit 04428c92
[etc/vim.git] / autoload / lsp / tag.vim
1 let s:tag_kind_priority = ['definition', 'declaration', 'implementation', 'type definition']
2
3 function! s:not_supported(what) abort
4     call lsp#log(a:what . ' not supported for ' . &filetype)
5 endfunction
6
7 function! s:location_to_tag(loc) abort
8     if has_key(a:loc, 'targetUri')
9         let l:uri = a:loc['targetUri']
10         let l:range = a:loc['targetRange']
11     else
12         let l:uri = a:loc['uri']
13         let l:range = a:loc['range']
14     endif
15
16     if !lsp#utils#is_file_uri(l:uri)
17         return v:null
18     endif
19
20     let l:path = lsp#utils#uri_to_path(l:uri)
21     let [l:line, l:col] = lsp#utils#position#lsp_to_vim(l:path, l:range['start'])
22     return {
23         \ 'filename': l:path,
24         \ 'cmd': printf('/\%%%dl\%%%dc/', l:line, l:col)
25         \ }
26 endfunction
27
28 function! s:handle_locations(ctx, server, type, data) abort
29     try
30         if lsp#client#is_error(a:data['response']) || !has_key(a:data['response'], 'result')
31             call lsp#utils#error('Failed to retrieve ' . a:type . ' for ' . a:server . ': ' . lsp#client#error_message(a:data['response']))
32             return
33         endif
34
35         let l:list = a:ctx['list']
36         let l:result = a:data['response']['result']
37         if type(l:result) != type([])
38             let l:result = [l:result]
39         endif
40         for l:loc in l:result
41             let l:tag = s:location_to_tag(l:loc)
42             if !empty(l:tag)
43                 call add(l:list, extend(l:tag, { 'name': a:ctx['pattern'], 'kind': a:type }))
44             endif
45         endfor
46     finally
47         let a:ctx['counter'] -= 1
48     endtry
49 endfunction
50
51 function! s:handle_symbols(ctx, server, data) abort
52     try
53         if lsp#client#is_error(a:data['response']) || !has_key(a:data['response'], 'result')
54             call lsp#utils#error('Failed to retrieve workspace symbols for ' . a:server . ': ' . lsp#client#error_message(a:data['response']))
55             return
56         endif
57
58         let l:list = a:ctx['list']
59         for l:symbol in a:data['response']['result']
60             let l:tag = s:location_to_tag(l:symbol['location'])
61             if empty(l:tag)
62                 continue
63             endif
64
65             let l:tag['name'] = l:symbol['name']
66             if has_key(l:symbol, 'kind')
67                 let l:tag['kind'] = lsp#ui#vim#utils#_get_symbol_text_from_kind(a:server, l:symbol['kind'])
68             endif
69             call add(l:list, l:tag)
70         endfor
71     finally
72         let a:ctx['counter'] -= 1
73     endtry
74 endfunction
75
76 function! s:tag_view_sub(ctx, method, params) abort
77     let l:operation = substitute(a:method, '\u', ' \l\0', 'g')
78
79     let l:capabilities_func = printf('lsp#capabilities#has_%s_provider(v:val)', substitute(l:operation, ' ', '_', 'g'))
80     let l:servers = filter(lsp#get_allowed_servers(), l:capabilities_func)
81     if empty(l:servers)
82         call s:not_supported('retrieving ' . l:operation)
83         return v:false
84     endif
85
86     let a:ctx['counter'] += len(l:servers)
87     for l:server in l:servers
88         call lsp#send_request(l:server, {
89             \ 'method': 'textDocument/'.a:method,
90             \ 'params': a:params,
91             \ 'on_notification': function('s:handle_locations', [a:ctx, l:server, l:operation])
92             \})
93     endfor
94     return v:true
95 endfunction
96
97 function! s:tag_view(ctx) abort
98     let l:params = {
99         \ 'textDocument': lsp#get_text_document_identifier(),
100         \ 'position': lsp#get_position(),
101         \ }
102     return !empty(filter(copy(g:lsp_tagfunc_source_methods),
103         \ {_, m -> s:tag_view_sub(a:ctx, m, l:params)}))
104 endfunction
105
106 function! s:tag_search(ctx) abort
107     let l:servers = filter(lsp#get_allowed_servers(), 'lsp#capabilities#has_workspace_symbol_provider(v:val)')
108     if empty(l:servers)
109         call s:not_supported('retrieving workspace symbols')
110         return v:false
111     endif
112
113     let a:ctx['counter'] = len(l:servers)
114     for l:server in l:servers
115         call lsp#send_request(l:server, {
116             \ 'method': 'workspace/symbol',
117             \ 'params': { 'query': a:ctx['pattern'] },
118             \ 'on_notification': function('s:handle_symbols', [a:ctx, l:server])
119             \ })
120     endfor
121     return v:true
122 endfunction
123
124 function! s:compare_tags(path, a, b) abort
125     " TODO: custom tag sorting, maybe?
126     if a:a['filename'] !=# a:b['filename']
127         if a:a['filename'] ==# a:path
128             return -1
129         elseif a:b['filename'] ==# a:path
130             return 1
131         endif
132     endif
133     let l:rank_a = index(s:tag_kind_priority, get(a:a, 'kind', ''))
134     let l:rank_b = index(s:tag_kind_priority, get(a:b, 'kind', ''))
135     if l:rank_a != l:rank_b
136         return l:rank_a < l:rank_b ? -1 : 1
137     endif
138     if a:a['filename'] !=# a:b['filename']
139         return a:a['filename'] <# a:b['filename'] ? -1 : 1
140     endif
141     return str2nr(a:a['cmd']) - str2nr(a:b['cmd'])
142 endfunction
143
144 function! lsp#tag#tagfunc(pattern, flags, info) abort
145     if stridx(a:flags, 'i') >= 0
146         return v:null
147     endif
148
149     let l:ctx = { 'pattern': a:pattern, 'counter': 0, 'list': [] }
150     if !(stridx(a:flags, 'c') >= 0 ? s:tag_view(l:ctx) : s:tag_search(l:ctx))
151         " No supported methods so use builtin tag source
152         return v:null
153     endif
154     call lsp#utils#_wait(-1, {-> l:ctx['counter'] == 0}, 50)
155     call sort(l:ctx['list'], function('s:compare_tags', [a:info['buf_ffname']]))
156     return l:ctx['list']
157 endfunction