]> git.madduck.net Git - etc/vim.git/blob - autoload/ale/debugging.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 / debugging.vim
1 " Author: w0rp <devw0rp@gmail.com>
2 " Description: This file implements debugging information for ALE
3
4 let g:ale_info_default_mode = get(g:, 'ale_info_default_mode', 'preview')
5
6 let s:global_variable_list = [
7 \    'ale_cache_executable_check_failures',
8 \    'ale_change_sign_column_color',
9 \    'ale_command_wrapper',
10 \    'ale_completion_delay',
11 \    'ale_completion_enabled',
12 \    'ale_completion_max_suggestions',
13 \    'ale_disable_lsp',
14 \    'ale_echo_cursor',
15 \    'ale_echo_msg_error_str',
16 \    'ale_echo_msg_format',
17 \    'ale_echo_msg_info_str',
18 \    'ale_echo_msg_warning_str',
19 \    'ale_enabled',
20 \    'ale_fix_on_save',
21 \    'ale_fixers',
22 \    'ale_history_enabled',
23 \    'ale_info_default_mode',
24 \    'ale_history_log_output',
25 \    'ale_keep_list_window_open',
26 \    'ale_lint_delay',
27 \    'ale_lint_on_enter',
28 \    'ale_lint_on_filetype_changed',
29 \    'ale_lint_on_insert_leave',
30 \    'ale_lint_on_save',
31 \    'ale_lint_on_text_changed',
32 \    'ale_linter_aliases',
33 \    'ale_linters',
34 \    'ale_linters_explicit',
35 \    'ale_linters_ignore',
36 \    'ale_list_vertical',
37 \    'ale_list_window_size',
38 \    'ale_loclist_msg_format',
39 \    'ale_max_buffer_history_size',
40 \    'ale_max_signs',
41 \    'ale_maximum_file_size',
42 \    'ale_open_list',
43 \    'ale_pattern_options',
44 \    'ale_pattern_options_enabled',
45 \    'ale_root',
46 \    'ale_set_balloons',
47 \    'ale_set_highlights',
48 \    'ale_set_loclist',
49 \    'ale_set_quickfix',
50 \    'ale_set_signs',
51 \    'ale_sign_column_always',
52 \    'ale_sign_error',
53 \    'ale_sign_info',
54 \    'ale_sign_offset',
55 \    'ale_sign_style_error',
56 \    'ale_sign_style_warning',
57 \    'ale_sign_warning',
58 \    'ale_sign_highlight_linenrs',
59 \    'ale_type_map',
60 \    'ale_use_neovim_diagnostics_api',
61 \    'ale_use_global_executables',
62 \    'ale_virtualtext_cursor',
63 \    'ale_warn_about_trailing_blank_lines',
64 \    'ale_warn_about_trailing_whitespace',
65 \]
66
67 function! s:Echo(message) abort
68     " no-custom-checks
69     echo a:message
70 endfunction
71
72 function! s:GetLinterVariables(filetype, exclude_linter_names) abort
73     let l:variable_list = []
74     let l:filetype_parts = split(a:filetype, '\.')
75
76     for l:key in keys(g:)
77         " Extract variable names like: 'ale_python_flake8_executable'
78         let l:match = matchlist(l:key, '\v^ale_([^_]+)_([^_]+)_.+$')
79
80         " Include matching variables.
81         if !empty(l:match)
82         \&& index(l:filetype_parts, l:match[1]) >= 0
83         \&& index(a:exclude_linter_names, l:match[2]) == -1
84             call add(l:variable_list, l:key)
85         endif
86     endfor
87
88     call sort(l:variable_list)
89
90     return l:variable_list
91 endfunction
92
93 function! s:EchoLinterVariables(variable_list) abort
94     for l:key in a:variable_list
95         call s:Echo('let g:' . l:key . ' = ' . string(g:[l:key]))
96
97         if has_key(b:, l:key)
98             call s:Echo('let b:' . l:key . ' = ' . string(b:[l:key]))
99         endif
100     endfor
101 endfunction
102
103 function! s:EchoGlobalVariables() abort
104     for l:key in s:global_variable_list
105         call s:Echo('let g:' . l:key . ' = ' . string(get(g:, l:key, v:null)))
106
107         if has_key(b:, l:key)
108             call s:Echo('let b:' . l:key . ' = ' . string(b:[l:key]))
109         endif
110     endfor
111 endfunction
112
113 " Echo a command that was run.
114 function! s:EchoCommand(item) abort
115     let l:status_message = a:item.status
116
117     " Include the exit code in output if we have it.
118     if a:item.status is# 'finished'
119         let l:status_message .= ' - exit code ' . a:item.exit_code
120     endif
121
122     call s:Echo('(' . l:status_message . ') ' . string(a:item.command))
123
124     if g:ale_history_log_output && has_key(a:item, 'output')
125         if empty(a:item.output)
126             call s:Echo('')
127             call s:Echo('<<<NO OUTPUT RETURNED>>>')
128             call s:Echo('')
129         else
130             call s:Echo('')
131             call s:Echo('<<<OUTPUT STARTS>>>')
132
133             for l:line in a:item.output
134                 call s:Echo(l:line)
135             endfor
136
137             call s:Echo('<<<OUTPUT ENDS>>>')
138             call s:Echo('')
139         endif
140     endif
141 endfunction
142
143 " Echo the results of an executable check.
144 function! s:EchoExecutable(item) abort
145     call s:Echo(printf(
146     \   '(executable check - %s) %s',
147     \   a:item.status ? 'success' : 'failure',
148     \   a:item.command,
149     \))
150 endfunction
151
152 function! s:EchoCommandHistory() abort
153     let l:buffer = bufnr('%')
154
155     for l:item in ale#history#Get(l:buffer)
156         if l:item.job_id is# 'executable'
157             call s:EchoExecutable(l:item)
158         else
159             call s:EchoCommand(l:item)
160         endif
161     endfor
162 endfunction
163
164 function! s:EchoLinterAliases(all_linters) abort
165     let l:first = 1
166
167     for l:linter in a:all_linters
168         if !empty(l:linter.aliases)
169             if l:first
170                 call s:Echo('   Linter Aliases:')
171             endif
172
173             let l:first = 0
174
175             call s:Echo(string(l:linter.name) . ' -> ' . string(l:linter.aliases))
176         endif
177     endfor
178 endfunction
179
180 function! s:EchoLSPErrorMessages(all_linter_names) abort
181     let l:lsp_error_messages = get(g:, 'ale_lsp_error_messages', {})
182     let l:header_echoed = 0
183
184     for l:linter_name in a:all_linter_names
185         let l:error_list = get(l:lsp_error_messages, l:linter_name, [])
186
187         if !empty(l:error_list)
188             if !l:header_echoed
189                 call s:Echo(' LSP Error Messages:')
190                 call s:Echo('')
191             endif
192
193             call s:Echo('(Errors for ' . l:linter_name . ')')
194
195             for l:message in l:error_list
196                 for l:line in split(l:message, "\n")
197                     call s:Echo(l:line)
198                 endfor
199             endfor
200         endif
201     endfor
202 endfunction
203
204 function! s:GetIgnoredLinters(buffer, enabled_linters) abort
205     let l:filetype = &filetype
206     let l:ignore_config = ale#Var(a:buffer, 'linters_ignore')
207     let l:disable_lsp = ale#Var(a:buffer, 'disable_lsp')
208
209     if (
210     \   !empty(l:ignore_config)
211     \   || l:disable_lsp is 1
212     \   || l:disable_lsp is v:true
213     \   || (l:disable_lsp is# 'auto' && get(g:, 'lspconfig', 0))
214     \)
215         let l:non_ignored = ale#engine#ignore#Exclude(
216         \   l:filetype,
217         \   a:enabled_linters,
218         \   l:ignore_config,
219         \   l:disable_lsp,
220         \)
221     else
222         let l:non_ignored = copy(a:enabled_linters)
223     endif
224
225     call map(l:non_ignored, 'v:val.name')
226
227     return filter(
228     \   copy(a:enabled_linters),
229     \   'index(l:non_ignored, v:val.name) < 0'
230     \)
231 endfunction
232
233 function! ale#debugging#Info(...) abort
234     let l:options = (a:0 > 0) ? a:1 : {}
235     let l:show_preview_info = get(l:options, 'preview')
236
237     let l:buffer = bufnr('')
238     let l:filetype = &filetype
239
240     let l:enabled_linters = deepcopy(ale#linter#Get(l:filetype))
241
242     " But have to build the list of available linters ourselves.
243     let l:all_linters = []
244     let l:linter_variable_list = []
245
246     for l:part in split(l:filetype, '\.')
247         let l:aliased_filetype = ale#linter#ResolveFiletype(l:part)
248         call extend(l:all_linters, ale#linter#GetAll(l:aliased_filetype))
249     endfor
250
251     let l:all_names = map(copy(l:all_linters), 'v:val[''name'']')
252     let l:enabled_names = map(copy(l:enabled_linters), 'v:val[''name'']')
253     let l:exclude_names = filter(copy(l:all_names), 'index(l:enabled_names, v:val) == -1')
254
255     " Load linter variables to display
256     " This must be done after linters are loaded.
257     let l:variable_list = s:GetLinterVariables(l:filetype, l:exclude_names)
258
259     let l:fixers = ale#fix#registry#SuggestedFixers(l:filetype)
260     let l:fixers = uniq(sort(l:fixers[0] + l:fixers[1]))
261     let l:fixers_string = join(map(copy(l:fixers), '"\n  " . v:val'), '')
262
263     " Get the names of ignored linters.
264     let l:ignored_names = map(
265     \   s:GetIgnoredLinters(l:buffer, l:enabled_linters),
266     \   'v:val.name'
267     \)
268
269     call s:Echo(' Current Filetype: ' . l:filetype)
270     call s:Echo('Available Linters: ' . string(l:all_names))
271     call s:EchoLinterAliases(l:all_linters)
272     call s:Echo('  Enabled Linters: ' . string(l:enabled_names))
273     call s:Echo('  Ignored Linters: ' . string(l:ignored_names))
274     call s:Echo(' Suggested Fixers:' . l:fixers_string)
275     " We use this line with only a space to know where to end highlights.
276     call s:Echo(' ')
277
278     " Only show Linter Variables directive if there are any.
279     if !empty(l:variable_list)
280         call s:Echo(' Linter Variables:')
281
282         if l:show_preview_info
283             call s:Echo('" Press Space to read :help for a setting')
284         endif
285
286         call s:EchoLinterVariables(l:variable_list)
287         " We use this line with only a space to know where to end highlights.
288         call s:Echo(' ')
289     endif
290
291     call s:Echo(' Global Variables:')
292
293     if l:show_preview_info
294         call s:Echo('" Press Space to read :help for a setting')
295     endif
296
297     call s:EchoGlobalVariables()
298     call s:Echo(' ')
299     call s:EchoLSPErrorMessages(l:all_names)
300     call s:Echo('  Command History:')
301     call s:Echo('')
302     call s:EchoCommandHistory()
303 endfunction
304
305 function! ale#debugging#InfoToClipboard() abort
306     if !has('clipboard')
307         call s:Echo('clipboard not available. Try :ALEInfoToFile instead.')
308
309         return
310     endif
311
312     let l:output = execute('call ale#debugging#Info()')
313
314     let @+ = l:output
315     call s:Echo('ALEInfo copied to your clipboard')
316 endfunction
317
318 function! ale#debugging#InfoToFile(filename) abort
319     let l:expanded_filename = expand(a:filename)
320
321     let l:output = execute('call ale#debugging#Info()')
322
323     call writefile(split(l:output, "\n"), l:expanded_filename)
324     call s:Echo('ALEInfo written to ' . l:expanded_filename)
325 endfunction
326
327 function! ale#debugging#InfoToPreview() abort
328     let l:output = execute('call ale#debugging#Info({''preview'': 1})')
329
330     call ale#preview#Show(split(l:output, "\n"), {
331     \   'filetype': 'ale-info',
332     \})
333 endfunction
334
335 function! ale#debugging#InfoCommand(...) abort
336     if len(a:000) > 1
337         " no-custom-checks
338         echom 'Invalid ALEInfo arguments!'
339
340         return
341     endif
342
343     " Do not show info for the info window itself.
344     if &filetype is# 'ale-info'
345         return
346     endif
347
348     " Get 'echo' from '-echo', if there's an argument.
349     let l:mode = get(a:000, '')[1:]
350
351     if empty(l:mode)
352         let l:mode = ale#Var(bufnr(''), 'info_default_mode')
353     endif
354
355     if l:mode is# 'echo'
356         call ale#debugging#Info()
357     elseif l:mode is# 'clip' || l:mode is# 'clipboard'
358         call ale#debugging#InfoToClipboard()
359     else
360         call ale#debugging#InfoToPreview()
361     endif
362 endfunction
363
364 function! ale#debugging#InfoToClipboardDeprecatedCommand() abort
365     " no-custom-checks
366     echom 'ALEInfoToClipboard is deprecated. Use ALEInfo -clipboard instead.'
367     call ale#debugging#InfoToClipboard()
368 endfunction