From 90d2c0cbc7341c24880c7d2430ce711fed196324 Mon Sep 17 00:00:00 2001 From: Stephen Macke Date: Thu, 14 Sep 2023 16:52:31 -0700 Subject: [PATCH 1/5] further resolve definitions if most recent update is not the definition itself --- pylsp/plugins/definition.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/pylsp/plugins/definition.py b/pylsp/plugins/definition.py index a5ccbd70..17fa36a3 100644 --- a/pylsp/plugins/definition.py +++ b/pylsp/plugins/definition.py @@ -7,16 +7,32 @@ log = logging.getLogger(__name__) +def _resolve_definition(script, settings, maybe_defn): + while not maybe_defn.is_definition() and maybe_defn.module_path == script.path: + defns = script.goto( + follow_imports=settings.get("follow_imports", True), + follow_builtin_imports=settings.get("follow_builtin_imports", True), + line=maybe_defn.line, + column=maybe_defn.column, + ) + if len(defns) == 1: + maybe_defn = defns[0] + else: + break + return maybe_defn + + @hookimpl def pylsp_definitions(config, document, position): settings = config.plugin_settings("jedi_definition") code_position = _utils.position_to_jedi_linecolumn(document, position) - definitions = document.jedi_script(use_document_path=True).goto( + script = document.jedi_script(use_document_path=True) + definitions = script.goto( follow_imports=settings.get("follow_imports", True), follow_builtin_imports=settings.get("follow_builtin_imports", True), **code_position, ) - + definitions = [_resolve_definition(script, settings, d) for d in definitions] follow_builtin_defns = settings.get("follow_builtin_definitions", True) return [ { From c683eb19e1ac4160d2889493a73721c780943179 Mon Sep 17 00:00:00 2001 From: Stephen Macke Date: Fri, 15 Sep 2023 09:10:47 -0700 Subject: [PATCH 2/5] add test and types --- pylsp/plugins/definition.py | 21 ++++++++++++++++----- test/plugins/test_definitions.py | 21 +++++++++++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/pylsp/plugins/definition.py b/pylsp/plugins/definition.py index 17fa36a3..fcb55b10 100644 --- a/pylsp/plugins/definition.py +++ b/pylsp/plugins/definition.py @@ -1,13 +1,22 @@ # Copyright 2017-2020 Palantir Technologies, Inc. # Copyright 2021- Python Language Server Contributors. - +from __future__ import annotations import logging +from typing import Any, Dict, List, TYPE_CHECKING from pylsp import hookimpl, uris, _utils +if TYPE_CHECKING: + from jedi.api import Script + from jedi.api.classes import Name + from pylsp.config.config import Config + from pylsp.workspace import Document + log = logging.getLogger(__name__) -def _resolve_definition(script, settings, maybe_defn): +def _resolve_definition( + maybe_defn: Name, script: Script, settings: Dict[str, Any] +) -> Name: while not maybe_defn.is_definition() and maybe_defn.module_path == script.path: defns = script.goto( follow_imports=settings.get("follow_imports", True), @@ -23,7 +32,9 @@ def _resolve_definition(script, settings, maybe_defn): @hookimpl -def pylsp_definitions(config, document, position): +def pylsp_definitions( + config: Config, document: Document, position: Dict[str, int] +) -> List[Dict[str, Any]]: settings = config.plugin_settings("jedi_definition") code_position = _utils.position_to_jedi_linecolumn(document, position) script = document.jedi_script(use_document_path=True) @@ -32,7 +43,7 @@ def pylsp_definitions(config, document, position): follow_builtin_imports=settings.get("follow_builtin_imports", True), **code_position, ) - definitions = [_resolve_definition(script, settings, d) for d in definitions] + definitions = [_resolve_definition(d, script, settings) for d in definitions] follow_builtin_defns = settings.get("follow_builtin_definitions", True) return [ { @@ -47,7 +58,7 @@ def pylsp_definitions(config, document, position): ] -def _not_internal_definition(definition): +def _not_internal_definition(definition: Name) -> bool: return ( definition.line is not None and definition.column is not None diff --git a/test/plugins/test_definitions.py b/test/plugins/test_definitions.py index 34acc6a9..495971a7 100644 --- a/test/plugins/test_definitions.py +++ b/test/plugins/test_definitions.py @@ -21,6 +21,11 @@ def __init__(self): def add_member(self, id, name): self.members[id] = name + + +subscripted_before_reference = {} +subscripted_before_reference[0] = 0 +subscripted_before_reference """ @@ -40,6 +45,22 @@ def test_definitions(config, workspace): ) +def test_indirect_definitions(config, workspace): + # Over 'subscripted_before_reference' + cursor_pos = {"line": 16, "character": 0} + + # The definition of 'subscripted_before_reference' + def_range = { + "start": {"line": 14, "character": 0}, + "end": {"line": 14, "character": len("subscripted_before_reference")}, + } + + doc = Document(DOC_URI, workspace, DOC) + assert [{"uri": DOC_URI, "range": def_range}] == pylsp_definitions( + config, doc, cursor_pos + ) + + def test_builtin_definition(config, workspace): # Over 'i' in dict cursor_pos = {"line": 8, "character": 24} From 6c412a88843bcf494dc5c8149e3873196388c9f6 Mon Sep 17 00:00:00 2001 From: Stephen Macke Date: Sat, 16 Sep 2023 11:58:53 -0700 Subject: [PATCH 3/5] add limit --- pylsp/plugins/definition.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pylsp/plugins/definition.py b/pylsp/plugins/definition.py index fcb55b10..ffc1b00b 100644 --- a/pylsp/plugins/definition.py +++ b/pylsp/plugins/definition.py @@ -14,10 +14,15 @@ log = logging.getLogger(__name__) +MAX_JEDI_GOTO_HOPS = 100 + + def _resolve_definition( maybe_defn: Name, script: Script, settings: Dict[str, Any] ) -> Name: - while not maybe_defn.is_definition() and maybe_defn.module_path == script.path: + for _ in range(MAX_JEDI_GOTO_HOPS): + if maybe_defn.is_definition() or maybe_defn.module_path != script.path: + break defns = script.goto( follow_imports=settings.get("follow_imports", True), follow_builtin_imports=settings.get("follow_builtin_imports", True), From 26ddbdb69d9f0e8a6b1eeb96b6e1b67c83ac9566 Mon Sep 17 00:00:00 2001 From: Stephen Macke Date: Thu, 28 Sep 2023 23:26:23 -0700 Subject: [PATCH 4/5] add another test --- test/plugins/test_definitions.py | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/test/plugins/test_definitions.py b/test/plugins/test_definitions.py index 495971a7..0764250c 100644 --- a/test/plugins/test_definitions.py +++ b/test/plugins/test_definitions.py @@ -12,7 +12,7 @@ DOC = """def a(): pass -print a() +print(a()) class Directory(object): @@ -26,6 +26,16 @@ def add_member(self, id, name): subscripted_before_reference = {} subscripted_before_reference[0] = 0 subscripted_before_reference + + +def my_func(): + print('called') + +alias = my_func +my_list = [1, None, alias] +inception = my_list[2] + +inception() """ @@ -49,7 +59,8 @@ def test_indirect_definitions(config, workspace): # Over 'subscripted_before_reference' cursor_pos = {"line": 16, "character": 0} - # The definition of 'subscripted_before_reference' + # The definition of 'subscripted_before_reference', + # skipping intermediate writes to the most recent definition def_range = { "start": {"line": 14, "character": 0}, "end": {"line": 14, "character": len("subscripted_before_reference")}, @@ -61,6 +72,23 @@ def test_indirect_definitions(config, workspace): ) +def test_definition_with_multihop_inference_goto(config, workspace): + # Over 'subscripted_before_reference' + cursor_pos = {"line": 26, "character": 0} + + # The most recent definition of 'inception', + # ignoring alias hops + def_range = { + "start": {"line": 24, "character": 0}, + "end": {"line": 24, "character": len("inception")}, + } + + doc = Document(DOC_URI, workspace, DOC) + assert [{"uri": DOC_URI, "range": def_range}] == pylsp_definitions( + config, doc, cursor_pos + ) + + def test_builtin_definition(config, workspace): # Over 'i' in dict cursor_pos = {"line": 8, "character": 24} From 715111822c41ac397b3a54b6c9222dea654a7eae Mon Sep 17 00:00:00 2001 From: Stephen Macke Date: Fri, 29 Sep 2023 17:46:15 -0700 Subject: [PATCH 5/5] fix comment --- test/plugins/test_definitions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/plugins/test_definitions.py b/test/plugins/test_definitions.py index 0764250c..f0e9ffef 100644 --- a/test/plugins/test_definitions.py +++ b/test/plugins/test_definitions.py @@ -73,7 +73,7 @@ def test_indirect_definitions(config, workspace): def test_definition_with_multihop_inference_goto(config, workspace): - # Over 'subscripted_before_reference' + # Over 'inception()' cursor_pos = {"line": 26, "character": 0} # The most recent definition of 'inception',