From 8bc0af8de3e059486e8afbceb743de871ae763e0 Mon Sep 17 00:00:00 2001 From: z80 Date: Sat, 14 Dec 2024 21:04:09 -0500 Subject: [PATCH] Move signature code to dedicated handler --- tests/test_info.py | 3 +- vyper_lsp/analyzer/AstAnalyzer.py | 77 ------------------------- vyper_lsp/handlers/signatures.py | 93 +++++++++++++++++++++++++++++++ vyper_lsp/main.py | 4 +- 4 files changed, 98 insertions(+), 79 deletions(-) create mode 100644 vyper_lsp/handlers/signatures.py diff --git a/tests/test_info.py b/tests/test_info.py index cff8ebc..c012056 100644 --- a/tests/test_info.py +++ b/tests/test_info.py @@ -2,6 +2,7 @@ from pygls.workspace import Document from vyper_lsp.ast import AST from vyper_lsp.analyzer.AstAnalyzer import AstAnalyzer +from vyper_lsp.handlers.signatures import SignatureHandler def test_signature_help(ast: AST): @@ -23,7 +24,7 @@ def foobar(): self.foo(self.baz(1), 2) """ ast.build_ast(src) - analyzer = AstAnalyzer(ast) + analyzer = SignatureHandler(ast) doc = Document(uri="", source=src) diff --git a/vyper_lsp/analyzer/AstAnalyzer.py b/vyper_lsp/analyzer/AstAnalyzer.py index a4207d7..ad3997e 100644 --- a/vyper_lsp/analyzer/AstAnalyzer.py +++ b/vyper_lsp/analyzer/AstAnalyzer.py @@ -57,83 +57,6 @@ def __init__(self, ast: AST) -> None: else: self.diagnostics_enabled = True - def _handle_internal_fn_signature( - self, current_line: str, fn_name: str - ) -> Optional[SignatureHelp]: - node = self.ast.find_function_declaration_node_for_name(fn_name) - if not node: - return None - - fn_name = node.name - parameters = [] - - fn_label = node.node_source_code.split(":\n")[0].removeprefix("def ") - - for arg in node.args.args: - start_index = fn_label.find(arg.arg) - end_index = start_index + len(arg.arg) - parameters.append( - ParameterInformation(label=(start_index, end_index), documentation=None) - ) - active_parameter = current_line.split("(")[-1].count(",") - return SignatureHelp( - signatures=[ - SignatureInformation( - label=fn_label, - parameters=parameters, - documentation=None, - active_parameter=active_parameter or 0, - ) - ], - active_signature=0, - ) - - def _handle_imported_fn_signature( - self, current_line: str, module: str, fn_name: str - ) -> Optional[SignatureHelp]: - if module in self.ast.imports: - if fn := self.ast.imports[module].functions[fn_name]: - logger.info(f"getting signature for {fn_name}") - logger.info(fn.decl_node) - node: FunctionDef = fn.decl_node - label = node.node_source_code.split("def ")[1].split(":\n")[0] - parameters = [] - for arg in node.args.args: - parameters.append( - ParameterInformation(label=arg.arg, documentation=None) - ) - active_parameter = current_line.split("(")[-1].count(",") - return SignatureHelp( - signatures=[ - SignatureInformation( - label=label, - parameters=parameters, - documentation=None, - active_parameter=active_parameter or 0, - ) - ], - active_signature=0, - ) - - def signature_help( - self, doc: Document, params: SignatureHelpParams - ) -> Optional[SignatureHelp]: - # TODO: Implement checking external functions, module functions, and interfaces - current_line = doc.lines[params.position.line] - expression = get_expression_at_cursor( - current_line, params.position.character - 1 - ) - parsed = utils.parse_fncall_expression(expression) - if parsed is None: - return None - module, fn_name = parsed - - # this returns for all external functions - if module == "self": - return self._handle_internal_fn_signature(current_line, fn_name) - else: - return self._handle_imported_fn_signature(current_line, module, fn_name) - def _dot_completions_for_module( self, element: str, top_level_node=None, line: str = "" ) -> List[CompletionItem]: diff --git a/vyper_lsp/handlers/signatures.py b/vyper_lsp/handlers/signatures.py new file mode 100644 index 0000000..9f647c7 --- /dev/null +++ b/vyper_lsp/handlers/signatures.py @@ -0,0 +1,93 @@ +import logging + +from pygls.workspace import Document +from vyper.ast import FunctionDef +from typing import Optional + +from lsprotocol.types import ParameterInformation, SignatureHelp, SignatureHelpParams, SignatureInformation +from vyper_lsp import utils +from vyper_lsp.ast import AST +from vyper_lsp.utils import get_expression_at_cursor + +logger = logging.getLogger("vyper-lsp") + +class SignatureHandler: + def __init__(self, ast: AST): + self.ast = ast + + def _handle_internal_fn_signature( + self, current_line: str, fn_name: str + ) -> Optional[SignatureHelp]: + node = self.ast.find_function_declaration_node_for_name(fn_name) + if not node: + return None + + fn_name = node.name + parameters = [] + + fn_label = node.node_source_code.split(":\n")[0].removeprefix("def ") + + for arg in node.args.args: + start_index = fn_label.find(arg.arg) + end_index = start_index + len(arg.arg) + parameters.append( + ParameterInformation(label=(start_index, end_index), documentation=None) + ) + active_parameter = current_line.split("(")[-1].count(",") + return SignatureHelp( + signatures=[ + SignatureInformation( + label=fn_label, + parameters=parameters, + documentation=None, + active_parameter=active_parameter or 0, + ) + ], + active_signature=0, + ) + + def _handle_imported_fn_signature( + self, current_line: str, module: str, fn_name: str + ) -> Optional[SignatureHelp]: + if module in self.ast.imports: + if fn := self.ast.imports[module].functions[fn_name]: + logger.info(f"getting signature for {fn_name}") + logger.info(fn.decl_node) + node: FunctionDef = fn.decl_node + label = node.node_source_code.split("def ")[1].split(":\n")[0] + parameters = [] + for arg in node.args.args: + parameters.append( + ParameterInformation(label=arg.arg, documentation=None) + ) + active_parameter = current_line.split("(")[-1].count(",") + return SignatureHelp( + signatures=[ + SignatureInformation( + label=label, + parameters=parameters, + documentation=None, + active_parameter=active_parameter or 0, + ) + ], + active_signature=0, + ) + + def signature_help( + self, doc: Document, params: SignatureHelpParams + ) -> Optional[SignatureHelp]: + # TODO: Implement checking external functions, module functions, and interfaces + current_line = doc.lines[params.position.line] + expression = get_expression_at_cursor( + current_line, params.position.character - 1 + ) + parsed = utils.parse_fncall_expression(expression) + if parsed is None: + return None + module, fn_name = parsed + + # this returns for all external functions + if module == "self": + return self._handle_internal_fn_signature(current_line, fn_name) + else: + return self._handle_imported_fn_signature(current_line, module, fn_name) diff --git a/vyper_lsp/main.py b/vyper_lsp/main.py index b17f532..5f4838f 100755 --- a/vyper_lsp/main.py +++ b/vyper_lsp/main.py @@ -31,6 +31,7 @@ from packaging.version import Version from pygls.server import LanguageServer from vyper_lsp.analyzer.AstAnalyzer import AstAnalyzer +from vyper_lsp.handlers.signatures import SignatureHandler from vyper_lsp.analyzer.SourceAnalyzer import SourceAnalyzer from vyper_lsp.debounce import Debouncer @@ -53,6 +54,7 @@ completer = ast_analyzer source_analyzer = SourceAnalyzer() +signature_handler = SignatureHandler(ast) debouncer = Debouncer(wait=0.5) @@ -152,7 +154,7 @@ def hover(ls: LanguageServer, params: HoverParams): ) def signature_help(ls: LanguageServer, params: SignatureHelpParams): document = ls.workspace.get_text_document(params.text_document.uri) - signature_info = ast_analyzer.signature_help(document, params) + signature_info = signature_handler.signature_help(document, params) if signature_info: return signature_info