]> git.madduck.net Git - etc/vim.git/blob - autoload/ale/rename.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/ale/' content from commit 22185c4c
[etc/vim.git] / autoload / ale / rename.vim
1 " Author: Jerko Steiner <jerko.steiner@gmail.com>
2 " Description: Rename symbol support for LSP / tsserver
3
4 let s:rename_map = {}
5
6 " Used to get the rename map in tests.
7 function! ale#rename#GetMap() abort
8     return deepcopy(s:rename_map)
9 endfunction
10
11 " Used to set the rename map in tests.
12 function! ale#rename#SetMap(map) abort
13     let s:rename_map = a:map
14 endfunction
15
16 function! ale#rename#ClearLSPData() abort
17     let s:rename_map = {}
18 endfunction
19
20 let g:ale_rename_tsserver_find_in_comments = get(g:, 'ale_rename_tsserver_find_in_comments', v:false)
21 let g:ale_rename_tsserver_find_in_strings = get(g:, 'ale_rename_tsserver_find_in_strings', v:false)
22
23 function! s:message(message) abort
24     call ale#util#Execute('echom ' . string(a:message))
25 endfunction
26
27 function! ale#rename#HandleTSServerResponse(conn_id, response) abort
28     if get(a:response, 'command', '') isnot# 'rename'
29         return
30     endif
31
32     if !has_key(s:rename_map, a:response.request_seq)
33         return
34     endif
35
36     let l:options = remove(s:rename_map, a:response.request_seq)
37
38     let l:old_name = l:options.old_name
39     let l:new_name = l:options.new_name
40
41     if get(a:response, 'success', v:false) is v:false
42         let l:message = get(a:response, 'message', 'unknown')
43         call s:message('Error renaming "' . l:old_name . '" to: "' . l:new_name
44         \ . '". Reason: ' . l:message)
45
46         return
47     endif
48
49     let l:changes = []
50
51     for l:response_item in a:response.body.locs
52         let l:filename = l:response_item.file
53         let l:text_changes = []
54
55         for l:loc in l:response_item.locs
56             call add(l:text_changes, {
57             \ 'start': {
58             \   'line': l:loc.start.line,
59             \   'offset': l:loc.start.offset,
60             \ },
61             \ 'end': {
62             \   'line': l:loc.end.line,
63             \   'offset': l:loc.end.offset,
64             \ },
65             \ 'newText': l:new_name,
66             \})
67         endfor
68
69         call add(l:changes, {
70         \   'fileName': l:filename,
71         \   'textChanges': l:text_changes,
72         \})
73     endfor
74
75     if empty(l:changes)
76         call s:message('Error renaming "' . l:old_name . '" to: "' . l:new_name . '"')
77
78         return
79     endif
80
81     call ale#code_action#HandleCodeAction(
82     \   {
83     \       'description': 'rename',
84     \       'changes': l:changes,
85     \   },
86     \   {
87     \       'conn_id': a:conn_id,
88     \       'should_save': g:ale_save_hidden || !&hidden,
89     \   },
90     \)
91 endfunction
92
93 function! ale#rename#HandleLSPResponse(conn_id, response) abort
94     if has_key(a:response, 'id')
95     \&& has_key(s:rename_map, a:response.id)
96         let l:options = remove(s:rename_map, a:response.id)
97
98         if !has_key(a:response, 'result')
99             call s:message('No rename result received from server')
100
101             return
102         endif
103
104         let l:changes_map = ale#code_action#GetChanges(a:response.result)
105
106         if empty(l:changes_map)
107             call s:message('No changes received from server')
108
109             return
110         endif
111
112         let l:changes = ale#code_action#BuildChangesList(l:changes_map)
113
114         call ale#code_action#HandleCodeAction(
115         \   {
116         \       'description': 'rename',
117         \       'changes': l:changes,
118         \   },
119         \   {
120         \       'conn_id': a:conn_id,
121         \       'should_save': g:ale_save_hidden || !&hidden,
122         \   },
123         \)
124     endif
125 endfunction
126
127 function! s:OnReady(line, column, options, linter, lsp_details) abort
128     let l:id = a:lsp_details.connection_id
129
130     if !ale#lsp#HasCapability(l:id, 'rename')
131         return
132     endif
133
134     let l:buffer = a:lsp_details.buffer
135
136     let l:Callback = a:linter.lsp is# 'tsserver'
137     \   ? function('ale#rename#HandleTSServerResponse')
138     \   : function('ale#rename#HandleLSPResponse')
139
140     call ale#lsp#RegisterCallback(l:id, l:Callback)
141
142     if a:linter.lsp is# 'tsserver'
143         let l:message = ale#lsp#tsserver_message#Rename(
144         \   l:buffer,
145         \   a:line,
146         \   a:column,
147         \   g:ale_rename_tsserver_find_in_comments,
148         \   g:ale_rename_tsserver_find_in_strings,
149         \)
150     else
151         " Send a message saying the buffer has changed first, or the
152         " rename position probably won't make sense.
153         call ale#lsp#NotifyForChanges(l:id, l:buffer)
154
155         let l:message = ale#lsp#message#Rename(
156         \   l:buffer,
157         \   a:line,
158         \   a:column,
159         \   a:options.new_name
160         \)
161     endif
162
163     let l:request_id = ale#lsp#Send(l:id, l:message)
164
165     let s:rename_map[l:request_id] = a:options
166 endfunction
167
168 function! s:ExecuteRename(linter, options) abort
169     let l:buffer = bufnr('')
170     let [l:line, l:column] = getpos('.')[1:2]
171
172     if a:linter.lsp isnot# 'tsserver'
173         let l:column = min([l:column, len(getline(l:line))])
174     endif
175
176     let l:Callback = function('s:OnReady', [l:line, l:column, a:options])
177     call ale#lsp_linter#StartLSP(l:buffer, a:linter, l:Callback)
178 endfunction
179
180 function! ale#rename#Execute() abort
181     let l:linters = ale#lsp_linter#GetEnabled(bufnr(''))
182
183     if empty(l:linters)
184         call s:message('No active LSPs')
185
186         return
187     endif
188
189     let l:old_name = expand('<cword>')
190     let l:new_name = ale#util#Input('New name: ', l:old_name)
191
192     if empty(l:new_name)
193         call s:message('New name cannot be empty!')
194
195         return
196     endif
197
198     for l:linter in l:linters
199         call s:ExecuteRename(l:linter, {
200         \   'old_name': l:old_name,
201         \   'new_name': l:new_name,
202         \})
203     endfor
204 endfunction