diff --git a/jaclang/compiler/passes/main/fuse_typeinfo_pass.py b/jaclang/compiler/passes/main/fuse_typeinfo_pass.py index 590105dbb..efe0a8525 100644 --- a/jaclang/compiler/passes/main/fuse_typeinfo_pass.py +++ b/jaclang/compiler/passes/main/fuse_typeinfo_pass.py @@ -563,3 +563,5 @@ def exit_atom_trailer(self, node: ast.AtomTrailer) -> None: else: if left.type_sym_tab: right.name_spec.sym = left.type_sym_tab.lookup(right.sym_name) + if right.name_spec.sym: + right.name_spec.sym.add_use(right.name_spec) diff --git a/jaclang/compiler/passes/main/pyjac_ast_link_pass.py b/jaclang/compiler/passes/main/pyjac_ast_link_pass.py index 78d6259c4..00376fd79 100644 --- a/jaclang/compiler/passes/main/pyjac_ast_link_pass.py +++ b/jaclang/compiler/passes/main/pyjac_ast_link_pass.py @@ -162,6 +162,13 @@ def exit_ability_def(self, node: ast.AbilityDef) -> None: py_nodes=node.parent.signature.return_type.gen.py_ast, ) + if isinstance(node.decl_link, ast.Ability) and isinstance( + node.target, ast.ArchRefChain + ): + for arch in node.target.archs: + if arch.arch_name.sym: + arch.arch_name.sym.add_use(arch.arch_name) + def exit_param_var(self, node: ast.ParamVar) -> None: """Sub objects. diff --git a/jaclang/langserve/engine.py b/jaclang/langserve/engine.py index 0c7a3e64a..bffea3ef5 100644 --- a/jaclang/langserve/engine.py +++ b/jaclang/langserve/engine.py @@ -16,6 +16,7 @@ from jaclang.compiler.passes.tool import FuseCommentsPass, JacFormatPass from jaclang.langserve.sem_manager import SemTokManager from jaclang.langserve.utils import ( + add_unique_text_edit, collect_all_symbols_in_scope, create_range, find_deepest_symbol_node_at_pos, @@ -429,18 +430,16 @@ def rename_symbol( node_selected = self.modules[file_path].sem_manager.static_sem_tokens[index1][3] if node_selected and node_selected.sym: changes: dict[str, list[lspt.TextEdit]] = {} - for node in [*node_selected.sym.uses, node_selected.sym.defn[0]]: + for node in [ + *node_selected.sym.uses, + node_selected.sym.defn[0], + ]: key = uris.from_fs_path(node.loc.mod_path) - value = [ - lspt.TextEdit( - range=create_range(node.loc), - new_text=new_name, - ) - ] - if key in changes: - changes[key].extend(value) - else: - changes[key] = value + new_edit = lspt.TextEdit( + range=create_range(node.loc), + new_text=new_name, + ) + add_unique_text_edit(changes, key, new_edit) return lspt.WorkspaceEdit(changes=changes) return None diff --git a/jaclang/langserve/tests/fixtures/rename.jac b/jaclang/langserve/tests/fixtures/rename.jac new file mode 100644 index 000000000..ab1266410 --- /dev/null +++ b/jaclang/langserve/tests/fixtures/rename.jac @@ -0,0 +1,30 @@ +can foo; + + + +:can:foo{ + print("foo"); +} + + +obj out{ + has cnt :int; + can bar; + can baz; +} + + +:obj:out:can:bar{ + print("bar"); +} + +:obj:out:can:baz{ + print("baz"); +} + +with entry{ + foo(); + new = out(); + new.cnt; + out(1).bar(); +} \ No newline at end of file diff --git a/jaclang/langserve/tests/test_server.py b/jaclang/langserve/tests/test_server.py index 20a0ba49c..79f7a90e4 100644 --- a/jaclang/langserve/tests/test_server.py +++ b/jaclang/langserve/tests/test_server.py @@ -519,6 +519,7 @@ def test_rename_symbol(self) -> None: ), (12, 34, "circleRadius", "12:21-12:27", "12:30-12:36", "11:19-11:25"), (62, 14, "target_area", "65:43-65:56", "70:32-70:45", "62:5-62:18"), + (57, 33, "type_of_shape", "75:12-75:22", "27:8-27:18,", "57:23-57:33"), ] for tup in test_cases: line, char, new_name, *expected_refs = tup @@ -527,3 +528,40 @@ def test_rename_symbol(self) -> None: ) for expected in expected_refs: self.assertIn(expected, references) + + def test_rename_uses(self) -> None: + """Test that the rename is correct.""" + lsp = JacLangServer() + workspace_path = self.fixture_abs_path("") + workspace = Workspace(workspace_path, lsp) + lsp.lsp._workspace = workspace + + circle_file = uris.from_fs_path(self.fixture_abs_path("rename.jac")) + lsp.deep_check(circle_file) + # fmt: off + test_cases = [ + (0, 7, "func", "25:4-25:7", "0:4-0:7", "4:5-4:8",), + (4, 6, "func", "25:4-25:7", "0:4-0:7", "4:5-4:8",), + (25, 7, "func", "25:4-25:7", "0:4-0:7", "4:5-4:8",), + (10, 10, "canBar", "27:8-27:11", "10:8-10:11"), + (27, 9, "canBar", "27:8-27:11", "10:8-10:11"), + (9, 6, "canBar", "26:10-26:13", "28:4-28:7", "16:5-16:8", "9:4-9:7"), + (26, 11, "canBar", "26:10-26:13", "28:4-28:7", "16:5-16:8", "9:4-9:7"), + (16, 7, "canBar", "26:10-26:13", "28:4-28:7", "16:5-16:8", "9:4-9:7"), + (28, 6, "canBar", "26:10-26:13", "28:4-28:7", "16:5-16:8", "9:4-9:7"), + (11, 10, "canBar", "11:8-11:11", "16:13-16:16", "28:11-28:14"), + (16, 14, "canBar", "11:8-11:11", "16:13-16:16", "28:11-28:14"), + (28, 13, "canBar", "11:8-11:11", "16:13-16:16", "28:11-28:14"), + (12, 10, "canBaz", "12:8-12:11", "20:13-20:16"), + (20, 14, "canBaz", "12:8-12:11", "20:13-20:16"), + (26, 6, "count", "27:4-27:7", "26:4-26:7"), + (27, 5, "count", "27:4-27:7", "26:4-26:7"), + ] + # fmt: on + for tup in test_cases: + line, char, new_name, *expected_refs = tup + references = str( + lsp.rename_symbol(circle_file, lspt.Position(line, char), new_name) + ) + for expected in expected_refs: + self.assertIn(expected, references) diff --git a/jaclang/langserve/utils.py b/jaclang/langserve/utils.py index 144df45ce..883d5c3b6 100644 --- a/jaclang/langserve/utils.py +++ b/jaclang/langserve/utils.py @@ -627,3 +627,20 @@ def get_line_of_code(line_number: int, lines: list[str]) -> Optional[tuple[str, else first_non_space ) return None + + +def add_unique_text_edit( + changes: dict[str, list[lspt.TextEdit]], key: str, new_edit: lspt.TextEdit +) -> None: + """Add a new text edit to the changes dictionary if it is unique.""" + if key not in changes: + changes[key] = [new_edit] + else: + for existing_edit in changes[key]: + if ( + existing_edit.range.start == new_edit.range.start + and existing_edit.range.end == new_edit.range.end + and existing_edit.new_text == new_edit.new_text + ): + return + changes[key].append(new_edit)