]>
Commit | Line | Data |
---|---|---|
1 | ||
2 | " Global Options | |
3 | " | |
4 | " Enable/Disable highlighting of errors in source. | |
5 | " Default is Enable | |
6 | " To disable the highlighting put the line | |
7 | " let g:JSLintHighlightErrorLine = 0 | |
8 | " in your .vimrc | |
9 | " | |
10 | if exists("b:did_jslint_plugin") | |
11 | finish | |
12 | else | |
13 | let b:did_jslint_plugin = 1 | |
14 | endif | |
15 | ||
16 | let s:install_dir = expand('<sfile>:p:h') | |
17 | ||
18 | au BufLeave <buffer> call s:JSLintClear() | |
19 | ||
20 | au BufEnter <buffer> call s:JSLint() | |
21 | au InsertLeave <buffer> call s:JSLint() | |
22 | "au InsertEnter <buffer> call s:JSLint() | |
23 | au BufWritePost <buffer> call s:JSLint() | |
24 | ||
25 | " due to http://tech.groups.yahoo.com/group/vimdev/message/52115 | |
26 | if(!has("win32") || v:version>702) | |
27 | au CursorHold <buffer> call s:JSLint() | |
28 | au CursorHoldI <buffer> call s:JSLint() | |
29 | ||
30 | au CursorHold <buffer> call s:GetJSLintMessage() | |
31 | endif | |
32 | ||
33 | au CursorMoved <buffer> call s:GetJSLintMessage() | |
34 | ||
35 | if !exists("g:JSLintHighlightErrorLine") | |
36 | let g:JSLintHighlightErrorLine = 1 | |
37 | endif | |
38 | ||
39 | if !exists("*s:JSLintUpdate") | |
40 | function s:JSLintUpdate() | |
41 | silent call s:JSLint() | |
42 | call s:GetJSLintMessage() | |
43 | endfunction | |
44 | endif | |
45 | ||
46 | if !exists(":JSLintUpdate") | |
47 | command JSLintUpdate :call s:JSLintUpdate() | |
48 | endif | |
49 | if !exists(":JSLintToggle") | |
50 | command JSLintToggle exec ":let b:jslint_disabled = exists('b:jslint_disabled') ? b:jslint_disabled ? 0 : 1 : 1" | | |
51 | \ echo 'JSLint ' . ['enabled', 'disabled'][b:jslint_disabled] . '.' | |
52 | endif | |
53 | ||
54 | noremap <buffer><silent> dd dd:JSLintUpdate<CR> | |
55 | noremap <buffer><silent> dw dw:JSLintUpdate<CR> | |
56 | noremap <buffer><silent> u u:JSLintUpdate<CR> | |
57 | noremap <buffer><silent> <C-R> <C-R>:JSLintUpdate<CR> | |
58 | ||
59 | " Set up command and parameters | |
60 | if has("win32") | |
61 | let s:runjslint_ext = 'js' | |
62 | if exists("%JS_CMD%") | |
63 | let s:cmd = "$JS_CMD" | |
64 | elseif executable('node') | |
65 | let s:cmd = "node" | |
66 | else | |
67 | let s:cmd = 'cscript /NoLogo ' | |
68 | let s:runjslint_ext = 'wsf' | |
69 | endif | |
70 | else | |
71 | let s:runjslint_ext = 'js' | |
72 | if exists("$JS_CMD") | |
73 | let s:cmd = "$JS_CMD" | |
74 | elseif executable('node') | |
75 | let s:cmd = 'node' | |
76 | elseif executable('nodejs') | |
77 | let s:cmd = 'nodejs' | |
78 | elseif executable('/System/Library/Frameworks/JavaScriptCore.framework/Resources/jsc') | |
79 | let s:cmd = '/System/Library/Frameworks/JavaScriptCore.framework/Resources/jsc' | |
80 | elseif executable('js') | |
81 | let s:cmd = 'js' | |
82 | else | |
83 | echoerr('No JS interpreter found. Checked for jsc, js (spidermonkey), and node') | |
84 | endif | |
85 | endif | |
86 | let s:plugin_path = s:install_dir . "/jslint/" | |
87 | if has('win32') | |
88 | let s:plugin_path = substitute(s:plugin_path, '/', '\', 'g') | |
89 | endif | |
90 | if has('win32') | |
91 | let s:cmd = 'cmd.exe /C "cd /d "' . s:plugin_path . '" && ' . s:cmd . ' "' . s:plugin_path . 'runjslint.' . s:runjslint_ext . '""' | |
92 | else | |
93 | let s:cmd = 'cd "' . s:plugin_path . '" && ' . s:cmd . ' "' . s:plugin_path . 'runjslint.' . s:runjslint_ext . '"' | |
94 | endif | |
95 | ||
96 | let s:jslintrc_file = expand('~/.jslintrc') | |
97 | if filereadable(s:jslintrc_file) | |
98 | let s:jslintrc = readfile(s:jslintrc_file) | |
99 | else | |
100 | let s:jslintrc = [] | |
101 | end | |
102 | ||
103 | " load .jslintrc file from the current (pwd) directory if exists | |
104 | let s:localrc_file = fnamemodify(".", ":p") . '/.jslintrc' | |
105 | if filereadable(s:localrc_file) | |
106 | let s:localrc = readfile(s:localrc_file) | |
107 | let s:jslintrc = s:jslintrc + s:localrc | |
108 | endif | |
109 | " load .jslintrc file from the directory where the file is if exists | |
110 | let s:file_dir_file = expand('%:p:h') . '/.jslintrc' | |
111 | if filereadable(s:file_dir_file) | |
112 | let s:filedir_rc = readfile(s:file_dir_file) | |
113 | let s:jslintrc = s:jslintrc + s:filedir_rc | |
114 | endif | |
115 | ||
116 | " WideMsg() prints [long] message up to (&columns-1) length | |
117 | " guaranteed without "Press Enter" prompt. | |
118 | if !exists("*s:WideMsg") | |
119 | function s:WideMsg(msg) | |
120 | let x=&ruler | let y=&showcmd | |
121 | set noruler noshowcmd | |
122 | redraw | |
123 | echo a:msg | |
124 | let &ruler=x | let &showcmd=y | |
125 | endfun | |
126 | endif | |
127 | ||
128 | ||
129 | function! s:JSLintClear() | |
130 | " Delete previous matches | |
131 | let s:matches = getmatches() | |
132 | for s:matchId in s:matches | |
133 | if s:matchId['group'] == 'JSLintError' | |
134 | call matchdelete(s:matchId['id']) | |
135 | endif | |
136 | endfor | |
137 | let b:matched = [] | |
138 | let b:matchedlines = {} | |
139 | let b:cleared = 1 | |
140 | endfunction | |
141 | ||
142 | function! s:JSLint() | |
143 | if exists("b:jslint_disabled") && b:jslint_disabled == 1 | |
144 | return | |
145 | endif | |
146 | ||
147 | highlight link JSLintError SpellBad | |
148 | ||
149 | if exists("b:cleared") | |
150 | if b:cleared == 0 | |
151 | call s:JSLintClear() | |
152 | endif | |
153 | let b:cleared = 1 | |
154 | endif | |
155 | ||
156 | let b:matched = [] | |
157 | let b:matchedlines = {} | |
158 | ||
159 | " Detect range | |
160 | if a:firstline == a:lastline | |
161 | " Skip a possible shebang line, e.g. for node.js script. | |
162 | if getline(1)[0:1] == "#!" | |
163 | let b:firstline = 2 | |
164 | else | |
165 | let b:firstline = 1 | |
166 | endif | |
167 | let b:lastline = '$' | |
168 | else | |
169 | let b:firstline = a:firstline | |
170 | let b:lastline = a:lastline | |
171 | endif | |
172 | ||
173 | let b:qf_list = [] | |
174 | let b:qf_window_count = -1 | |
175 | ||
176 | let lines = join(s:jslintrc + getline(b:firstline, b:lastline), "\n") | |
177 | if len(lines) == 0 | |
178 | return | |
179 | endif | |
180 | if has('win32') || has('win64') | |
181 | let b:jslint_output = system(s:cmd, lines . "\n") | |
182 | else | |
183 | let old_shell = &shell | |
184 | let &shell = '/bin/bash' | |
185 | let b:jslint_output = system(s:cmd, lines . "\n") | |
186 | let &shell = old_shell | |
187 | endif | |
188 | if v:shell_error | |
189 | echoerr b:jslint_output | |
190 | echoerr 'could not invoke JSLint!' | |
191 | let b:jslint_disabled = 1 | |
192 | end | |
193 | ||
194 | for error in split(b:jslint_output, "\n") | |
195 | " Match {line}:{char}:{message} | |
196 | let b:parts = matchlist(error, '\v(\d+):(\d+):([A-Z]+):(.*)') | |
197 | if !empty(b:parts) | |
198 | let l:line = b:parts[1] + (b:firstline - 1 - len(s:jslintrc)) " Get line relative to selection | |
199 | let l:errorMessage = b:parts[4] | |
200 | ||
201 | if l:line < 1 | |
202 | echoerr 'error in jslintrc, line ' . b:parts[1] . ', character ' . b:parts[2] . ': ' . l:errorMessage | |
203 | else | |
204 | " Store the error for an error under the cursor | |
205 | let s:matchDict = {} | |
206 | let s:matchDict['lineNum'] = l:line | |
207 | let s:matchDict['message'] = l:errorMessage | |
208 | let b:matchedlines[l:line] = s:matchDict | |
209 | if b:parts[3] == 'ERROR' | |
210 | let l:errorType = 'E' | |
211 | else | |
212 | let l:errorType = 'W' | |
213 | endif | |
214 | if g:JSLintHighlightErrorLine == 1 | |
215 | let s:mID = matchadd('JSLintError', '\v%' . l:line . 'l\S.*(\S|$)') | |
216 | endif | |
217 | " Add line to match list | |
218 | call add(b:matched, s:matchDict) | |
219 | ||
220 | " Store the error for the quickfix window | |
221 | let l:qf_item = {} | |
222 | let l:qf_item.bufnr = bufnr('%') | |
223 | let l:qf_item.filename = expand('%') | |
224 | let l:qf_item.lnum = l:line | |
225 | let l:qf_item.text = l:errorMessage | |
226 | let l:qf_item.type = l:errorType | |
227 | ||
228 | " Add line to quickfix list | |
229 | call add(b:qf_list, l:qf_item) | |
230 | endif | |
231 | endif | |
232 | endfor | |
233 | ||
234 | if exists("s:jslint_qf") | |
235 | " if jslint quickfix window is already created, reuse it | |
236 | call s:ActivateJSLintQuickFixWindow() | |
237 | call setqflist(b:qf_list, 'r') | |
238 | else | |
239 | " one jslint quickfix window for all buffers | |
240 | call setqflist(b:qf_list, '') | |
241 | let s:jslint_qf = s:GetQuickFixStackCount() | |
242 | endif | |
243 | let b:cleared = 0 | |
244 | endfunction | |
245 | ||
246 | let b:showing_message = 0 | |
247 | ||
248 | if !exists("*s:GetJSLintMessage") | |
249 | function s:GetJSLintMessage() | |
250 | let s:cursorPos = getpos(".") | |
251 | ||
252 | " Bail if RunJSLint hasn't been called yet | |
253 | if !exists('b:matchedlines') | |
254 | return | |
255 | endif | |
256 | ||
257 | if has_key(b:matchedlines, s:cursorPos[1]) | |
258 | let s:jslintMatch = get(b:matchedlines, s:cursorPos[1]) | |
259 | call s:WideMsg(s:jslintMatch['message']) | |
260 | let b:showing_message = 1 | |
261 | return | |
262 | endif | |
263 | ||
264 | if b:showing_message == 1 | |
265 | echo | |
266 | let b:showing_message = 0 | |
267 | endif | |
268 | endfunction | |
269 | endif | |
270 | ||
271 | if !exists("*s:GetQuickFixStackCount") | |
272 | function s:GetQuickFixStackCount() | |
273 | let l:stack_count = 0 | |
274 | try | |
275 | silent colder 9 | |
276 | catch /E380:/ | |
277 | endtry | |
278 | ||
279 | try | |
280 | for i in range(9) | |
281 | silent cnewer | |
282 | let l:stack_count = l:stack_count + 1 | |
283 | endfor | |
284 | catch /E381:/ | |
285 | return l:stack_count | |
286 | endtry | |
287 | endfunction | |
288 | endif | |
289 | ||
290 | if !exists("*s:ActivateJSLintQuickFixWindow") | |
291 | function s:ActivateJSLintQuickFixWindow() | |
292 | try | |
293 | silent colder 9 " go to the bottom of quickfix stack | |
294 | catch /E380:/ | |
295 | catch /E788:/ | |
296 | endtry | |
297 | ||
298 | if s:jslint_qf > 0 | |
299 | try | |
300 | exe "silent cnewer " . s:jslint_qf | |
301 | catch /E381:/ | |
302 | echoerr "Could not activate JSLint Quickfix Window." | |
303 | endtry | |
304 | endif | |
305 | endfunction | |
306 | endif | |
307 |