--- a/QScintilla/Editor.py Wed Sep 27 18:00:37 2017 +0200 +++ b/QScintilla/Editor.py Mon Oct 02 14:24:58 2017 +0200 @@ -380,10 +380,21 @@ # set the text display again self.__setTextDisplay() - # set the autocompletion and calltips function + # set the auto-completion function + self.__acContext = True + self.__acText = "" + self.__acCompletions = set() + self.__acTimer = QTimer(self) + self.__acTimer.setSingleShot(True) + self.__acTimer.setInterval( + Preferences.getEditor("AutoCompletionTimeout")) + self.__acTimer.timeout.connect(self.__autoComplete) + self.__completionListHookFunctions = {} + self.__completionListAsyncHookFunctions = {} self.__setAutoCompletion() + # set the call-tips function self.__ctHookFunctions = {} self.__setCallTips() @@ -4521,7 +4532,8 @@ ## auto-completion hook interfaces ################################################################# - def addCompletionListHook(self, key, func): + # TODO: add support for asynchroneous auto-completion lists + def addCompletionListHook(self, key, func, async=False): """ Public method to set an auto-completion list provider. @@ -4531,9 +4543,14 @@ should be a function taking a reference to the editor and a boolean indicating to complete a context. It should return the possible completions as a list of strings. - @type function(editor, bool) -> list of str - """ - if key in self.__completionListHookFunctions: + @type function(editor, bool) -> list of str in case async is False + and function(editor, bool, str) returning nothing in case async + is True + @param async flag indicating an asynchroneous function + @type bool + """ + if key in self.__completionListHookFunctions or \ + key in self.__completionListAsyncHookFunctions: # it was already registered E5MessageBox.warning( self, @@ -4543,11 +4560,16 @@ .format(key)) return - if not self.__completionListHookFunctions: + if not self.__completionListHookFunctions and \ + not self.__completionListAsyncHookFunctions: if self.autoCompletionThreshold() > 0: self.setAutoCompletionThreshold(0) self.SCN_CHARADDED.connect(self.__charAdded) - self.__completionListHookFunctions[key] = func + + if async: + self.__completionListAsyncHookFunctions[key] = func + else: + self.__completionListHookFunctions[key] = func def removeCompletionListHook(self, key): """ @@ -4559,8 +4581,11 @@ """ if key in self.__completionListHookFunctions: del self.__completionListHookFunctions[key] - - if not self.__completionListHookFunctions: + elif key in self.__completionListAsyncHookFunctions: + del self.__completionListAsyncHookFunctions[key] + + if not self.__completionListHookFunctions and \ + not self.__completionListAsyncHookFunctions: self.SCN_CHARADDED.disconnect(self.__charAdded) if self.autoCompletionThreshold() == 0: self.setAutoCompletionThreshold( @@ -4575,45 +4600,81 @@ @return function providing completion list @rtype function or None """ - if key in self.__completionListHookFunctions: - return self.__completionListHookFunctions[key] - else: - return None + return (self.__completionListHookFunctions.get(key) or + self.__completionListAsyncHookFunctions.get(key)) def autoComplete(self, auto=False, context=True): """ - Public method to start autocompletion. + Public method to start auto-completion. @keyparam auto flag indicating a call from the __charAdded method (boolean) @keyparam context flag indicating to complete a context (boolean) """ if auto and self.autoCompletionThreshold() == -1: - # autocompletion is disabled + # auto-completion is disabled return - if self.__completionListHookFunctions: - if self.isListActive(): - self.cancelList() - completionsList = [] - for key in self.__completionListHookFunctions: - completionsList.extend( - self.__completionListHookFunctions[key](self, context)) - completionsList = list(set(completionsList)) - if len(completionsList) == 0: - if Preferences.getEditor("AutoCompletionScintillaOnFail") and \ - (self.autoCompletionSource() != QsciScintilla.AcsNone or - not auto): - self.autoCompleteQScintilla() + if self.__completionListHookFunctions or \ + self.__completionListAsyncHookFunctions: + if Preferences.getEditor("AutoCompletionTimeout"): + self.__acTimer.stop() + self.__acContext = context + self.__acTimer.start() else: - completionsList.sort() - self.showUserList(EditorAutoCompletionListID, - completionsList) + self.__autoComplete(context) elif not auto: self.autoCompleteQScintilla() elif self.autoCompletionSource() != QsciScintilla.AcsNone: self.autoCompleteQScintilla() + def __autoComplete(self, context=None): + """ + Private method to start auto-completion via plug-ins. + + @keyparam context flag indicating to complete a context + @type bool or None + """ + # TODO: add a cache for recent completions + if context is None: + context = self.__acContext + + line, col = self.getCursorPosition() + self.__acText = self.getWordLeft(line, col) + self.__acCompletions.clear() + + for key in self.__completionListAsyncHookFunctions: + self.__completionListAsyncHookFunctions[key]( + self, context, self.__acText) + + for key in self.__completionListHookFunctions: + completions = self.__completionListHookFunctions[key]( + self, context) + self.completionsListReady(completions, self.__acText) + + def completionsListReady(self, completions, acText): + """ + Public method to show the completions determined by a completions + provider. + + @param completions list of possible completions + @type list of str + @param acText text to be completed + @type str + """ + if acText == self.__acText: + # process the list only, if not already obsolete + if self.isListActive(): + self.cancelList() + + self.__acCompletions.update(set(completions)) + if self.__acCompletions: + self.showUserList( + EditorAutoCompletionListID, + sorted(list(self.__acCompletions), + reverse=Preferences.getEditor( + "AutoCompletionReversedList"))) + def __completionListSelected(self, listId, txt): """ Private slot to handle the selection from the completion list.