RefactoringRope/CodeAssistServer.py

Fri, 29 Sep 2017 10:23:35 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Fri, 29 Sep 2017 10:23:35 +0200
branch
server_client_variant
changeset 203
c38750e1bafd
parent 202
a111134b5dc7
child 209
c1dce8630555
permissions
-rw-r--r--

Performed some code cleanup actions.

# -*- coding: utf-8 -*-

# Copyright (c) 2008 - 2017 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing the autocompletion interface to rope.
"""

from __future__ import unicode_literals

import os

from PyQt5.QtCore import pyqtSlot, QCoreApplication, QTimer

from E5Gui.E5Application import e5App

from .JsonServer import JsonServer

import Globals
import Preferences


class CodeAssistServer(JsonServer):
    """
    Class implementing the autocompletion interface to rope.
    """
    def __init__(self, plugin, parent=None):
        """
        Constructor
        
        @param plugin reference to the plugin object
        @type RefactoringRopePlugin
        @param parent parent
        @type QObject
        """
        super(CodeAssistServer, self).__init__(
            "CodeAssistServer", multiplex=True, parent=parent)
        
        self.__plugin = plugin
        self.__ui = parent
        
        self.__editorLanguageMapping = {}
        
        # attributes to store the resuls of the client side
        self.__completions = None
        self.__calltips = None
        
        self.__methodMapping = {
            "CompletionsResult": self.__processCompletionsResult,
            "CallTipsResult": self.__processCallTipsResult,
        }
        
        # Python 2
        interpreter = Preferences.getDebugger("PythonInterpreter")
        self.__startCodeAssistClient(interpreter, "Python2")
        
        # Python 3
        interpreter = Preferences.getDebugger("Python3Interpreter")
        self.__startCodeAssistClient(interpreter, "Python3")
    
    def __updateEditorLanguageMapping(self):
        """
        Private method to update the editor language to connection mapping.
        """
        self.__editorLanguageMapping = {}
        for name in self.connectionNames():
            if name == "Python2":
                self.__editorLanguageMapping.update({
                    "Python": "Python2",
                    "Python2": "Python2",
                    "Pygments|Python": "Python2",
                })
            elif name == "Python3":
                self.__editorLanguageMapping.update({
                    "Python3": "Python3",
                    "Pygments|Python 3": "Python3",
                })
    
    def isSupportedLanguage(self, language):
        """
        Public method to check, if the given language is supported.
        
        @param language editor programming language to check
        @type str
        @return flag indicating the support status
        @rtype bool
        """
        return language in self.__editorLanguageMapping
    
    def getCompletions(self, editor):
        """
        Public method to calculate the possible completions.
        
        @param editor reference to the editor object, that called this method
        @type QScintilla.Editor
        @return list of proposals
        @rtype list of str
        """
        # reset the completions buffer
        self.__completions = None
        
        language = editor.getLanguage()
        if language not in self.__editorLanguageMapping:
            return []
        idString = self.__editorLanguageMapping[language]
        
        filename = editor.getFileName()
        line, index = editor.getCursorPosition()
        source = editor.text()
        offset = len("".join(source.splitlines(True)[:line])) + index
        maxfixes = self.__plugin.getPreferences("MaxFixes")
        
        self.sendJson("getCompletions", {
            "FileName": filename,
            "Source": source,
            "Offset": offset,
            "MaxFixes": maxfixes,
        }, idString=idString)
        
        # emulate the synchronous behaviour
        timer = QTimer()
        timer.setSingleShot(True)
        timer.start(5000)           # 5s timeout
        while self.__completions is None and timer.isActive():
            QCoreApplication.processEvents()
        
        return [] if self.__completions is None else self.__completions
    
    def __processCompletionsResult(self, result):
        """
        Private method to process the completions sent by the client.
        
        @param result dictionary containing the result sent by the client
        @type dict
        """
        if "Error" in result:
            self.__completions = []
        else:
            self.__completions = result["Completions"]
    
    def getCallTips(self, pos, editor):
        """
        Public method to calculate calltips.
        
        @param pos position in the text for the calltip
        @type int
        @param editor reference to the editor object, that called this method
        @type QScintilla.Editor
        @return list of possible calltips
        @rtype list of str
        """
        # reset the calltips buffer
        self.__calltips = None
        
        language = editor.getLanguage()
        if language not in self.__editorLanguageMapping:
            return []
        idString = self.__editorLanguageMapping[language]
        
        filename = editor.getFileName()
        source = editor.text()
        line, index = editor.lineIndexFromPosition(pos)
        offset = len("".join(source.splitlines(True)[:line])) + index
        maxfixes = self.__plugin.getPreferences("CalltipsMaxFixes")
        
        self.sendJson("getCallTips", {
            "FileName": filename,
            "Source": source,
            "Offset": offset,
            "MaxFixes": maxfixes,
        }, idString=idString)
        
        # emulate the synchronous behaviour
        timer = QTimer()
        timer.setSingleShot(True)
        timer.start(5000)           # 5s timeout
        while self.__calltips is None and timer.isActive():
            QCoreApplication.processEvents()
        
        return [] if self.__calltips is None else self.__calltips
    
    def __processCallTipsResult(self, result):
        """
        Private method to process the calltips sent by the client.
        
        @param result dictionary containing the result sent by the client
        @type dict
        """
        if "Error" in result:
            self.__calltips = []
        else:
            self.__calltips = result["CallTips"]
    
    def reportChanged(self, filename, oldSource):
        """
        Public slot to report some changed sources.
        
        @param filename file name of the changed source
        @type str
        @param oldSource source code before the change
        @type str
        """
        editor = e5App().getObject("ViewManager").getOpenEditor(filename)
        if editor is not None:
            language = editor.getLanguage()
            if language in self.__editorLanguageMapping:
                idString = self.__editorLanguageMapping[language]
                
                self.sendJson("reportChanged", {
                    "FileName": filename,
                    "OldSource": oldSource,
                }, idString=idString)
    
    #######################################################################
    ## Methods below handle the network connection
    #######################################################################
    
    def handleCall(self, method, params):
        """
        Public method to handle a method call from the client.
        
        @param method requested method name
        @type str
        @param params dictionary with method specific parameters
        @type dict
        """
        self.__methodMapping[method](params)
    
    def __startCodeAssistClient(self, interpreter, idString):
        """
        Private method to start the code assist client with the given
        interpreter.
        
        @param interpreter interpreter to be used for the code assist client
        @type str
        @param idString id of the client to be started
        @type str
        """
        if interpreter:
            client = os.path.join(os.path.dirname(__file__),
                                  "CodeAssistClient.py")
            ok = self.startClient(interpreter, client,
                                  [Globals.getConfigDir()],
                                  idString=idString)
            if not ok:
                self.__ui.appendToStderr(self.tr(
                    "'{0}' is not supported because the configured interpreter"
                    " could not be started."
                ).format(idString))
        else:
            self.__ui.appendToStderr(self.tr(
                "'{0}' is not supported because no suitable interpreter is"
                " configured."
            ).format(idString))
    
    @pyqtSlot()
    def handleNewConnection(self):
        """
        Public slot for new incoming connections from a client.
        """
        super(CodeAssistServer, self).handleNewConnection()
        self.__updateEditorLanguageMapping()
    
    def activate(self):
        """
        Public method to activate the code assist server.
        
        Note: This method provides for some growth potential.
            Currently it is empty.
        """
        pass
    
    def deactivate(self):
        """
        Public method to deactivate the code assist server.
        """
        """
        Public method to shut down the code assist server.
        """
        self.stopAllClients()

eric ide

mercurial