286 if context: |
288 if context: |
287 self.__lastContext = word |
289 self.__lastContext = word |
288 else: |
290 else: |
289 self.__lastContext = None |
291 self.__lastContext = None |
290 |
292 |
|
293 prefix = "" |
|
294 mod = None |
|
295 if context: |
|
296 beg = beg[:col + 1] |
|
297 else: |
|
298 beg = editor.text(line)[:col] |
|
299 col = len(beg) |
|
300 wsep = editor.getLexer().autoCompletionWordSeparators() |
|
301 if wsep: |
|
302 if beg[col - 1] in wsep: |
|
303 col -= 1 |
|
304 else: |
|
305 while col >= 0 and beg[col - 1] not in wsep: |
|
306 col -= 1 |
|
307 if col >= 0: |
|
308 col -= 1 |
|
309 prefix = editor.getWordLeft(line, col) |
|
310 if editor.isPy2File() or editor.isPy3File(): |
|
311 src = editor.text() |
|
312 fn = editor.getFileName() |
|
313 if fn is None: |
|
314 fn = "" |
|
315 mod = Module("", fn, imp.PY_SOURCE) |
|
316 mod.scan(src) |
|
317 |
291 if word: |
318 if word: |
292 if self.__plugin.getPreferences("AutoCompletionSource") & AcsAPIs: |
319 if self.__plugin.getPreferences("AutoCompletionSource") & AcsAPIs: |
293 api = self.__apisManager.getAPIs(language) |
320 api = self.__apisManager.getAPIs(language) |
294 apiCompletionsList = self.__getApiCompletions(api, word, context) |
321 apiCompletionsList = self.__getApiCompletions( |
|
322 api, word, context, prefix, mod, editor) |
295 |
323 |
296 if self.__plugin.getPreferences("AutoCompletionSource") & AcsProject: |
324 if self.__plugin.getPreferences("AutoCompletionSource") & AcsProject: |
297 api = self.__apisManager.getAPIs(ApisNameProject) |
325 api = self.__apisManager.getAPIs(ApisNameProject) |
298 projectCompletionList = self.__getApiCompletions(api, word, context) |
326 projectCompletionList = self.__getApiCompletions( |
|
327 api, word, context, prefix, mod, editor) |
299 |
328 |
300 if self.__plugin.getPreferences("AutoCompletionSource") & AcsDocument: |
329 if self.__plugin.getPreferences("AutoCompletionSource") & AcsDocument: |
301 docCompletionsList = \ |
330 docCompletionsList = self.getCompletionsFromDocument( |
302 self.getCompletionsFromDocument(editor, word, context, sep) |
331 editor, word, context, sep, prefix, mod) |
303 |
332 |
304 completionsList = list( |
333 completionsList = list( |
305 set(apiCompletionsList) |
334 set(apiCompletionsList) |
306 .union(set(docCompletionsList)) |
335 .union(set(docCompletionsList)) |
307 .union(set(projectCompletionList)) |
336 .union(set(projectCompletionList)) |
309 |
338 |
310 if len(completionsList) > 0: |
339 if len(completionsList) > 0: |
311 completionsList.sort() |
340 completionsList.sort() |
312 editor.showUserList(EditorAutoCompletionListID, completionsList) |
341 editor.showUserList(EditorAutoCompletionListID, completionsList) |
313 |
342 |
314 def __getApiCompletions(self, api, word, context): |
343 def __getApiCompletions(self, api, word, context, prefix, module, editor): |
315 """ |
344 """ |
316 Private method to determine a list of completions from an API object. |
345 Private method to determine a list of completions from an API object. |
317 |
346 |
318 @param api reference to the API object to be used (APIsManager.DbAPIs) |
347 @param api reference to the API object to be used (APIsManager.DbAPIs) |
319 @param word word (or wordpart) to complete (string) |
348 @param word word (or wordpart) to complete (string) |
320 @param context flag indicating to autocomplete a context (boolean) |
349 @param context flag indicating to autocomplete a context (boolean) |
|
350 @param prefix prefix of the word to be completed (string) |
|
351 @param module reference to the scanned module info (Module) |
|
352 @param editor reference to the editor object (QScintilla.Editor) |
321 @return list of possible completions (list of strings) |
353 @return list of possible completions (list of strings) |
322 """ |
354 """ |
323 completionsList = [] |
355 completionsList = [] |
324 if api is not None: |
356 if api is not None: |
325 if context: |
357 if prefix and module and prefix == "self": |
|
358 line, col = editor.getCursorPosition() |
|
359 for cl in module.classes.values(): |
|
360 if line >= cl.lineno and \ |
|
361 (cl.endlineno == -1 or line <= cl.endlineno): |
|
362 completions = [] |
|
363 for super in cl.super: |
|
364 if prefix == word: |
|
365 completions.extend( |
|
366 api.getCompletions(context=super)) |
|
367 else: |
|
368 completions.extend( |
|
369 api.getCompletions(start=word, context=super)) |
|
370 for completion in completions: |
|
371 if not completion["context"]: |
|
372 entry = completion["completion"] |
|
373 else: |
|
374 entry = "{0} ({1})".format( |
|
375 completion["completion"], |
|
376 completion["context"] |
|
377 ) |
|
378 if entry in completionsList: |
|
379 completionsList.remove(entry) |
|
380 if completion["pictureId"]: |
|
381 entry += "?{0}".format(completion["pictureId"]) |
|
382 else: |
|
383 cont = False |
|
384 re = QRegExp(QRegExp.escape(entry) + "\?\d{,2}") |
|
385 for comp in completionsList: |
|
386 if re.exactMatch(comp): |
|
387 cont = True |
|
388 break |
|
389 if cont: |
|
390 continue |
|
391 if entry not in completionsList: |
|
392 completionsList.append(entry) |
|
393 |
|
394 break |
|
395 elif context: |
326 completions = api.getCompletions(context=word) |
396 completions = api.getCompletions(context=word) |
327 for completion in completions: |
397 for completion in completions: |
328 entry = completion["completion"] |
398 entry = completion["completion"] |
329 if completion["pictureId"]: |
399 if completion["pictureId"]: |
330 entry += "?{0}".format(completion["pictureId"]) |
400 entry += "?{0}".format(completion["pictureId"]) |
355 continue |
425 continue |
356 if entry not in completionsList: |
426 if entry not in completionsList: |
357 completionsList.append(entry) |
427 completionsList.append(entry) |
358 return completionsList |
428 return completionsList |
359 |
429 |
360 def getCompletionsFromDocument(self, editor, word, context, sep): |
430 def getCompletionsFromDocument(self, editor, word, context, sep, prefix, module): |
361 """ |
431 """ |
362 Public method to determine autocompletion proposals from the document. |
432 Public method to determine autocompletion proposals from the document. |
363 |
433 |
364 @param editor reference to the editor object (QScintilla.Editor) |
434 @param editor reference to the editor object (QScintilla.Editor) |
365 @param word string to be completed (string) |
435 @param word string to be completed (string) |
366 @param context flag indicating to autocomplete a context (boolean) |
436 @param context flag indicating to autocomplete a context (boolean) |
367 @param sep separator string (string) |
437 @param sep separator string (string) |
|
438 @param prefix prefix of the word to be completed (string) |
|
439 @param module reference to the scanned module info (Module) |
368 @return list of possible completions (list of strings) |
440 @return list of possible completions (list of strings) |
369 """ |
441 """ |
370 currentPos = editor.currentPosition() |
|
371 completionsList = [] |
442 completionsList = [] |
372 if context: |
443 |
373 word += sep |
444 prefixFound = False |
374 |
445 if prefix and module: |
375 if editor.isUtf8(): |
446 line, col = editor.getCursorPosition() |
376 sword = word.encode("utf-8") |
447 if prefix in ["cls", "self"]: |
377 else: |
448 prefixFound = True |
378 sword = word |
449 for cl in module.classes.values(): |
379 res = editor.findFirstTarget(sword, False, |
450 if line >= cl.lineno and \ |
380 editor.autoCompletionCaseSensitivity(), |
451 (cl.endlineno == -1 or line <= cl.endlineno): |
381 False, begline=0, begindex=0, ws_=True) |
452 comps = [] |
382 while res: |
453 for method in cl.methods.values(): |
383 start, length = editor.getFoundTarget() |
454 if method.name == "__init__": |
384 pos = start + length |
455 continue |
385 if pos != currentPos: |
456 # determine icon type |
386 if context: |
457 if method.isPrivate(): |
387 completion = "" |
458 iconID = Editor.MethodPrivateID |
388 else: |
459 elif method.isProtected(): |
389 completion = word |
460 iconID = Editor.MethodProtectedID |
390 line, index = editor.lineIndexFromPosition(pos) |
461 else: |
391 curWord = editor.getWord(line, index, useWordChars=False) |
462 iconID = Editor.MethodID |
392 completion += curWord[len(completion):] |
463 if hasattr(method, "modifier"): |
393 if completion and completion not in completionsList: |
464 if (prefix == "cls" and \ |
394 completionsList.append( |
465 method.modifier == method.Class) or \ |
395 "{0}?{1}".format(completion, self.__fromDocumentID)) |
466 prefix == "self": |
|
467 comps.append((method.name, iconID)) |
|
468 else: |
|
469 # eric 5.1 cannot differentiate method types |
|
470 comps.append((method.name, iconID)) |
|
471 if prefix != "cls": |
|
472 for attribute in cl.attributes.values(): |
|
473 # determine icon type |
|
474 if attribute.isPrivate(): |
|
475 iconID = Editor.AttributePrivateID |
|
476 elif attribute.isProtected(): |
|
477 iconID = Editor.AttributeProtectedID |
|
478 else: |
|
479 iconID = Editor.AttributePrivateID |
|
480 comps.append((attribute.name, iconID)) |
|
481 for attribute in cl.globals.values(): |
|
482 # determine icon type |
|
483 if attribute.isPrivate(): |
|
484 iconID = Editor.AttributePrivateID |
|
485 elif attribute.isProtected(): |
|
486 iconID = Editor.AttributeProtectedID |
|
487 else: |
|
488 iconID = Editor.AttributePrivateID |
|
489 comps.append((attribute.name, iconID)) |
|
490 |
|
491 if word != prefix: |
|
492 completionsList.extend( |
|
493 ["{0}?{1}".format(c[0], c[1]) |
|
494 for c in comps if c[0].startswith(word)]) |
|
495 else: |
|
496 completionsList.extend( |
|
497 ["{0}?{1}".format(c[0], c[1]) |
|
498 for c in comps]) |
|
499 break |
|
500 else: |
|
501 # possibly completing a named class attribute or method |
|
502 if prefix in module.classes: |
|
503 prefixFound = True |
|
504 cl = module.classes[prefix] |
|
505 comps = [] |
|
506 for method in cl.methods.values(): |
|
507 if method.name == "__init__": |
|
508 continue |
|
509 if not hasattr(method, "modifier"): |
|
510 # eric 5.1 cannot differentiate method types |
|
511 continue |
|
512 if method.modifier in [method.Class, method.Static]: |
|
513 # determine icon type |
|
514 if method.isPrivate(): |
|
515 iconID = Editor.MethodPrivateID |
|
516 elif method.isProtected(): |
|
517 iconID = Editor.MethodProtectedID |
|
518 else: |
|
519 iconID = Editor.MethodID |
|
520 comps.append((method.name, iconID)) |
|
521 for attribute in cl.globals.values(): |
|
522 # determine icon type |
|
523 if attribute.isPrivate(): |
|
524 iconID = Editor.AttributePrivateID |
|
525 elif attribute.isProtected(): |
|
526 iconID = Editor.AttributeProtectedID |
|
527 else: |
|
528 iconID = Editor.AttributePrivateID |
|
529 comps.append((attribute.name, iconID)) |
|
530 |
|
531 if word != prefix: |
|
532 completionsList.extend( |
|
533 ["{0}?{1}".format(c[0], c[1]) |
|
534 for c in comps if c[0].startswith(word)]) |
|
535 else: |
|
536 completionsList.extend( |
|
537 ["{0}?{1}".format(c[0], c[1]) |
|
538 for c in comps]) |
|
539 |
|
540 if not prefixFound: |
|
541 currentPos = editor.currentPosition() |
|
542 if context: |
|
543 word += sep |
396 |
544 |
397 res = editor.findNextTarget() |
545 if editor.isUtf8(): |
|
546 sword = word.encode("utf-8") |
|
547 else: |
|
548 sword = word |
|
549 res = editor.findFirstTarget(sword, False, |
|
550 editor.autoCompletionCaseSensitivity(), |
|
551 False, begline=0, begindex=0, ws_=True) |
|
552 while res: |
|
553 start, length = editor.getFoundTarget() |
|
554 pos = start + length |
|
555 if pos != currentPos: |
|
556 if context: |
|
557 completion = "" |
|
558 else: |
|
559 completion = word |
|
560 line, index = editor.lineIndexFromPosition(pos) |
|
561 curWord = editor.getWord(line, index, useWordChars=False) |
|
562 completion += curWord[len(completion):] |
|
563 if completion and completion not in completionsList: |
|
564 completionsList.append( |
|
565 "{0}?{1}".format(completion, self.__fromDocumentID)) |
|
566 |
|
567 res = editor.findNextTarget() |
398 |
568 |
399 completionsList.sort() |
569 completionsList.sort() |
400 return completionsList |
570 return completionsList |
401 |
571 |
402 ########################### |
572 ########################### |
432 if language == "": |
602 if language == "": |
433 return |
603 return |
434 |
604 |
435 line, col = editor.lineIndexFromPosition(pos) |
605 line, col = editor.lineIndexFromPosition(pos) |
436 wc = re.sub("\w", "", editor.wordCharacters()) |
606 wc = re.sub("\w", "", editor.wordCharacters()) |
|
607 pat = re.compile("\w{0}".format(re.escape(wc))) |
437 text = editor.text(line) |
608 text = editor.text(line) |
438 while col > 0 and \ |
609 while col > 0 and \ |
439 (not text[col - 1].isalnum() or \ |
610 not pat.match(text[col - 1]): |
440 (wc and text[col - 1] not in wc)): |
|
441 col -= 1 |
611 col -= 1 |
442 word = editor.getWordLeft(line, col) |
612 word = editor.getWordLeft(line, col) |
443 |
613 |
|
614 prefix = "" |
|
615 mod = None |
|
616 beg = editor.text(line)[:col] |
|
617 col = len(beg) |
|
618 wsep = editor.getLexer().autoCompletionWordSeparators() |
|
619 if wsep: |
|
620 if beg[col - 1] in wsep: |
|
621 col -= 1 |
|
622 else: |
|
623 while col >= 0 and beg[col - 1] not in wsep + [" ", "\t"]: |
|
624 col -= 1 |
|
625 if col >= 0: |
|
626 col -= 1 |
|
627 prefix = editor.getWordLeft(line, col) |
|
628 if editor.isPy2File() or editor.isPy3File(): |
|
629 src = editor.text() |
|
630 fn = editor.getFileName() |
|
631 if fn is None: |
|
632 fn = "" |
|
633 mod = Module("", fn, imp.PY_SOURCE) |
|
634 mod.scan(src) |
|
635 |
444 apiCalltips = [] |
636 apiCalltips = [] |
445 projectCalltips = [] |
637 projectCalltips = [] |
|
638 documentCalltips = [] |
446 |
639 |
447 if self.__plugin.getPreferences("AutoCompletionSource") & AcsAPIs: |
640 if self.__plugin.getPreferences("AutoCompletionSource") & AcsAPIs: |
448 api = self.__apisManager.getAPIs(language) |
641 api = self.__apisManager.getAPIs(language) |
449 if api is not None: |
642 if api is not None: |
450 apiCalltips = api.getCalltips(word, commas, self.__lastContext, |
643 apiCalltips = self.__getApiCalltips( |
451 self.__lastFullContext, |
644 api, word, commas, prefix, mod, editor) |
452 self.__plugin.getPreferences("CallTipsContextShown")) |
|
453 |
645 |
454 if self.__plugin.getPreferences("AutoCompletionSource") & AcsProject: |
646 if self.__plugin.getPreferences("AutoCompletionSource") & AcsProject: |
455 api = self.__apisManager.getAPIs(ApisNameProject) |
647 api = self.__apisManager.getAPIs(ApisNameProject) |
456 projectCalltips = api.getCalltips(word, commas, self.__lastContext, |
648 projectCalltips = self.__getApiCalltips( |
|
649 api, word, commas, prefix, mod, editor) |
|
650 |
|
651 if self.__plugin.getPreferences("AutoCompletionSource") & AcsDocument: |
|
652 documentCalltips = self.__getCalltipsFromDocument( |
|
653 word, prefix, mod, editor) |
|
654 |
|
655 return sorted( |
|
656 set(apiCalltips) |
|
657 .union(set(projectCalltips)) |
|
658 .union(set(documentCalltips)) |
|
659 ) |
|
660 |
|
661 def __getApiCalltips(self, api, word, commas, prefix, module, editor): |
|
662 """ |
|
663 Private method to determine calltips from APIs. |
|
664 |
|
665 @param api reference to the API object to be used (APIsManager.DbAPIs) |
|
666 @param word function to get calltips for (string) |
|
667 @param commas minimum number of commas contained in the calltip (integer) |
|
668 @param prefix prefix of the word to be completed (string) |
|
669 @param module reference to the scanned module info (Module) |
|
670 @param editor reference to the editor object (QScintilla.Editor) |
|
671 @return list of calltips (list of string) |
|
672 """ |
|
673 calltips = [] |
|
674 if prefix and module and prefix == "self": |
|
675 line, col = editor.getCursorPosition() |
|
676 for cl in module.classes.values(): |
|
677 if line >= cl.lineno and \ |
|
678 (cl.endlineno == -1 or line <= cl.endlineno): |
|
679 for super in cl.super: |
|
680 calltips.extend(api.getCalltips(word, commas, super, None, |
|
681 self.__plugin.getPreferences("CallTipsContextShown"))) |
|
682 break |
|
683 else: |
|
684 calltips = api.getCalltips(word, commas, self.__lastContext, |
457 self.__lastFullContext, |
685 self.__lastFullContext, |
458 self.__plugin.getPreferences("CallTipsContextShown")) |
686 self.__plugin.getPreferences("CallTipsContextShown")) |
459 |
687 |
460 return sorted(set(apiCalltips).union(set(projectCalltips))) |
688 return calltips |
|
689 |
|
690 def __getCalltipsFromDocument(self, word, prefix, module, editor): |
|
691 """ |
|
692 Private method to determine calltips from the document. |
|
693 |
|
694 @param word function to get calltips for (string) |
|
695 @param prefix prefix of the word to be completed (string) |
|
696 @param module reference to the scanned module info (Module) |
|
697 @param editor reference to the editor object (QScintilla.Editor) |
|
698 @return list of calltips (list of string) |
|
699 """ |
|
700 calltips = [] |
|
701 if module: |
|
702 if prefix: |
|
703 # prefix can be 'self', 'cls' or a class name |
|
704 sep = editor.getLexer().autoCompletionWordSeparators()[0] |
|
705 if prefix in ["self", "cls"]: |
|
706 line, col = editor.getCursorPosition() |
|
707 for cl in module.classes.values(): |
|
708 if line >= cl.lineno and \ |
|
709 (cl.endlineno == -1 or line <= cl.endlineno): |
|
710 if word in cl.methods: |
|
711 method = cl.methods[word] |
|
712 if hasattr(method, "modifier"): |
|
713 if prefix == "self" or \ |
|
714 (prefix == "cls" and \ |
|
715 method.modifier == method.Class): |
|
716 calltips.append("{0}{1}{2}({3})".format( |
|
717 cl.name, |
|
718 sep, |
|
719 word, |
|
720 ', '.join(method.parameters[1:]))) |
|
721 else: |
|
722 # eric 5.1 cannot differentiate method types |
|
723 calltips.append("{0}{1}{2}({3})".format( |
|
724 cl.name, |
|
725 sep, |
|
726 word, |
|
727 ', '.join(method.parameters[1:]))) |
|
728 break |
|
729 else: |
|
730 if prefix in module.classes: |
|
731 cl = module.classes[prefix] |
|
732 if word in cl.methods: |
|
733 method = cl.methods[word] |
|
734 if hasattr(method, "modifier") and \ |
|
735 method.modifier == method.Class: |
|
736 # only eric 5.2 and newer can differentiate method types |
|
737 calltips.append("{0}{1}{2}({3})".format( |
|
738 cl.name, |
|
739 sep, |
|
740 word, |
|
741 ', '.join(method.parameters[1:]))) |
|
742 else: |
|
743 # calltip for a module function or class |
|
744 if word in module.functions: |
|
745 fun = module.functions[word] |
|
746 calltips.append("{0}({1})".format( |
|
747 word, |
|
748 ', '.join(fun.parameters[1:]))) |
|
749 elif word in module.classes: |
|
750 cl = module.classes[word] |
|
751 if "__init__" in cl.methods: |
|
752 method = cl.methods["__init__"] |
|
753 calltips.append("{0}({1})".format( |
|
754 word, |
|
755 ', '.join(method.parameters[1:]))) |
|
756 |
|
757 return calltips |