Wed, 05 Oct 2022 09:39:15 +0200
Some minor corrections.
# -*- coding: utf-8 -*- # Copyright (c) 2017 - 2022 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing the Jedi client of eric7. """ import contextlib import sys SuppressedException = Exception modulePath = sys.argv[-1] # it is always the last parameter sys.path.insert(1, modulePath) try: import jedi except ImportError: sys.exit(42) from eric7.EricNetwork.EricJsonClient import EricJsonClient class JediClient(EricJsonClient): """ Class implementing the Jedi client of eric7. """ def __init__(self, host, port, idString): """ Constructor @param host ip address the background service is listening @type str @param port port of the background service @type int @param idString assigned client id to be sent back to the server in order to identify the connection @type str """ super().__init__(host, port, idString) self.__methodMapping = { "openProject": self.__openProject, "closeProject": self.__closeProject, "getCompletions": self.__getCompletions, "getCallTips": self.__getCallTips, "getDocumentation": self.__getDocumentation, "hoverHelp": self.__getHoverHelp, "gotoDefinition": self.__getAssignment, "gotoReferences": self.__getReferences, "renameVariable": self.__renameVariable, "extractVariable": self.__extractVariable, "inlineVariable": self.__inlineVariable, "extractFunction": self.__extractFunction, "applyRefactoring": self.__applyRefactoring, "cancelRefactoring": self.__cancelRefactoring, } self.__id = idString self.__project = None self.__refactorings = {} def handleCall(self, method, params): """ Public method to handle a method call from the server. @param method requested method name @type str @param params dictionary with method specific parameters @type dict """ self.__methodMapping[method](params) def __handleError(self, err): """ Private method to process an error. @param err exception object @type Exception or Warning @return dictionary containing the error information @rtype dict """ error = str(type(err)).split()[-1] error = error[1:-2].split(".")[-1] errorDict = { "Error": error, "ErrorString": str(err), } return errorDict def __openProject(self, params): """ Private method to create a jedi project and load its saved data. @param params dictionary containing the method parameters @type dict """ projectPath = params["ProjectPath"] self.__project = jedi.Project(projectPath) def __closeProject(self, params): """ Private method to save a jedi project's data. @param params dictionary containing the method parameters @type dict """ if self.__project is not None: self.__project.save() self.__project = None def __completionType(self, completion): """ Private method to assemble the completion type depending on the visibility indicated by the completion name. @param completion reference to the completion object @type jedi.api.classes.Completion @return modified completion type @rtype str """ if completion.name.startswith("__"): compType = "__" + completion.type elif completion.name.startswith("_"): compType = "_" + completion.type else: compType = completion.type return compType def __completionFullName(self, completion): """ Private method to extract the full completion name. @param completion reference to the completion object @type jedi.api.classes.Completion @return full completion name @rtype str """ fullName = completion.full_name fullName = ( fullName.replace("__main__", completion.module_name) if fullName else completion.module_name ) return fullName def __getCompletions(self, params): """ Private method to calculate possible completions. @param params dictionary containing the method parameters @type dict """ filename = params["FileName"] source = params["Source"] line = params["Line"] index = params["Index"] fuzzy = params["Fuzzy"] errorDict = {} response = [] script = jedi.Script(source, path=filename, project=self.__project) try: completions = script.complete(line, index, fuzzy=fuzzy) response = [ { "ModulePath": str(completion.module_path), "Name": completion.name, "FullName": self.__completionFullName(completion), "CompletionType": self.__completionType(completion), } for completion in completions if not ( completion.name.startswith("__") and completion.name.endswith("__") ) ] except SuppressedException as err: errorDict = self.__handleError(err) result = { "Completions": response, "CompletionText": params["CompletionText"], "FileName": filename, } result.update(errorDict) self.sendJson("CompletionsResult", result) def __getCallTips(self, params): """ Private method to calculate possible calltips. @param params dictionary containing the method parameters @type dict """ filename = params["FileName"] source = params["Source"] line = params["Line"] index = params["Index"] errorDict = {} calltips = [] script = jedi.Script(source, path=filename, project=self.__project) try: signatures = script.get_signatures(line, index) for signature in signatures: name = signature.name params = self.__extractParameters(signature) calltips.append("{0}{1}".format(name, params)) except SuppressedException as err: errorDict = self.__handleError(err) result = { "CallTips": calltips, } result.update(errorDict) self.sendJson("CallTipsResult", result) def __extractParameters(self, signature): """ Private method to extract the call parameter descriptions. @param signature a jedi signature object @type object @return a string with comma seperated parameter names and default values @rtype str """ try: params = ", ".join( [param.description.split("param ", 1)[-1] for param in signature.params] ) return "({0})".format(params) except AttributeError: # Empty strings as argspec suppress display of "definition" return " " def __getDocumentation(self, params): """ Private method to get some source code documentation. @param params dictionary containing the method parameters @type dict """ filename = params["FileName"] source = params["Source"] line = params["Line"] index = params["Index"] errorDict = {} docu = {} script = jedi.Script(source, path=filename, project=self.__project) try: definitions = script.infer(line, index) definition = definitions[0] # use the first one only docu = { "name": definition.full_name, "module": definition.module_name, "argspec": self.__extractParameters(definition), "docstring": definition.docstring(), } except SuppressedException as err: errorDict = self.__handleError(err) result = { "DocumentationDict": docu, } result.update(errorDict) self.sendJson("DocumentationResult", result) def __getHoverHelp(self, params): """ Private method to get some source code documentation. @param params dictionary containing the method parameters @type dict """ filename = params["FileName"] source = params["Source"] line = params["Line"] index = params["Index"] uid = params["Uuid"] script = jedi.Script(source, path=filename, project=self.__project) errorDict = {} helpText = "" try: helpText = script.help(line, index)[0].docstring() except SuppressedException as err: errorDict = self.__handleError(err) result = { "Line": line, "Index": index, "HoverHelp": helpText, "Uuid": uid, } result.update(errorDict) self.sendJson("HoverHelpResult", result) def __getAssignment(self, params): """ Private method to get the place a parameter is defined. @param params dictionary containing the method parameters @type dict """ filename = params["FileName"] source = params["Source"] line = params["Line"] index = params["Index"] uid = params["Uuid"] errorDict = {} gotoDefinition = {} script = jedi.Script(source, path=filename, project=self.__project) try: assignments = script.goto( line, index, follow_imports=True, follow_builtin_imports=True ) for assignment in assignments: if bool(assignment.module_path): gotoDefinition = { "ModulePath": str(assignment.module_path), "Line": (0 if assignment.line is None else assignment.line), "Column": assignment.column, } if ( gotoDefinition["ModulePath"] == filename and gotoDefinition["Line"] == line ): # user called for the definition itself # => send the references instead self.__getReferences(params) return break except SuppressedException as err: errorDict = self.__handleError(err) result = { "GotoDefinitionDict": gotoDefinition, "Uuid": uid, } result.update(errorDict) self.sendJson("GotoDefinitionResult", result) def __getReferences(self, params): """ Private method to get the places a parameter is referenced. @param params dictionary containing the method parameters @type dict """ filename = params["FileName"] source = params["Source"] line = params["Line"] index = params["Index"] uid = params["Uuid"] errorDict = {} gotoReferences = [] script = jedi.Script(source, path=filename, project=self.__project) try: references = script.get_references(line, index, include_builtins=False) for reference in references: if bool(reference.module_path): if ( reference.line == line and str(reference.module_path) == filename ): continue gotoReferences.append( { "ModulePath": str(reference.module_path), "Line": (0 if reference.line is None else reference.line), "Column": reference.column, "Code": reference.get_line_code(), } ) except SuppressedException as err: errorDict = self.__handleError(err) result = { "GotoReferencesList": gotoReferences, "Uuid": uid, } result.update(errorDict) self.sendJson("GotoReferencesResult", result) def __renameVariable(self, params): """ Private method to rename the variable under the cursor. @param params dictionary containing the method parameters @type dict """ filename = params["FileName"] source = params["Source"] line = params["Line"] index = params["Index"] uid = params["Uuid"] newName = params["NewName"] errorDict = {} diff = "" script = jedi.Script(source, path=filename, project=self.__project) try: refactoring = script.rename(line, index, new_name=newName) self.__refactorings[uid] = refactoring diff = refactoring.get_diff() except SuppressedException as err: errorDict = self.__handleError(err) result = { "Diff": diff, "Uuid": uid, } result.update(errorDict) self.sendJson("RefactoringDiff", result) def __extractVariable(self, params): """ Private method to extract a statement to a new variable. @param params dictionary containing the method parameters @type dict """ filename = params["FileName"] source = params["Source"] line = params["Line"] index = params["Index"] endLine = params["EndLine"] endIndex = params["EndIndex"] uid = params["Uuid"] newName = params["NewName"] errorDict = {} diff = "" script = jedi.Script(source, path=filename, project=self.__project) try: refactoring = script.extract_variable( line, index, new_name=newName, until_line=endLine, until_column=endIndex ) self.__refactorings[uid] = refactoring diff = refactoring.get_diff() except SuppressedException as err: errorDict = self.__handleError(err) result = { "Diff": diff, "Uuid": uid, } result.update(errorDict) self.sendJson("RefactoringDiff", result) def __inlineVariable(self, params): """ Private method to inline a variable statement. @param params dictionary containing the method parameters @type dict """ filename = params["FileName"] source = params["Source"] line = params["Line"] index = params["Index"] uid = params["Uuid"] errorDict = {} diff = "" script = jedi.Script(source, path=filename, project=self.__project) try: refactoring = script.inline(line, index) self.__refactorings[uid] = refactoring diff = refactoring.get_diff() except SuppressedException as err: errorDict = self.__handleError(err) result = { "Diff": diff, "Uuid": uid, } result.update(errorDict) self.sendJson("RefactoringDiff", result) def __extractFunction(self, params): """ Private method to extract an expression to a new function. @param params dictionary containing the method parameters @type dict """ filename = params["FileName"] source = params["Source"] line = params["Line"] index = params["Index"] endLine = params["EndLine"] endIndex = params["EndIndex"] uid = params["Uuid"] newName = params["NewName"] errorDict = {} diff = "" script = jedi.Script(source, path=filename, project=self.__project) try: refactoring = script.extract_function( line, index, new_name=newName, until_line=endLine, until_column=endIndex ) self.__refactorings[uid] = refactoring diff = refactoring.get_diff() except SuppressedException as err: errorDict = self.__handleError(err) result = { "Diff": diff, "Uuid": uid, } result.update(errorDict) self.sendJson("RefactoringDiff", result) def __applyRefactoring(self, params): """ Private method to apply a refactoring. @param params dictionary containing the method parameters @type dict """ uid = params["Uuid"] errorDict = {} try: refactoring = self.__refactorings[uid] refactoring.apply() ok = True except KeyError: ok = False except SuppressedException as err: errorDict = self.__handleError(err) result = { "result": ok, } result.update(errorDict) self.sendJson("RefactoringApplyResult", result) with contextlib.suppress(KeyError): del self.__refactorings[uid] def __cancelRefactoring(self, params): """ Private method to cancel a refactoring. @param params dictionary containing the method parameters @type dict """ uid = params["Uuid"] with contextlib.suppress(KeyError): del self.__refactorings[uid] if __name__ == "__main__": if len(sys.argv) != 5: print("Host, port, id and module path parameters are missing." " Abort.") sys.exit(1) host, port, idString = sys.argv[1:-1] client = JediClient(host, int(port), idString) # Start the main loop client.run() sys.exit(0) # # eflag: noqa = M801