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.
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
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')
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
15 let g:ale_lsp_show_message_severity = get(g:, 'ale_lsp_show_message_severity', 'error')
18 let s:getcmdwintype_exists = exists('*getcmdwintype')
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))
24 return l:max > 0 ? (line2byte(line('$') + 1) > l:max) : 0
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.
33 " Do nothing if ALE is disabled.
34 if !getbufvar(a:buffer, 'ale_enabled', get(g:, 'ale_enabled', 0))
38 " Don't perform any checks when newer NeoVim versions are exiting.
39 if get(v:, 'exiting', v:null) isnot v:null
43 let l:filetype = getbufvar(a:buffer, '&filetype')
45 " Do nothing when there's no filetype.
50 " Do nothing for diff buffers.
51 if getbufvar(a:buffer, '&diff')
55 " Do nothing for blacklisted files.
56 if index(get(g:, 'ale_filetype_blacklist', []), l:filetype) >= 0
60 " Do nothing if running from command mode.
61 if s:getcmdwintype_exists && !empty(getcmdwintype())
65 let l:filename = fnamemodify(bufname(a:buffer), ':t')
67 " Do nothing for directories.
72 " Don't start linting and so on when an operator is pending.
73 if ale#util#Mode(1) is# 'no'
77 " Do nothing if running in the sandbox.
78 if ale#util#InSandbox()
82 " Do nothing if the file is too large.
83 if ale#FileTooLarge(a:buffer)
87 " Do nothing from CtrlP buffers with CtrlP-funky.
88 if exists(':CtrlPFunky') is 2
89 \&& getbufvar(a:buffer, '&l:statusline') =~# 'CtrlPMode.*funky'
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)
101 let l:ignore_config = ale#Var(a:buffer, 'linters_ignore')
102 let l:disable_lsp = ale#Var(a:buffer, 'disable_lsp')
104 " Load code to ignore linters only if we need to.
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))
111 let l:linters = ale#engine#ignore#Exclude(
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
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)
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')))
133 call ale#engine#RunLinters(a:buffer, l:linters, l:lint_file)
136 " (delay, [linting_flag, buffer_number])
137 function! ale#Queue(delay, ...) abort
139 throw 'too many arguments!'
142 let l:buffer = get(a:000, 1, v:null)
144 if l:buffer is v:null
145 let l:buffer = bufnr('')
148 if type(l:buffer) isnot v:t_number
149 throw 'buffer_number must be a Number'
152 if ale#ShouldDoNothing(l:buffer)
156 " Default linting_flag to ''
157 let l:should_lint_file = get(a:000, 0) is# 'lint_file'
159 if s:lint_timer != -1
160 call timer_stop(s:lint_timer)
161 let s:lint_timer = -1
165 let s:lint_timer = timer_start(
167 \ function('s:Lint', [l:buffer, l:should_lint_file])
170 call s:Lint(l:buffer, l:should_lint_file, 0)
174 let s:current_ale_version = [4, 0, 0]
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+))?$')
181 let l:version = [l:match[1] + 0, l:match[2] + 0, l:match[4] + 0]
183 return ale#semver#GTE(s:current_ale_version, l:version)
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.
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), '', {})
198 return get(l:vars, l:full_name, g:[l:full_name])
201 " Initialize a variable with a default value, if it isn't already set.
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
207 if !has_key(g:, l:full_name)
208 let g:[l:full_name] = a:default
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.
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 : ''
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
225 return 'set ' . ale#Escape(a:variable_name . '=' . a:value) . ' && '
228 return a:variable_name . '=' . ale#Escape(a:value) . ' '
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 (^).
239 \ ? '"' . substitute(a:str, '"', '""', 'g') . '"'
240 \ : substitute(a:str, '\v([&|<>^])', '^\1', 'g'),
247 return shellescape (a:str)
250 " Get the loclist item message according to a given format string.
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)' : ''
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
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')
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
284 function! ale#GetFilenameMappings(buffer, name) abort
285 let l:linter_mappings = ale#Var(a:buffer, 'filename_mappings')
287 if type(l:linter_mappings) is v:t_list
288 return l:linter_mappings
293 if !has_key(l:linter_mappings, l:name)
294 " Use * as a default setting for all tools.
298 return get(l:linter_mappings, l:name, [])