QScintilla/Editor.py

changeset 5886
ba6d27371e25
parent 5881
13381dbbb81e
child 5887
9a6ce5faed7a
diff -r 7a6f30e7f79c -r ba6d27371e25 QScintilla/Editor.py
--- 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.

eric ide

mercurial