|
Lines 1-17
Link Here
|
| 1 |
"pythoncomplete.vim - Omni Completion for python |
1 |
"pythoncomplete.vim - Omni Completion for python |
| 2 |
" Maintainer: Aaron Griffin <aaronmgriffin@gmail.com> |
2 |
" Maintainer: Aaron Griffin <aaronmgriffin@gmail.com> |
| 3 |
" Version: 0.7 |
3 |
" Version: 0.9 |
| 4 |
" Last Updated: 19 Oct 2006 |
4 |
" Last Updated: 18 Jun 2009 |
| 5 |
" |
5 |
" |
| 6 |
" Changes |
6 |
" Changes |
| 7 |
" TODO: |
7 |
" TODO: |
| 8 |
" User defined docstrings aren't handled right... |
|
|
| 9 |
" 'info' item output can use some formatting work |
8 |
" 'info' item output can use some formatting work |
| 10 |
" Add an "unsafe eval" mode, to allow for return type evaluation |
9 |
" Add an "unsafe eval" mode, to allow for return type evaluation |
| 11 |
" Complete basic syntax along with import statements |
10 |
" Complete basic syntax along with import statements |
| 12 |
" i.e. "import url<c-x,c-o>" |
11 |
" i.e. "import url<c-x,c-o>" |
| 13 |
" Continue parsing on invalid line?? |
12 |
" Continue parsing on invalid line?? |
| 14 |
" |
13 |
" |
|
|
14 |
" v 0.9 |
| 15 |
" * Fixed docstring parsing for classes and functions |
| 16 |
" * Fixed parsing of *args and **kwargs type arguments |
| 17 |
" * Better function param parsing to handle things like tuples and |
| 18 |
" lambda defaults args |
| 19 |
" |
| 20 |
" v 0.8 |
| 21 |
" * Fixed an issue where the FIRST assignment was always used instead of |
| 22 |
" using a subsequent assignment for a variable |
| 23 |
" * Fixed a scoping issue when working inside a parameterless function |
| 24 |
" |
| 25 |
" |
| 15 |
" v 0.7 |
26 |
" v 0.7 |
| 16 |
" * Fixed function list sorting (_ and __ at the bottom) |
27 |
" * Fixed function list sorting (_ and __ at the bottom) |
| 17 |
" * Removed newline removal from docs. It appears vim handles these better in |
28 |
" * Removed newline removal from docs. It appears vim handles these better in |
|
Lines 63-69
Link Here
|
| 63 |
while idx > 0 |
74 |
while idx > 0 |
| 64 |
let idx -= 1 |
75 |
let idx -= 1 |
| 65 |
let c = line[idx] |
76 |
let c = line[idx] |
| 66 |
if c =~ '\w' || c =~ '\.' || c == '(' |
77 |
if c =~ '\w' || c =~ '\.' |
| 67 |
let cword = c . cword |
78 |
let cword = c . cword |
| 68 |
continue |
79 |
continue |
| 69 |
elseif strlen(cword) > 0 || idx == 0 |
80 |
elseif strlen(cword) > 0 || idx == 0 |
|
Lines 206-212
Link Here
|
| 206 |
if len(stmt) > 0 and stmt[-1] == '(': |
217 |
if len(stmt) > 0 and stmt[-1] == '(': |
| 207 |
result = eval(_sanitize(stmt[:-1]), self.compldict) |
218 |
result = eval(_sanitize(stmt[:-1]), self.compldict) |
| 208 |
doc = result.__doc__ |
219 |
doc = result.__doc__ |
| 209 |
if doc == None: doc = '' |
220 |
if doc is None: doc = '' |
| 210 |
args = self.get_arguments(result) |
221 |
args = self.get_arguments(result) |
| 211 |
return [{'word':self._cleanstr(args),'info':self._cleanstr(doc)}] |
222 |
return [{'word':self._cleanstr(args),'info':self._cleanstr(doc)}] |
| 212 |
elif ridx == -1: |
223 |
elif ridx == -1: |
|
Lines 223-240
Link Here
|
| 223 |
|
234 |
|
| 224 |
try: maindoc = result.__doc__ |
235 |
try: maindoc = result.__doc__ |
| 225 |
except: maindoc = ' ' |
236 |
except: maindoc = ' ' |
| 226 |
if maindoc == None: maindoc = ' ' |
237 |
if maindoc is None: maindoc = ' ' |
| 227 |
for m in all: |
238 |
for m in all: |
| 228 |
if m == "_PyCmplNoType": continue #this is internal |
239 |
if m == "_PyCmplNoType": continue #this is internal |
| 229 |
try: |
240 |
try: |
| 230 |
dbg('possible completion: %s' % m) |
241 |
dbg('possible completion: %s' % m) |
| 231 |
if m.find(match) == 0: |
242 |
if m.find(match) == 0: |
| 232 |
if result == None: inst = all[m] |
243 |
if result is None: inst = all[m] |
| 233 |
else: inst = getattr(result,m) |
244 |
else: inst = getattr(result,m) |
| 234 |
try: doc = inst.__doc__ |
245 |
try: doc = inst.__doc__ |
| 235 |
except: doc = maindoc |
246 |
except: doc = maindoc |
| 236 |
typestr = str(inst) |
247 |
typestr = str(inst) |
| 237 |
if doc == None or doc == '': doc = maindoc |
248 |
if doc is None or doc == '': doc = maindoc |
| 238 |
|
249 |
|
| 239 |
wrd = m[len(match):] |
250 |
wrd = m[len(match):] |
| 240 |
c = {'word':wrd, 'abbr':m, 'info':self._cleanstr(doc)} |
251 |
c = {'word':wrd, 'abbr':m, 'info':self._cleanstr(doc)} |
|
Lines 260-268
Link Here
|
| 260 |
return [] |
271 |
return [] |
| 261 |
|
272 |
|
| 262 |
class Scope(object): |
273 |
class Scope(object): |
| 263 |
def __init__(self,name,indent): |
274 |
def __init__(self,name,indent,docstr=''): |
| 264 |
self.subscopes = [] |
275 |
self.subscopes = [] |
| 265 |
self.docstr = '' |
276 |
self.docstr = docstr |
| 266 |
self.locals = [] |
277 |
self.locals = [] |
| 267 |
self.parent = None |
278 |
self.parent = None |
| 268 |
self.name = name |
279 |
self.name = name |
|
Lines 281-309
Link Here
|
| 281 |
while d.find(' ') > -1: d = d.replace(' ',' ') |
292 |
while d.find(' ') > -1: d = d.replace(' ',' ') |
| 282 |
while d[0] in '"\'\t ': d = d[1:] |
293 |
while d[0] in '"\'\t ': d = d[1:] |
| 283 |
while d[-1] in '"\'\t ': d = d[:-1] |
294 |
while d[-1] in '"\'\t ': d = d[:-1] |
|
|
295 |
dbg("Scope(%s)::docstr = %s" % (self,d)) |
| 284 |
self.docstr = d |
296 |
self.docstr = d |
| 285 |
|
297 |
|
| 286 |
def local(self,loc): |
298 |
def local(self,loc): |
| 287 |
if not self._hasvaralready(loc): |
299 |
self._checkexisting(loc) |
| 288 |
self.locals.append(loc) |
300 |
self.locals.append(loc) |
| 289 |
|
301 |
|
| 290 |
def copy_decl(self,indent=0): |
302 |
def copy_decl(self,indent=0): |
| 291 |
""" Copy a scope's declaration only, at the specified indent level - not local variables """ |
303 |
""" Copy a scope's declaration only, at the specified indent level - not local variables """ |
| 292 |
return Scope(self.name,indent) |
304 |
return Scope(self.name,indent,self.docstr) |
| 293 |
|
305 |
|
| 294 |
def _hasvaralready(self,test): |
306 |
def _checkexisting(self,test): |
| 295 |
"Convienance function... keep out duplicates" |
307 |
"Convienance function... keep out duplicates" |
| 296 |
if test.find('=') > -1: |
308 |
if test.find('=') > -1: |
| 297 |
var = test.split('=')[0].strip() |
309 |
var = test.split('=')[0].strip() |
| 298 |
for l in self.locals: |
310 |
for l in self.locals: |
| 299 |
if l.find('=') > -1 and var == l.split('=')[0].strip(): |
311 |
if l.find('=') > -1 and var == l.split('=')[0].strip(): |
| 300 |
return True |
312 |
self.locals.remove(l) |
| 301 |
return False |
|
|
| 302 |
|
313 |
|
| 303 |
def get_code(self): |
314 |
def get_code(self): |
| 304 |
# we need to start with this, to fix up broken completions |
315 |
str = "" |
| 305 |
# hopefully this name is unique enough... |
316 |
if len(self.docstr) > 0: str += '"""'+self.docstr+'"""\n' |
| 306 |
str = '"""'+self.docstr+'"""\n' |
|
|
| 307 |
for l in self.locals: |
317 |
for l in self.locals: |
| 308 |
if l.startswith('import'): str += l+'\n' |
318 |
if l.startswith('import'): str += l+'\n' |
| 309 |
str += 'class _PyCmplNoType:\n def __getattr__(self,name):\n return None\n' |
319 |
str += 'class _PyCmplNoType:\n def __getattr__(self,name):\n return None\n' |
|
Lines 330-340
Link Here
|
| 330 |
return ' '*(self.indent+1) |
340 |
return ' '*(self.indent+1) |
| 331 |
|
341 |
|
| 332 |
class Class(Scope): |
342 |
class Class(Scope): |
| 333 |
def __init__(self, name, supers, indent): |
343 |
def __init__(self, name, supers, indent, docstr=''): |
| 334 |
Scope.__init__(self,name,indent) |
344 |
Scope.__init__(self,name,indent, docstr) |
| 335 |
self.supers = supers |
345 |
self.supers = supers |
| 336 |
def copy_decl(self,indent=0): |
346 |
def copy_decl(self,indent=0): |
| 337 |
c = Class(self.name,self.supers,indent) |
347 |
c = Class(self.name,self.supers,indent, self.docstr) |
| 338 |
for s in self.subscopes: |
348 |
for s in self.subscopes: |
| 339 |
c.add(s.copy_decl(indent+1)) |
349 |
c.add(s.copy_decl(indent+1)) |
| 340 |
return c |
350 |
return c |
|
Lines 351-361
Link Here
|
| 351 |
|
361 |
|
| 352 |
|
362 |
|
| 353 |
class Function(Scope): |
363 |
class Function(Scope): |
| 354 |
def __init__(self, name, params, indent): |
364 |
def __init__(self, name, params, indent, docstr=''): |
| 355 |
Scope.__init__(self,name,indent) |
365 |
Scope.__init__(self,name,indent, docstr) |
| 356 |
self.params = params |
366 |
self.params = params |
| 357 |
def copy_decl(self,indent=0): |
367 |
def copy_decl(self,indent=0): |
| 358 |
return Function(self.name,self.params,indent) |
368 |
return Function(self.name,self.params,indent, self.docstr) |
| 359 |
def get_code(self): |
369 |
def get_code(self): |
| 360 |
str = "%sdef %s(%s):\n" % \ |
370 |
str = "%sdef %s(%s):\n" % \ |
| 361 |
(self.currentindent(),self.name,','.join(self.params)) |
371 |
(self.currentindent(),self.name,','.join(self.params)) |
|
Lines 371-377
Link Here
|
| 371 |
def _parsedotname(self,pre=None): |
381 |
def _parsedotname(self,pre=None): |
| 372 |
#returns (dottedname, nexttoken) |
382 |
#returns (dottedname, nexttoken) |
| 373 |
name = [] |
383 |
name = [] |
| 374 |
if pre == None: |
384 |
if pre is None: |
| 375 |
tokentype, token, indent = self.next() |
385 |
tokentype, token, indent = self.next() |
| 376 |
if tokentype != NAME and token != '*': |
386 |
if tokentype != NAME and token != '*': |
| 377 |
return ('', token) |
387 |
return ('', token) |
|
Lines 405-421
Link Here
|
| 405 |
while True: |
415 |
while True: |
| 406 |
tokentype, token, indent = self.next() |
416 |
tokentype, token, indent = self.next() |
| 407 |
if token in (')', ',') and level == 1: |
417 |
if token in (')', ',') and level == 1: |
| 408 |
names.append(name) |
418 |
if '=' not in name: name = name.replace(' ', '') |
|
|
419 |
names.append(name.strip()) |
| 409 |
name = '' |
420 |
name = '' |
| 410 |
if token == '(': |
421 |
if token == '(': |
| 411 |
level += 1 |
422 |
level += 1 |
|
|
423 |
name += "(" |
| 412 |
elif token == ')': |
424 |
elif token == ')': |
| 413 |
level -= 1 |
425 |
level -= 1 |
| 414 |
if level == 0: break |
426 |
if level == 0: break |
|
|
427 |
else: name += ")" |
| 415 |
elif token == ',' and level == 1: |
428 |
elif token == ',' and level == 1: |
| 416 |
pass |
429 |
pass |
| 417 |
else: |
430 |
else: |
| 418 |
name += str(token) |
431 |
name += "%s " % str(token) |
| 419 |
return names |
432 |
return names |
| 420 |
|
433 |
|
| 421 |
def _parsefunction(self,indent): |
434 |
def _parsefunction(self,indent): |
|
Lines 495-510
Link Here
|
| 495 |
#Handle 'self' params |
508 |
#Handle 'self' params |
| 496 |
if scp.parent != None and type(scp.parent) == Class: |
509 |
if scp.parent != None and type(scp.parent) == Class: |
| 497 |
slice = 1 |
510 |
slice = 1 |
| 498 |
p = scp.params[0] |
|
|
| 499 |
i = p.find('=') |
| 500 |
if i != -1: p = p[:i] |
| 501 |
newscope.local('%s = %s' % (scp.params[0],scp.parent.name)) |
511 |
newscope.local('%s = %s' % (scp.params[0],scp.parent.name)) |
| 502 |
for p in scp.params[slice:]: |
512 |
for p in scp.params[slice:]: |
| 503 |
i = p.find('=') |
513 |
i = p.find('=') |
|
|
514 |
if len(p) == 0: continue |
| 515 |
pvar = '' |
| 516 |
ptype = '' |
| 504 |
if i == -1: |
517 |
if i == -1: |
| 505 |
newscope.local('%s = _PyCmplNoType()' % p) |
518 |
pvar = p |
|
|
519 |
ptype = '_PyCmplNoType()' |
| 506 |
else: |
520 |
else: |
| 507 |
newscope.local('%s = %s' % (p[:i],_sanitize(p[i+1]))) |
521 |
pvar = p[:i] |
|
|
522 |
ptype = _sanitize(p[i+1:]) |
| 523 |
if pvar.startswith('**'): |
| 524 |
pvar = pvar[2:] |
| 525 |
ptype = '{}' |
| 526 |
elif pvar.startswith('*'): |
| 527 |
pvar = pvar[1:] |
| 528 |
ptype = '[]' |
| 529 |
|
| 530 |
newscope.local('%s = %s' % (pvar,ptype)) |
| 508 |
|
531 |
|
| 509 |
for s in scp.subscopes: |
532 |
for s in scp.subscopes: |
| 510 |
ns = s.copy_decl(0) |
533 |
ns = s.copy_decl(0) |
|
Lines 532-548
Link Here
|
| 532 |
self.scope = self.scope.pop(indent) |
555 |
self.scope = self.scope.pop(indent) |
| 533 |
elif token == 'def': |
556 |
elif token == 'def': |
| 534 |
func = self._parsefunction(indent) |
557 |
func = self._parsefunction(indent) |
| 535 |
if func == None: |
558 |
if func is None: |
| 536 |
print "function: syntax error..." |
559 |
print "function: syntax error..." |
| 537 |
continue |
560 |
continue |
|
|
561 |
dbg("new scope: function") |
| 538 |
freshscope = True |
562 |
freshscope = True |
| 539 |
self.scope = self.scope.add(func) |
563 |
self.scope = self.scope.add(func) |
| 540 |
elif token == 'class': |
564 |
elif token == 'class': |
| 541 |
cls = self._parseclass(indent) |
565 |
cls = self._parseclass(indent) |
| 542 |
if cls == None: |
566 |
if cls is None: |
| 543 |
print "class: syntax error..." |
567 |
print "class: syntax error..." |
| 544 |
continue |
568 |
continue |
| 545 |
freshscope = True |
569 |
freshscope = True |
|
|
570 |
dbg("new scope: class") |
| 546 |
self.scope = self.scope.add(cls) |
571 |
self.scope = self.scope.add(cls) |
| 547 |
|
572 |
|
| 548 |
elif token == 'import': |
573 |
elif token == 'import': |
|
Lines 569-574
Link Here
|
| 569 |
name,token = self._parsedotname(token) |
594 |
name,token = self._parsedotname(token) |
| 570 |
if token == '=': |
595 |
if token == '=': |
| 571 |
stmt = self._parseassignment() |
596 |
stmt = self._parseassignment() |
|
|
597 |
dbg("parseassignment: %s = %s" % (name, stmt)) |
| 572 |
if stmt != None: |
598 |
if stmt != None: |
| 573 |
self.scope.local("%s = %s" % (name,stmt)) |
599 |
self.scope.local("%s = %s" % (name,stmt)) |
| 574 |
freshscope = False |
600 |
freshscope = False |