Mercurial > public > dotfiles
comparison vim/vimfiles/bundle/pyflakes-vim/ftplugin/python/pyflakes.vim @ 8:097c95760fd0
Added pyflakes-vim plugin support.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Sun, 29 Apr 2012 16:30:44 -0500 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
7:86e0ac713642 | 8:097c95760fd0 |
---|---|
1 " pyflakes.vim - A script to highlight Python code on the fly with warnings | |
2 " from Pyflakes, a Python lint tool. | |
3 " | |
4 " Place this script and the accompanying pyflakes directory in | |
5 " .vim/ftplugin/python. | |
6 " | |
7 " See README for additional installation and information. | |
8 " | |
9 " Thanks to matlib.vim for ideas/code on interactive linting. | |
10 " | |
11 " Maintainer: Kevin Watters <kevin.watters@gmail.com> | |
12 " Version: 0.1 | |
13 | |
14 if exists("b:did_pyflakes_plugin") | |
15 finish " only load once | |
16 else | |
17 let b:did_pyflakes_plugin = 1 | |
18 endif | |
19 | |
20 if !exists('g:pyflakes_builtins') | |
21 let g:pyflakes_builtins = [] | |
22 endif | |
23 | |
24 if !exists("b:did_python_init") | |
25 let b:did_python_init = 0 | |
26 | |
27 if !has('python') | |
28 echoerr "Error: the pyflakes.vim plugin requires Vim to be compiled with +python" | |
29 finish | |
30 endif | |
31 | |
32 if !exists('g:pyflakes_use_quickfix') | |
33 let g:pyflakes_use_quickfix = 1 | |
34 endif | |
35 | |
36 | |
37 python << EOF | |
38 import vim | |
39 import os.path | |
40 import sys | |
41 | |
42 if sys.version_info[:2] < (2, 5): | |
43 raise AssertionError('Vim must be compiled with Python 2.5 or higher; you have ' + sys.version) | |
44 | |
45 # get the directory this script is in: the pyflakes python module should be installed there. | |
46 scriptdir = os.path.join(os.path.dirname(vim.eval('expand("<sfile>")')), 'pyflakes') | |
47 sys.path.insert(0, scriptdir) | |
48 | |
49 import ast | |
50 from pyflakes import checker, messages | |
51 from operator import attrgetter | |
52 import re | |
53 | |
54 class loc(object): | |
55 def __init__(self, lineno, col=None): | |
56 self.lineno = lineno | |
57 self.col_offset = col | |
58 | |
59 class SyntaxError(messages.Message): | |
60 message = 'could not compile: %s' | |
61 def __init__(self, filename, lineno, col, message): | |
62 messages.Message.__init__(self, filename, loc(lineno, col)) | |
63 self.message_args = (message,) | |
64 | |
65 class blackhole(object): | |
66 write = flush = lambda *a, **k: None | |
67 | |
68 def check(buffer): | |
69 filename = buffer.name | |
70 contents = buffer[:] | |
71 | |
72 # shebang usually found at the top of the file, followed by source code encoding marker. | |
73 # assume everything else that follows is encoded in the encoding. | |
74 encoding_found = False | |
75 for n, line in enumerate(contents): | |
76 if n >= 2: | |
77 break | |
78 elif re.match(r'#.*coding[:=]\s*([-\w.]+)', line): | |
79 contents = ['']*(n+1) + contents[n+1:] | |
80 break | |
81 | |
82 contents = '\n'.join(contents) + '\n' | |
83 | |
84 vimenc = vim.eval('&encoding') | |
85 if vimenc: | |
86 contents = contents.decode(vimenc) | |
87 | |
88 builtins = set(['__file__']) | |
89 try: | |
90 builtins.update(set(eval(vim.eval('string(g:pyflakes_builtins)')))) | |
91 except Exception: | |
92 pass | |
93 | |
94 try: | |
95 # TODO: use warnings filters instead of ignoring stderr | |
96 old_stderr, sys.stderr = sys.stderr, blackhole() | |
97 try: | |
98 tree = ast.parse(contents, filename or '<unknown>') | |
99 finally: | |
100 sys.stderr = old_stderr | |
101 except: | |
102 try: | |
103 value = sys.exc_info()[1] | |
104 lineno, offset, line = value[1][1:] | |
105 except IndexError: | |
106 lineno, offset, line = 1, 0, '' | |
107 if line and line.endswith("\n"): | |
108 line = line[:-1] | |
109 | |
110 return [SyntaxError(filename, lineno, offset, str(value))] | |
111 else: | |
112 # pyflakes looks to _MAGIC_GLOBALS in checker.py to see which | |
113 # UndefinedNames to ignore | |
114 old_globals = getattr(checker,' _MAGIC_GLOBALS', []) | |
115 checker._MAGIC_GLOBALS = set(old_globals) | builtins | |
116 | |
117 w = checker.Checker(tree, filename) | |
118 | |
119 checker._MAGIC_GLOBALS = old_globals | |
120 | |
121 w.messages.sort(key = attrgetter('lineno')) | |
122 return w.messages | |
123 | |
124 | |
125 def vim_quote(s): | |
126 return s.replace("'", "''") | |
127 EOF | |
128 let b:did_python_init = 1 | |
129 endif | |
130 | |
131 if !b:did_python_init | |
132 finish | |
133 endif | |
134 | |
135 au BufLeave <buffer> call s:ClearPyflakes() | |
136 | |
137 au BufEnter <buffer> call s:RunPyflakes() | |
138 au InsertLeave <buffer> call s:RunPyflakes() | |
139 au InsertEnter <buffer> call s:RunPyflakes() | |
140 au BufWritePost <buffer> call s:RunPyflakes() | |
141 | |
142 au CursorHold <buffer> call s:RunPyflakes() | |
143 au CursorHoldI <buffer> call s:RunPyflakes() | |
144 | |
145 au CursorHold <buffer> call s:GetPyflakesMessage() | |
146 au CursorMoved <buffer> call s:GetPyflakesMessage() | |
147 | |
148 if !exists("*s:PyflakesUpdate") | |
149 function s:PyflakesUpdate() | |
150 silent call s:RunPyflakes() | |
151 call s:GetPyflakesMessage() | |
152 endfunction | |
153 endif | |
154 | |
155 " Call this function in your .vimrc to update PyFlakes | |
156 if !exists(":PyflakesUpdate") | |
157 command PyflakesUpdate :call s:PyflakesUpdate() | |
158 endif | |
159 | |
160 " Hook common text manipulation commands to update PyFlakes | |
161 " TODO: is there a more general "text op" autocommand we could register | |
162 " for here? | |
163 noremap <buffer><silent> dd dd:PyflakesUpdate<CR> | |
164 noremap <buffer><silent> dw dw:PyflakesUpdate<CR> | |
165 noremap <buffer><silent> u u:PyflakesUpdate<CR> | |
166 noremap <buffer><silent> <C-R> <C-R>:PyflakesUpdate<CR> | |
167 | |
168 " WideMsg() prints [long] message up to (&columns-1) length | |
169 " guaranteed without "Press Enter" prompt. | |
170 if !exists("*s:WideMsg") | |
171 function s:WideMsg(msg) | |
172 let x=&ruler | let y=&showcmd | |
173 set noruler noshowcmd | |
174 redraw | |
175 echo strpart(a:msg, 0, &columns-1) | |
176 let &ruler=x | let &showcmd=y | |
177 endfun | |
178 endif | |
179 | |
180 if !exists("*s:GetQuickFixStackCount") | |
181 function s:GetQuickFixStackCount() | |
182 let l:stack_count = 0 | |
183 try | |
184 silent colder 9 | |
185 catch /E380:/ | |
186 endtry | |
187 | |
188 try | |
189 for i in range(9) | |
190 silent cnewer | |
191 let l:stack_count = l:stack_count + 1 | |
192 endfor | |
193 catch /E381:/ | |
194 return l:stack_count | |
195 endtry | |
196 endfunction | |
197 endif | |
198 | |
199 if !exists("*s:ActivatePyflakesQuickFixWindow") | |
200 function s:ActivatePyflakesQuickFixWindow() | |
201 try | |
202 silent colder 9 " go to the bottom of quickfix stack | |
203 catch /E380:/ | |
204 endtry | |
205 | |
206 if s:pyflakes_qf > 0 | |
207 try | |
208 exe "silent cnewer " . s:pyflakes_qf | |
209 catch /E381:/ | |
210 echoerr "Could not activate Pyflakes Quickfix Window." | |
211 endtry | |
212 endif | |
213 endfunction | |
214 endif | |
215 | |
216 if !exists("*s:RunPyflakes") | |
217 function s:RunPyflakes() | |
218 highlight link PyFlakes SpellBad | |
219 | |
220 if exists("b:cleared") | |
221 if b:cleared == 0 | |
222 silent call s:ClearPyflakes() | |
223 let b:cleared = 1 | |
224 endif | |
225 else | |
226 let b:cleared = 1 | |
227 endif | |
228 | |
229 let b:matched = [] | |
230 let b:matchedlines = {} | |
231 | |
232 let b:qf_list = [] | |
233 let b:qf_window_count = -1 | |
234 | |
235 python << EOF | |
236 for w in check(vim.current.buffer): | |
237 vim.command('let s:matchDict = {}') | |
238 vim.command("let s:matchDict['lineNum'] = " + str(w.lineno)) | |
239 vim.command("let s:matchDict['message'] = '%s'" % vim_quote(w.message % w.message_args)) | |
240 vim.command("let b:matchedlines[" + str(w.lineno) + "] = s:matchDict") | |
241 | |
242 vim.command("let l:qf_item = {}") | |
243 vim.command("let l:qf_item.bufnr = bufnr('%')") | |
244 vim.command("let l:qf_item.filename = expand('%')") | |
245 vim.command("let l:qf_item.lnum = %s" % str(w.lineno)) | |
246 vim.command("let l:qf_item.text = '%s'" % vim_quote(w.message % w.message_args)) | |
247 vim.command("let l:qf_item.type = 'E'") | |
248 | |
249 if getattr(w, 'col', None) is None or isinstance(w, SyntaxError): | |
250 # without column information, just highlight the whole line | |
251 # (minus the newline) | |
252 vim.command(r"let s:mID = matchadd('PyFlakes', '\%" + str(w.lineno) + r"l\n\@!')") | |
253 else: | |
254 # with a column number, highlight the first keyword there | |
255 vim.command(r"let s:mID = matchadd('PyFlakes', '^\%" + str(w.lineno) + r"l\_.\{-}\zs\k\+\k\@!\%>" + str(w.col) + r"c')") | |
256 | |
257 vim.command("let l:qf_item.vcol = 1") | |
258 vim.command("let l:qf_item.col = %s" % str(w.col + 1)) | |
259 | |
260 vim.command("call add(b:matched, s:matchDict)") | |
261 vim.command("call add(b:qf_list, l:qf_item)") | |
262 EOF | |
263 if g:pyflakes_use_quickfix == 1 | |
264 if exists("s:pyflakes_qf") | |
265 " if pyflakes quickfix window is already created, reuse it | |
266 call s:ActivatePyflakesQuickFixWindow() | |
267 call setqflist(b:qf_list, 'r') | |
268 else | |
269 " one pyflakes quickfix window for all buffer | |
270 call setqflist(b:qf_list, '') | |
271 let s:pyflakes_qf = s:GetQuickFixStackCount() | |
272 endif | |
273 endif | |
274 | |
275 let b:cleared = 0 | |
276 endfunction | |
277 end | |
278 | |
279 " keep track of whether or not we are showing a message | |
280 let b:showing_message = 0 | |
281 | |
282 if !exists("*s:GetPyflakesMessage") | |
283 function s:GetPyflakesMessage() | |
284 let s:cursorPos = getpos(".") | |
285 | |
286 " Bail if RunPyflakes hasn't been called yet. | |
287 if !exists('b:matchedlines') | |
288 return | |
289 endif | |
290 | |
291 " if there's a message for the line the cursor is currently on, echo | |
292 " it to the console | |
293 if has_key(b:matchedlines, s:cursorPos[1]) | |
294 let s:pyflakesMatch = get(b:matchedlines, s:cursorPos[1]) | |
295 call s:WideMsg(s:pyflakesMatch['message']) | |
296 let b:showing_message = 1 | |
297 return | |
298 endif | |
299 | |
300 " otherwise, if we're showing a message, clear it | |
301 if b:showing_message == 1 | |
302 echo | |
303 let b:showing_message = 0 | |
304 endif | |
305 endfunction | |
306 endif | |
307 | |
308 if !exists('*s:ClearPyflakes') | |
309 function s:ClearPyflakes() | |
310 let s:matches = getmatches() | |
311 for s:matchId in s:matches | |
312 if s:matchId['group'] == 'PyFlakes' | |
313 call matchdelete(s:matchId['id']) | |
314 endif | |
315 endfor | |
316 let b:matched = [] | |
317 let b:matchedlines = {} | |
318 let b:cleared = 1 | |
319 endfunction | |
320 endif | |
321 |