]> git.madduck.net Git - etc/vim.git/blob - autoload/ale.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.vim
1 " Author: w0rp <devw0rp@gmail.com>, David Alexander <opensource@thelonelyghost.com>
2 " Description: Primary code path for the plugin
3 "   Manages execution of linters when requested by autocommands
4
5 " Strings used for severity in the echoed message
6 let g:ale_echo_msg_error_str = get(g:, 'ale_echo_msg_error_str', 'Error')
7 let g:ale_echo_msg_info_str = get(g:, 'ale_echo_msg_info_str', 'Info')
8 let g:ale_echo_msg_log_str = get(g:, 'ale_echo_msg_log_str', 'Log')
9 let g:ale_echo_msg_warning_str = get(g:, 'ale_echo_msg_warning_str', 'Warning')
10
11 " LSP window/showMessage format
12 let g:ale_lsp_show_message_format = get(g:, 'ale_lsp_show_message_format', '%severity%:%linter%: %s')
13 " Valid values mimic LSP definitions (error, warning and information; log is
14 " never shown)
15 let g:ale_lsp_show_message_severity = get(g:, 'ale_lsp_show_message_severity', 'error')
16
17 let s:lint_timer = -1
18 let s:getcmdwintype_exists = exists('*getcmdwintype')
19
20 " Return 1 if a file is too large for ALE to handle.
21 function! ale#FileTooLarge(buffer) abort
22     let l:max = getbufvar(a:buffer, 'ale_maximum_file_size', get(g:, 'ale_maximum_file_size', 0))
23
24     return l:max > 0 ? (line2byte(line('$') + 1) > l:max) : 0
25 endfunction
26
27 " A function for checking various conditions whereby ALE just shouldn't
28 " attempt to do anything, say if particular buffer types are open in Vim.
29 function! ale#ShouldDoNothing(buffer) abort
30     " The checks are split into separate if statements to make it possible to
31     " profile each check individually with Vim's profiling tools.
32     "
33     " Do nothing if ALE is disabled.
34     if !getbufvar(a:buffer, 'ale_enabled', get(g:, 'ale_enabled', 0))
35         return 1
36     endif
37
38     " Don't perform any checks when newer NeoVim versions are exiting.
39     if get(v:, 'exiting', v:null) isnot v:null
40         return 1
41     endif
42
43     let l:filetype = getbufvar(a:buffer, '&filetype')
44
45     " Do nothing when there's no filetype.
46     if l:filetype is# ''
47         return 1
48     endif
49
50     " Do nothing for diff buffers.
51     if getbufvar(a:buffer, '&diff')
52         return 1
53     endif
54
55     " Do nothing for blacklisted files.
56     if index(get(g:, 'ale_filetype_blacklist', []), l:filetype) >= 0
57         return 1
58     endif
59
60     " Do nothing if running from command mode.
61     if s:getcmdwintype_exists && !empty(getcmdwintype())
62         return 1
63     endif
64
65     let l:filename = fnamemodify(bufname(a:buffer), ':t')
66
67     " Do nothing for directories.
68     if l:filename is# '.'
69         return 1
70     endif
71
72     " Don't start linting and so on when an operator is pending.
73     if ale#util#Mode(1) is# 'no'
74         return 1
75     endif
76
77     " Do nothing if running in the sandbox.
78     if ale#util#InSandbox()
79         return 1
80     endif
81
82     " Do nothing if the file is too large.
83     if ale#FileTooLarge(a:buffer)
84         return 1
85     endif
86
87     " Do nothing from CtrlP buffers with CtrlP-funky.
88     if exists(':CtrlPFunky') is 2
89     \&& getbufvar(a:buffer, '&l:statusline') =~# 'CtrlPMode.*funky'
90         return 1
91     endif
92
93     return 0
94 endfunction
95
96 function! s:Lint(buffer, should_lint_file, timer_id) abort
97     " Use the filetype from the buffer
98     let l:filetype = getbufvar(a:buffer, '&filetype')
99     let l:linters = ale#linter#Get(l:filetype)
100
101     let l:ignore_config = ale#Var(a:buffer, 'linters_ignore')
102     let l:disable_lsp = ale#Var(a:buffer, 'disable_lsp')
103
104     " Load code to ignore linters only if we need to.
105     if (
106     \   !empty(l:ignore_config)
107     \   || l:disable_lsp is 1
108     \   || l:disable_lsp is v:true
109     \   || (l:disable_lsp is# 'auto' && get(g:, 'lspconfig', 0))
110     \)
111         let l:linters = ale#engine#ignore#Exclude(
112         \   l:filetype,
113         \   l:linters,
114         \   l:ignore_config,
115         \   l:disable_lsp,
116         \)
117     endif
118
119     " Tell other sources that they can start checking the buffer now.
120     let g:ale_want_results_buffer = a:buffer
121     silent doautocmd <nomodeline> User ALEWantResults
122     unlet! g:ale_want_results_buffer
123
124     " Don't set up buffer data and so on if there are no linters to run.
125     if !has_key(g:ale_buffer_info, a:buffer) && empty(l:linters)
126         return
127     endif
128
129     " Clear lint_file linters, or only run them if the file exists.
130     let l:lint_file = empty(l:linters)
131     \   || (a:should_lint_file && filereadable(expand('#' . a:buffer . ':p')))
132
133     call ale#engine#RunLinters(a:buffer, l:linters, l:lint_file)
134 endfunction
135
136 " (delay, [linting_flag, buffer_number])
137 function! ale#Queue(delay, ...) abort
138     if a:0 > 2
139         throw 'too many arguments!'
140     endif
141
142     let l:buffer = get(a:000, 1, v:null)
143
144     if l:buffer is v:null
145         let l:buffer = bufnr('')
146     endif
147
148     if type(l:buffer) isnot v:t_number
149         throw 'buffer_number must be a Number'
150     endif
151
152     if ale#ShouldDoNothing(l:buffer)
153         return
154     endif
155
156     " Default linting_flag to ''
157     let l:should_lint_file = get(a:000, 0) is# 'lint_file'
158
159     if s:lint_timer != -1
160         call timer_stop(s:lint_timer)
161         let s:lint_timer = -1
162     endif
163
164     if a:delay > 0
165         let s:lint_timer = timer_start(
166         \   a:delay,
167         \   function('s:Lint', [l:buffer, l:should_lint_file])
168         \)
169     else
170         call s:Lint(l:buffer, l:should_lint_file, 0)
171     endif
172 endfunction
173
174 let s:current_ale_version = [4, 0, 0]
175
176 " A function used to check for ALE features in files outside of the project.
177 function! ale#Has(feature) abort
178     let l:match = matchlist(a:feature, '\c\v^ale-(\d+)\.(\d+)(\.(\d+))?$')
179
180     if !empty(l:match)
181         let l:version = [l:match[1] + 0, l:match[2] + 0, l:match[4] + 0]
182
183         return ale#semver#GTE(s:current_ale_version, l:version)
184     endif
185
186     return 0
187 endfunction
188
189 " Given a buffer number and a variable name, look for that variable in the
190 " buffer scope, then in global scope. If the name does not exist in the global
191 " scope, an exception will be thrown.
192 "
193 " Every variable name will be prefixed with 'ale_'.
194 function! ale#Var(buffer, variable_name) abort
195     let l:full_name = 'ale_' . a:variable_name
196     let l:vars = getbufvar(str2nr(a:buffer), '', {})
197
198     return get(l:vars, l:full_name, g:[l:full_name])
199 endfunction
200
201 " Initialize a variable with a default value, if it isn't already set.
202 "
203 " Every variable name will be prefixed with 'ale_'.
204 function! ale#Set(variable_name, default) abort
205     let l:full_name = 'ale_' . a:variable_name
206
207     if !has_key(g:, l:full_name)
208         let g:[l:full_name] = a:default
209     endif
210 endfunction
211
212 " Given a string for adding to a command, return the string padded with a
213 " space on the left if it is not empty. Otherwise return an empty string.
214 "
215 " This can be used for making command strings cleaner and easier to test.
216 function! ale#Pad(string) abort
217     return !empty(a:string) ? ' ' . a:string : ''
218 endfunction
219
220 " Given a environment variable name and a value, produce part of a command for
221 " setting an environment variable before running a command. The syntax will be
222 " valid for cmd on Windows, or most shells on Unix.
223 function! ale#Env(variable_name, value) abort
224     if has('win32')
225         return 'set ' . ale#Escape(a:variable_name . '=' . a:value) . ' && '
226     endif
227
228     return a:variable_name . '=' . ale#Escape(a:value) . ' '
229 endfunction
230
231 " Escape a string suitably for each platform.
232 " shellescape does not work on Windows.
233 function! ale#Escape(str) abort
234     if fnamemodify(&shell, ':t') is? 'cmd.exe'
235         " If the string contains spaces, it will be surrounded by quotes.
236         " Otherwise, special characters will be escaped with carets (^).
237         return substitute(
238         \   a:str =~# ' '
239         \       ?  '"' .  substitute(a:str, '"', '""', 'g') . '"'
240         \       : substitute(a:str, '\v([&|<>^])', '^\1', 'g'),
241         \   '%',
242         \   '%%',
243         \   'g',
244         \)
245     endif
246
247     return shellescape (a:str)
248 endfunction
249
250 " Get the loclist item message according to a given format string.
251 "
252 " See `:help g:ale_loclist_msg_format` and `:help g:ale_echo_msg_format`
253 function! ale#GetLocItemMessage(item, format_string) abort
254     let l:msg = a:format_string
255     let l:severity = g:ale_echo_msg_warning_str
256     let l:code = get(a:item, 'code', '')
257     let l:type = get(a:item, 'type', 'E')
258     let l:linter_name = get(a:item, 'linter_name', '')
259     let l:code_repl = !empty(l:code) ? '\=submatch(1) . l:code . submatch(2)' : ''
260
261     if l:type is# 'E'
262         let l:severity = g:ale_echo_msg_error_str
263     elseif l:type is# 'I'
264         let l:severity = g:ale_echo_msg_info_str
265     endif
266
267     " Replace special markers with certain information.
268     " \=l:variable is used to avoid escaping issues.
269     let l:msg = substitute(l:msg, '\v\%([^\%]*)code([^\%]*)\%', l:code_repl, 'g')
270     let l:msg = substitute(l:msg, '\V%severity%', '\=l:severity', 'g')
271     let l:msg = substitute(l:msg, '\V%type%', '\=l:type', 'g')
272     let l:msg = substitute(l:msg, '\V%linter%', '\=l:linter_name', 'g')
273     " Replace %s with the text.
274     let l:msg = substitute(l:msg, '\V%s', '\=a:item.text', 'g')
275     " Windows may insert carriage return line endings (^M), strip these characters.
276     let l:msg = substitute(l:msg, '\r', '', 'g')
277
278     return l:msg
279 endfunction
280
281 " Given a buffer and a linter or fixer name, return an Array of two-item
282 " Arrays describing how to map filenames to and from the local to foreign file
283 " systems.
284 function! ale#GetFilenameMappings(buffer, name) abort
285     let l:linter_mappings = ale#Var(a:buffer, 'filename_mappings')
286
287     if type(l:linter_mappings) is v:t_list
288         return l:linter_mappings
289     endif
290
291     let l:name = a:name
292
293     if !has_key(l:linter_mappings, l:name)
294         " Use * as a default setting for all tools.
295         let l:name = '*'
296     endif
297
298     return get(l:linter_mappings, l:name, [])
299 endfunction