1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
|
" Author: w0rp <devw0rp@gmail.com>, David Alexander <opensource@thelonelyghost.com>
" Description: Primary code path for the plugin
" Manages execution of linters when requested by autocommands
let s:lint_timer = -1
let s:queued_buffer_number = -1
let s:should_lint_file_for_buffer = {}
let s:error_delay_ms = 1000 * 60 * 2
let s:timestamp_map = {}
" Given a key for a script variable for tracking the time to wait until
" a given function should be called, a funcref for a function to call, and
" a List of arguments, call the function and return whatever value it returns.
"
" If the function throws an exception, then the function will not be called
" for a while, and 0 will be returned instead.
function! ale#CallWithCooldown(timestamp_key, func, arglist) abort
let l:now = ale#util#ClockMilliseconds()
if l:now < get(s:timestamp_map, a:timestamp_key, -1)
return 0
endif
let s:timestamp_map[a:timestamp_key] = l:now + s:error_delay_ms
let l:return_value = call(a:func, a:arglist)
let s:timestamp_map[a:timestamp_key] = -1
return l:return_value
endfunction
" Return 1 if a file is too large for ALE to handle.
function! ale#FileTooLarge() abort
let l:max = ale#Var(bufnr(''), 'maximum_file_size')
return l:max > 0 ? (line2byte(line('$') + 1) > l:max) : 0
endfunction
" A function for checking various conditions whereby ALE just shouldn't
" attempt to do anything, say if particular buffer types are open in Vim.
function! ale#ShouldDoNothing(buffer) abort
" Do nothing for blacklisted files
" OR if ALE is running in the sandbox
return index(g:ale_filetype_blacklist, &filetype) >= 0
\ || (exists('*getcmdwintype') && !empty(getcmdwintype()))
\ || ale#util#InSandbox()
\ || !ale#Var(a:buffer, 'enabled')
\ || ale#FileTooLarge()
\ || getbufvar(a:buffer, '&l:statusline') =~# 'CtrlPMode.*funky'
endfunction
" (delay, [linting_flag, buffer_number])
function! ale#Queue(delay, ...) abort
if a:0 > 2
throw 'too many arguments!'
endif
" Default linting_flag to ''
let l:linting_flag = get(a:000, 0, '')
let l:buffer = get(a:000, 1, bufnr(''))
return ale#CallWithCooldown(
\ 'dont_queue_until',
\ function('s:ALEQueueImpl'),
\ [a:delay, l:linting_flag, l:buffer],
\)
endfunction
function! s:ALEQueueImpl(delay, linting_flag, buffer) abort
if a:linting_flag isnot# '' && a:linting_flag isnot# 'lint_file'
throw "linting_flag must be either '' or 'lint_file'"
endif
if type(a:buffer) != type(0)
throw 'buffer_number must be a Number'
endif
if ale#ShouldDoNothing(a:buffer)
return
endif
" Remember that we want to check files for this buffer.
" We will remember this until we finally run the linters, via any event.
if a:linting_flag is# 'lint_file'
let s:should_lint_file_for_buffer[bufnr('%')] = 1
endif
if s:lint_timer != -1
call timer_stop(s:lint_timer)
let s:lint_timer = -1
endif
let l:linters = ale#linter#Get(getbufvar(a:buffer, '&filetype'))
" Don't set up buffer data and so on if there are no linters to run.
if empty(l:linters)
" If we have some previous buffer data, then stop any jobs currently
" running and clear everything.
if has_key(g:ale_buffer_info, a:buffer)
call ale#engine#RunLinters(a:buffer, [], 1)
endif
return
endif
if a:delay > 0
let s:queued_buffer_number = a:buffer
let s:lint_timer = timer_start(a:delay, function('ale#Lint'))
else
call ale#Lint(-1, a:buffer)
endif
endfunction
function! ale#Lint(...) abort
if a:0 > 1
" Use the buffer number given as the optional second argument.
let l:buffer = a:2
elseif a:0 > 0 && a:1 == s:lint_timer
" Use the buffer number for the buffer linting was queued for.
let l:buffer = s:queued_buffer_number
else
" Use the current buffer number.
let l:buffer = bufnr('')
endif
return ale#CallWithCooldown(
\ 'dont_lint_until',
\ function('s:ALELintImpl'),
\ [l:buffer],
\)
endfunction
function! s:ALELintImpl(buffer) abort
if ale#ShouldDoNothing(a:buffer)
return
endif
" Use the filetype from the buffer
let l:linters = ale#linter#Get(getbufvar(a:buffer, '&filetype'))
let l:should_lint_file = 0
" Check if we previously requested checking the file.
if has_key(s:should_lint_file_for_buffer, a:buffer)
unlet s:should_lint_file_for_buffer[a:buffer]
" Lint files if they exist.
let l:should_lint_file = filereadable(expand('#' . a:buffer . ':p'))
endif
call ale#engine#RunLinters(a:buffer, l:linters, l:should_lint_file)
endfunction
" Reset flags indicating that files should be checked for all buffers.
function! ale#ResetLintFileMarkers() abort
let s:should_lint_file_for_buffer = {}
endfunction
function! ale#ResetErrorDelays() abort
let s:timestamp_map = {}
endfunction
let g:ale_has_override = get(g:, 'ale_has_override', {})
" Call has(), but check a global Dictionary so we can force flags on or off
" for testing purposes.
function! ale#Has(feature) abort
return get(g:ale_has_override, a:feature, has(a:feature))
endfunction
" Given a buffer number and a variable name, look for that variable in the
" buffer scope, then in global scope. If the name does not exist in the global
" scope, an exception will be thrown.
"
" Every variable name will be prefixed with 'ale_'.
function! ale#Var(buffer, variable_name) abort
let l:nr = str2nr(a:buffer)
let l:full_name = 'ale_' . a:variable_name
if bufexists(l:nr)
let l:vars = getbufvar(l:nr, '')
elseif has_key(g:, 'ale_fix_buffer_data')
let l:vars = get(g:ale_fix_buffer_data, l:nr, {'vars': {}}).vars
else
let l:vars = {}
endif
return get(l:vars, l:full_name, g:[l:full_name])
endfunction
" Initialize a variable with a default value, if it isn't already set.
"
" Every variable name will be prefixed with 'ale_'.
function! ale#Set(variable_name, default) abort
let l:full_name = 'ale_' . a:variable_name
let l:value = get(g:, l:full_name, a:default)
let g:[l:full_name] = l:value
return l:value
endfunction
" Escape a string suitably for each platform.
" shellescape does not work on Windows.
function! ale#Escape(str) abort
if fnamemodify(&shell, ':t') is? 'cmd.exe'
" If the string contains spaces, it will be surrounded by quotes.
" Otherwise, special characters will be escaped with carets (^).
return substitute(
\ a:str =~# ' '
\ ? '"' . substitute(a:str, '"', '""', 'g') . '"'
\ : substitute(a:str, '\v([&|<>^])', '^\1', 'g'),
\ '%',
\ '%%',
\ 'g',
\)
endif
return shellescape (a:str)
endfunction
|