From a4f8937d018a9f4dd2fb6c2cc7b1e03ac9e8a6ed Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Thu, 15 Feb 2024 15:30:00 -0800 Subject: [PATCH 1/6] Add support for whole-workspace 'find by reference' Signed-off-by: Danila Fedorin --- .../src/chpl-language-server.py | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/tools/chpl-language-server/src/chpl-language-server.py b/tools/chpl-language-server/src/chpl-language-server.py index fe028fe4a397..3fb7380e620f 100755 --- a/tools/chpl-language-server/src/chpl-language-server.py +++ b/tools/chpl-language-server/src/chpl-language-server.py @@ -355,12 +355,29 @@ class ResolvedPair: resolved_to: NodeAndRange +@dataclass +class References: + in_file: "FileInfo" + uses: List[NodeAndRange] + + def append(self, x: NodeAndRange): + self.uses.append(x) + + def clear(self): + self.uses.clear() + + def __iter__(self): + return iter(self.uses) + + class ContextContainer: def __init__(self, file: str, config: Optional["WorkspaceConfig"]): + self.config: Optional["WorkspaceConfig"] = config self.file_paths: List[str] = [] self.module_paths: List[str] = [file] self.context: chapel.Context = chapel.Context() self.file_infos: List["FileInfo"] = [] + self.global_uses: Dict[str, List[References]] = defaultdict(list) if config: file_config = config.for_file(file) @@ -413,7 +430,7 @@ class FileInfo: instantiation_segments: PositionList[ Tuple[NodeAndRange, chapel.TypedSignature] ] = field(init=False) - uses_here: Dict[str, List[NodeAndRange]] = field(init=False) + uses_here: Dict[str, References] = field(init=False) instantiations: Dict[str, Set[chapel.TypedSignature]] = field(init=False) siblings: chapel.SiblingMap = field(init=False) used_modules: List[chapel.Module] = field(init=False) @@ -423,6 +440,7 @@ def __post_init__(self): self.use_segments = PositionList(lambda x: x.ident.rng) self.def_segments = PositionList(lambda x: x.rng) self.instantiation_segments = PositionList(lambda x: x[0].rng) + self.uses_here = {} self.rebuild_index() def parse_file(self) -> List[chapel.AstNode]: @@ -442,6 +460,15 @@ def get_asts(self) -> List[chapel.AstNode]: with self.context.context.track_errors() as _: return self.parse_file() + def _get_use_container(self, uid: str) -> References: + if uid in self.uses_here: + return self.uses_here[uid] + + refs = References(self, []) + self.uses_here[uid] = refs + self.context.global_uses[uid].append(refs) + return refs + def _note_reference(self, node: Union[chapel.Dot, chapel.Identifier]): """ Given a node that can refer to another node, note what it refers @@ -451,7 +478,7 @@ def _note_reference(self, node: Union[chapel.Dot, chapel.Identifier]): if not to: return - self.uses_here[to.unique_id()].append(NodeAndRange(node)) + self._get_use_container(to.unique_id()).append(NodeAndRange(node)) self.use_segments.append( ResolvedPair(NodeAndRange(node), NodeAndRange(to)) ) @@ -523,8 +550,9 @@ def rebuild_index(self): # Use this class as an AST visitor to rebuild the use and definition segment # table, as well as the list of references. - self.uses_here = defaultdict(list) self.instantiations = defaultdict(set) + for _, refs in self.uses_here.items(): + refs.clear() self.use_segments.clear() self.def_segments.clear() self.visit(asts) @@ -1025,8 +1053,9 @@ async def get_refs(ls: ChapelLanguageServer, params: ReferenceParams): return None locations = [node_and_loc.get_location()] - for use in fi.uses_here[node_and_loc.node.unique_id()]: - locations.append(use.get_location()) + for uselist in fi.context.global_uses[node_and_loc.node.unique_id()]: + for use in uselist: + locations.append(use.get_location()) return locations @@ -1231,7 +1260,7 @@ async def document_highlight( highlights = [ DocumentHighlight(node_and_loc.rng, DocumentHighlightKind.Text) ] - for use in fi.uses_here[node_and_loc.node.unique_id()]: + for use in fi.uses_here.get(node_and_loc.node.unique_id(), []): highlights.append( DocumentHighlight(use.rng, DocumentHighlightKind.Text) ) From 63f6480b1b5a19a7c9e14c801012bc19a6d5078b Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Thu, 15 Feb 2024 15:30:14 -0800 Subject: [PATCH 2/6] Eagerly 'fault in' every file in a Context when finding references Signed-off-by: Danila Fedorin --- .../chpl-language-server/src/chpl-language-server.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tools/chpl-language-server/src/chpl-language-server.py b/tools/chpl-language-server/src/chpl-language-server.py index 3fb7380e620f..b7999439443a 100755 --- a/tools/chpl-language-server/src/chpl-language-server.py +++ b/tools/chpl-language-server/src/chpl-language-server.py @@ -643,6 +643,9 @@ def __init__(self, ls: "ChapelLanguageServer", json: Dict[str, Any]): self.files[key] = compile_commands[0] + def file_paths(self) -> Iterable[str]: + return self.files.keys() + def for_file(self, path: str) -> Optional[Dict[str, Any]]: if path in self.files: return self.files[path] @@ -738,6 +741,13 @@ def get_context(self, uri: str) -> ContextContainer: return context + def eagerly_process_all_files(self, context: ContextContainer): + cfg = context.config + if cfg: + for file in cfg.files: + self.get_file_info("file://" + file, do_update=False) + + def get_file_info( self, uri: str, do_update: bool = False ) -> Tuple[FileInfo, List[Any]]: @@ -1052,6 +1062,8 @@ async def get_refs(ls: ChapelLanguageServer, params: ReferenceParams): if not node_and_loc: return None + ls.eagerly_process_all_files(fi.context) + locations = [node_and_loc.get_location()] for uselist in fi.context.global_uses[node_and_loc.node.unique_id()]: for use in uselist: From f9e8022cca86e31782dfcc5b3ec1ae690b17453c Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Tue, 20 Feb 2024 09:45:03 -0800 Subject: [PATCH 3/6] Run black Signed-off-by: Danila Fedorin --- tools/chpl-language-server/src/chpl-language-server.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tools/chpl-language-server/src/chpl-language-server.py b/tools/chpl-language-server/src/chpl-language-server.py index b7999439443a..33743922cc97 100755 --- a/tools/chpl-language-server/src/chpl-language-server.py +++ b/tools/chpl-language-server/src/chpl-language-server.py @@ -515,7 +515,9 @@ def _collect_possibly_visible_decls(self): self.possibly_visible_decls.append(child) def _search_instantiations( - self, root: Union[chapel.AstNode, List[chapel.AstNode]], via: Optional[chapel.TypedSignature] = None + self, + root: Union[chapel.AstNode, List[chapel.AstNode]], + via: Optional[chapel.TypedSignature] = None, ): for node in chapel.preorder(root): if not isinstance(node, chapel.FnCall): @@ -747,7 +749,6 @@ def eagerly_process_all_files(self, context: ContextContainer): for file in cfg.files: self.get_file_info("file://" + file, do_update=False) - def get_file_info( self, uri: str, do_update: bool = False ) -> Tuple[FileInfo, List[Any]]: @@ -1352,9 +1353,7 @@ async def semantic_tokens_range( start_pos = location_to_range(ast.location()).start instantiation = fi.get_inst_segment_at_position(start_pos) tokens.extend( - ls.get_dead_code_tokens( - ast, fi.file_lines(), instantiation - ) + ls.get_dead_code_tokens(ast, fi.file_lines(), instantiation) ) return SemanticTokens(data=encode_deltas(tokens, 0, 0)) From 573377e2ebd6563c65b4f4ba8736a64785f21608 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Tue, 20 Feb 2024 09:54:43 -0800 Subject: [PATCH 4/6] Replace another use of 'uses_here' with global uses Signed-off-by: Danila Fedorin --- tools/chpl-language-server/src/chpl-language-server.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/chpl-language-server/src/chpl-language-server.py b/tools/chpl-language-server/src/chpl-language-server.py index 33743922cc97..21f8db90f3db 100755 --- a/tools/chpl-language-server/src/chpl-language-server.py +++ b/tools/chpl-language-server/src/chpl-language-server.py @@ -1214,8 +1214,9 @@ def add_to_edits(nr: NodeAndRange): edits[nr.get_uri()].append(TextEdit(nr.rng, params.new_name)) add_to_edits(node_and_loc) - for use in fi.uses_here[node_and_loc.node.unique_id()]: - add_to_edits(use) + for uselist in fi.context.global_uses[node_and_loc.node.unique_id()]: + for use in uselist: + add_to_edits(use) return WorkspaceEdit(changes=edits) From 37d5904f2b7d988f70ede4923a7707ac6e9790e5 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Tue, 20 Feb 2024 10:02:15 -0800 Subject: [PATCH 5/6] Use 'abspath' for files which Context reports relatively to cwd. Signed-off-by: Danila Fedorin --- tools/chpl-language-server/src/chpl-language-server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/chpl-language-server/src/chpl-language-server.py b/tools/chpl-language-server/src/chpl-language-server.py index 21f8db90f3db..8244bc44f98d 100755 --- a/tools/chpl-language-server/src/chpl-language-server.py +++ b/tools/chpl-language-server/src/chpl-language-server.py @@ -200,7 +200,7 @@ def completion_item_for_decl( def location_to_location(loc) -> Location: - return Location("file://" + loc.path(), location_to_range(loc)) + return Location("file://" + os.path.abspath(loc.path()), location_to_range(loc)) def get_symbol_information( @@ -345,7 +345,7 @@ def get_location(self): return Location(self.get_uri(), self.rng) def get_uri(self): - path = self.node.location().path() + path = os.path.abspath(self.node.location().path()) return f"file://{path}" From 0597af21f045108408e36157575ee297499d059c Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Tue, 20 Feb 2024 10:16:59 -0800 Subject: [PATCH 6/6] Eagerly fault in files for rename to be able to find references Signed-off-by: Danila Fedorin --- tools/chpl-language-server/src/chpl-language-server.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/chpl-language-server/src/chpl-language-server.py b/tools/chpl-language-server/src/chpl-language-server.py index 8244bc44f98d..e0c60abac60a 100755 --- a/tools/chpl-language-server/src/chpl-language-server.py +++ b/tools/chpl-language-server/src/chpl-language-server.py @@ -1213,6 +1213,8 @@ def add_to_edits(nr: NodeAndRange): edits[nr.get_uri()] = [] edits[nr.get_uri()].append(TextEdit(nr.rng, params.new_name)) + ls.eagerly_process_all_files(fi.context) + add_to_edits(node_and_loc) for uselist in fi.context.global_uses[node_and_loc.node.unique_id()]: for use in uselist: