jaclang 0.8.0__py3-none-any.whl → 0.8.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of jaclang might be problematic. Click here for more details.
- jaclang/cli/cli.py +11 -9
- jaclang/compiler/jac.lark +2 -12
- jaclang/compiler/larkparse/jac_parser.py +1 -1
- jaclang/compiler/parser.py +360 -521
- jaclang/compiler/passes/main/cfg_build_pass.py +2 -2
- jaclang/compiler/passes/main/def_impl_match_pass.py +14 -13
- jaclang/compiler/passes/main/def_use_pass.py +4 -7
- jaclang/compiler/passes/main/import_pass.py +3 -3
- jaclang/compiler/passes/main/inheritance_pass.py +2 -2
- jaclang/compiler/passes/main/pyast_gen_pass.py +196 -218
- jaclang/compiler/passes/main/pyast_load_pass.py +115 -311
- jaclang/compiler/passes/main/pyjac_ast_link_pass.py +8 -7
- jaclang/compiler/passes/main/sym_tab_build_pass.py +3 -3
- jaclang/compiler/passes/main/sym_tab_link_pass.py +4 -4
- jaclang/compiler/passes/main/tests/fixtures/symtab_link_tests/action/actions.jac +1 -5
- jaclang/compiler/passes/main/tests/fixtures/symtab_link_tests/main.jac +1 -8
- jaclang/compiler/passes/main/tests/test_cfg_build_pass.py +4 -2
- jaclang/compiler/passes/tool/doc_ir_gen_pass.py +197 -120
- jaclang/compiler/program.py +2 -7
- jaclang/compiler/tests/fixtures/fam.jac +2 -2
- jaclang/compiler/tests/fixtures/pkg_import_lib/__init__.jac +1 -0
- jaclang/compiler/tests/fixtures/pkg_import_lib/sub/__init__.jac +1 -0
- jaclang/compiler/tests/fixtures/pkg_import_lib/sub/helper.jac +3 -0
- jaclang/compiler/tests/fixtures/pkg_import_lib/tools.jac +3 -0
- jaclang/compiler/tests/fixtures/pkg_import_lib_py/__init__.py +11 -0
- jaclang/compiler/tests/fixtures/pkg_import_lib_py/sub/__init__.py +7 -0
- jaclang/compiler/tests/fixtures/pkg_import_lib_py/sub/helper.jac +3 -0
- jaclang/compiler/tests/fixtures/pkg_import_lib_py/tools.jac +3 -0
- jaclang/compiler/tests/fixtures/pkg_import_main.jac +10 -0
- jaclang/compiler/tests/fixtures/pkg_import_main_py.jac +11 -0
- jaclang/compiler/tests/test_importer.py +20 -0
- jaclang/compiler/tests/test_parser.py +1 -0
- jaclang/compiler/unitree.py +456 -304
- jaclang/langserve/engine.jac +498 -0
- jaclang/langserve/sem_manager.jac +309 -0
- jaclang/langserve/server.jac +186 -0
- jaclang/langserve/tests/server_test/test_lang_serve.py +6 -7
- jaclang/langserve/tests/server_test/utils.py +4 -1
- jaclang/langserve/tests/session.jac +294 -0
- jaclang/langserve/tests/test_sem_tokens.py +2 -2
- jaclang/langserve/tests/test_server.py +12 -7
- jaclang/langserve/utils.jac +51 -30
- jaclang/runtimelib/archetype.py +1 -1
- jaclang/runtimelib/builtin.py +17 -14
- jaclang/runtimelib/importer.py +26 -8
- jaclang/runtimelib/machine.py +96 -55
- jaclang/runtimelib/tests/fixtures/traversing_save.jac +7 -5
- jaclang/runtimelib/utils.py +3 -3
- jaclang/tests/fixtures/backward_edge_visit.jac +31 -0
- jaclang/tests/fixtures/builtin_printgraph.jac +85 -0
- jaclang/tests/fixtures/builtin_printgraph_json.jac +21 -0
- jaclang/tests/fixtures/builtin_printgraph_mermaid.jac +16 -0
- jaclang/tests/fixtures/chandra_bugs2.jac +20 -13
- jaclang/tests/fixtures/concurrency.jac +1 -1
- jaclang/tests/fixtures/edge_ability.jac +49 -0
- jaclang/tests/fixtures/guess_game.jac +1 -1
- jaclang/tests/fixtures/here_usage_error.jac +21 -0
- jaclang/tests/fixtures/here_visitor_usage.jac +21 -0
- jaclang/tests/fixtures/node_del.jac +30 -36
- jaclang/tests/fixtures/visit_traversal.jac +47 -0
- jaclang/tests/test_cli.py +12 -7
- jaclang/tests/test_language.py +91 -16
- jaclang/utils/helpers.py +14 -6
- jaclang/utils/lang_tools.py +2 -3
- jaclang/utils/tests/test_lang_tools.py +2 -1
- jaclang/utils/treeprinter.py +3 -4
- {jaclang-0.8.0.dist-info → jaclang-0.8.1.dist-info}/METADATA +4 -3
- {jaclang-0.8.0.dist-info → jaclang-0.8.1.dist-info}/RECORD +71 -55
- {jaclang-0.8.0.dist-info → jaclang-0.8.1.dist-info}/WHEEL +1 -1
- jaclang/langserve/engine.py +0 -553
- jaclang/langserve/sem_manager.py +0 -383
- jaclang/langserve/server.py +0 -167
- jaclang/langserve/tests/session.py +0 -255
- jaclang/tests/fixtures/builtin_dotgen.jac +0 -42
- jaclang/tests/fixtures/builtin_dotgen_json.jac +0 -21
- /jaclang/langserve/{__init__.py → __init__.jac} +0 -0
- {jaclang-0.8.0.dist-info → jaclang-0.8.1.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
"""Living Workspace of Jac project."""
|
|
2
|
+
|
|
3
|
+
import asyncio;
|
|
4
|
+
import logging;
|
|
5
|
+
import time;
|
|
6
|
+
import from concurrent.futures { ThreadPoolExecutor }
|
|
7
|
+
import from typing { Callable , Optional }
|
|
8
|
+
|
|
9
|
+
import jaclang.compiler.unitree as uni;
|
|
10
|
+
import from jaclang { JacMachineInterface as Jac }
|
|
11
|
+
import from jaclang.compiler.passes.main { CompilerMode as CMode }
|
|
12
|
+
import from jaclang.compiler.program { JacProgram }
|
|
13
|
+
import from jaclang.compiler.unitree { UniScopeNode }
|
|
14
|
+
import from sem_manager { SemTokManager }
|
|
15
|
+
import from jaclang.vendor.pygls { uris }
|
|
16
|
+
import from jaclang.vendor.pygls.server { LanguageServer }
|
|
17
|
+
|
|
18
|
+
import lsprotocol.types as lspt;
|
|
19
|
+
import utils;
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
"""Handles Jac module, semantic manager, and alert management."""
|
|
23
|
+
class ModuleManager {
|
|
24
|
+
"""Initialize ModuleManager."""
|
|
25
|
+
def init(self: ModuleManager, program: JacProgram, sem_managers: <>dict) -> None {
|
|
26
|
+
self.program = program;
|
|
27
|
+
self.sem_managers = sem_managers;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
"""Update modules in JacProgram's hub and semantic managers."""
|
|
31
|
+
def update(
|
|
32
|
+
self: ModuleManager, file_path: str, build: uni.Module, update_annexed: bool = True
|
|
33
|
+
) -> None {
|
|
34
|
+
file_path = file_path.removeprefix('file://');
|
|
35
|
+
self.program.mod.hub[file_path] = build;
|
|
36
|
+
self.sem_managers[file_path] = SemTokManager(ir=build);
|
|
37
|
+
if update_annexed {
|
|
38
|
+
for (p,mod) in self.program.mod.hub.items() { if p != file_path {
|
|
39
|
+
self.sem_managers[p] = SemTokManager(ir=mod);
|
|
40
|
+
} }
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
"""Remove errors and warnings for a specific file from the lists."""
|
|
45
|
+
def clear_alerts_for_file(self: ModuleManager, file_path_fs: str) -> None {
|
|
46
|
+
self.program.errors_had = [ e for e in self.program.errors_had if e.loc.mod_path != file_path_fs ];
|
|
47
|
+
self.program.warnings_had = [ w for w in self.program.warnings_had if w.loc.mod_path != file_path_fs ];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
"""Jac Language Server, manages JacProgram and LSP."""
|
|
53
|
+
class JacLangServer ( JacProgram , LanguageServer ) {
|
|
54
|
+
"""Initialize JacLangServer."""
|
|
55
|
+
def init(self: JacLangServer) -> None {
|
|
56
|
+
LanguageServer.init(self, 'jac-lsp', 'v0.1');
|
|
57
|
+
JacProgram.init(self);
|
|
58
|
+
self.executor = ThreadPoolExecutor();
|
|
59
|
+
self.tasks: <>dict[(str,asyncio.Task)] = {};
|
|
60
|
+
self.sem_managers: <>dict[(str,SemTokManager)] = {};
|
|
61
|
+
self.module_manager = ModuleManager(self, self.sem_managers);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
"""Return diagnostics for all files as a dict {uri: diagnostics}."""
|
|
65
|
+
@ property
|
|
66
|
+
def diagnostics(self: JacLangServer, ) -> <>dict[str, <>list] {
|
|
67
|
+
result = {};
|
|
68
|
+
for file_path in self.mod.hub { uri = uris.from_fs_path(file_path); result[uri] = utils.gen_diagnostics(uri, self.errors_had, self.warnings_had); }
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
"""Remove errors and warnings for a specific file from the lists."""
|
|
73
|
+
def _clear_alerts_for_file(self: JacLangServer, file_path_fs: str) -> None {
|
|
74
|
+
self.module_manager.clear_alerts_for_file(file_path_fs);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
"""Get IR for a file path."""
|
|
78
|
+
def get_ir(self: JacLangServer, file_path: str) -> Optional[uni.Module] {
|
|
79
|
+
file_path = file_path.removeprefix('file://');
|
|
80
|
+
return self.mod.hub.get(file_path);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
"""Update modules in JacProgram's hub and semantic managers."""
|
|
84
|
+
def update_modules(
|
|
85
|
+
self: JacLangServer, file_path: str, build: uni.Module, need: bool = True
|
|
86
|
+
) -> None {
|
|
87
|
+
self.log_py(f"'Updating modules for '{file_path}");
|
|
88
|
+
self.module_manager.update(file_path, build, update_annexed=need);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
"""Rebuild a file (syntax only)."""
|
|
92
|
+
def quick_check(self: JacLangServer, file_path: str) -> bool {
|
|
93
|
+
try { file_path_fs = file_path.removeprefix('file://'); document = self.workspace.get_text_document(file_path); self._clear_alerts_for_file(file_path_fs); build = self.compile_from_str(
|
|
94
|
+
source_str=document.source, file_path=document.path, mode=CMode.PARSE
|
|
95
|
+
); self.update_modules(file_path_fs, build, need=False); self.publish_diagnostics(
|
|
96
|
+
file_path, utils.gen_diagnostics(
|
|
97
|
+
file_path, self.errors_had, self.warnings_had
|
|
98
|
+
)
|
|
99
|
+
); return len(self.errors_had) == 0; } except Exception as e { self.log_error(f"'Error during syntax check: '{e}"); return False; }
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
"""Rebuild a file and its dependencies (typecheck)."""
|
|
103
|
+
def deep_check(
|
|
104
|
+
self: JacLangServer, file_path: str, annex_view: Optional[str] = None
|
|
105
|
+
) -> bool {
|
|
106
|
+
try { start_time = time.time(); file_path_fs = file_path.removeprefix('file://'); document = self.workspace.get_text_document(file_path); self._clear_alerts_for_file(file_path_fs); build = self.compile_from_str(
|
|
107
|
+
source_str=document.source, file_path=document.path, mode=CMode.TYPECHECK
|
|
108
|
+
); self.update_modules(file_path_fs, build); if build.annexable_by {
|
|
109
|
+
return self.deep_check(
|
|
110
|
+
uris.from_fs_path(build.annexable_by), annex_view=file_path
|
|
111
|
+
);
|
|
112
|
+
} self.publish_diagnostics(
|
|
113
|
+
annex_view if annex_view else file_path , utils.gen_diagnostics(
|
|
114
|
+
annex_view if annex_view else file_path , self.errors_had, self.warnings_had
|
|
115
|
+
)
|
|
116
|
+
); if annex_view {
|
|
117
|
+
self.publish_diagnostics(
|
|
118
|
+
file_path, utils.gen_diagnostics(
|
|
119
|
+
file_path, self.errors_had, self.warnings_had
|
|
120
|
+
)
|
|
121
|
+
);
|
|
122
|
+
} self.log_py(
|
|
123
|
+
f"'PROFILE: Deep check took '{(time.time() - start_time)}' seconds.'"
|
|
124
|
+
); return len(self.errors_had) == 0; } except Exception as e { self.log_error(f"'Error during deep check: '{e}"); return False; }
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
"""Analyze and publish diagnostics."""
|
|
128
|
+
async def launch_quick_check(self: JacLangServer, uri: str) -> None {
|
|
129
|
+
await asyncio.get_event_loop().run_in_executor(
|
|
130
|
+
self.executor, self.quick_check, uri
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
"""Analyze and publish diagnostics."""
|
|
135
|
+
async def launch_deep_check(self: JacLangServer, uri: str) -> None {
|
|
136
|
+
async def run_in_executor(
|
|
137
|
+
func: Callable[([str,Optional[str]],bool)], file_path: str, annex_view: Optional[str] = None
|
|
138
|
+
) -> None {
|
|
139
|
+
loop = asyncio.get_event_loop();
|
|
140
|
+
await loop.run_in_executor(self.executor, func, file_path, annex_view);
|
|
141
|
+
}
|
|
142
|
+
if uri in self.tasks and not self.tasks[uri].done() {
|
|
143
|
+
self.log_py(f"'Canceling '{uri}' deep check...'");
|
|
144
|
+
self.tasks[uri].cancel();
|
|
145
|
+
del (self.tasks[uri],) ;
|
|
146
|
+
}
|
|
147
|
+
self.log_py(f"'Analyzing '{uri}'...'");
|
|
148
|
+
task = asyncio.create_task(run_in_executor(self.deep_check, uri));
|
|
149
|
+
self.tasks[uri] = task;
|
|
150
|
+
await task;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
"""Return completion for a file."""
|
|
154
|
+
def get_completion(
|
|
155
|
+
self: JacLangServer, file_path: str, position: lspt.Position, completion_trigger: Optional[str]
|
|
156
|
+
) -> lspt.CompletionList {
|
|
157
|
+
document = self.workspace.get_text_document(file_path);
|
|
158
|
+
mod_ir = self.get_ir(file_path);
|
|
159
|
+
if not mod_ir {
|
|
160
|
+
return lspt.CompletionList(is_incomplete=False, items=[]);
|
|
161
|
+
}
|
|
162
|
+
current_line = document.lines[position.line];
|
|
163
|
+
current_pos = position.character;
|
|
164
|
+
current_symbol_path = utils.parse_symbol_path(current_line, current_pos);
|
|
165
|
+
builtin_mod = next(
|
|
166
|
+
( mod for (name,mod) in self.mod.hub.items() if 'builtins' in name )
|
|
167
|
+
);
|
|
168
|
+
builtin_tab = builtin_mod.sym_tab;
|
|
169
|
+
assert isinstance(builtin_tab, UniScopeNode) ;
|
|
170
|
+
completion_items = [];
|
|
171
|
+
node_selected = utils.find_deepest_symbol_node_at_pos(
|
|
172
|
+
mod_ir, position.line, (position.character - 2)
|
|
173
|
+
);
|
|
174
|
+
mod_tab = mod_ir.sym_tab if not node_selected else node_selected.sym_tab ;
|
|
175
|
+
current_symbol_table = mod_tab;
|
|
176
|
+
if completion_trigger == '.' {
|
|
177
|
+
if current_symbol_path {
|
|
178
|
+
temp_tab = mod_tab;
|
|
179
|
+
for symbol in current_symbol_path { if symbol == 'self' {
|
|
180
|
+
is_ability_def = temp_tab
|
|
181
|
+
if isinstance(temp_tab, uni.ImplDef)
|
|
182
|
+
else temp_tab.find_parent_of_type(uni.ImplDef)
|
|
183
|
+
;
|
|
184
|
+
if not is_ability_def {
|
|
185
|
+
archi_owner = mod_tab.find_parent_of_type(uni.Archetype);
|
|
186
|
+
temp_tab = archi_owner.sym_tab
|
|
187
|
+
if archi_owner and archi_owner.sym_tab
|
|
188
|
+
else mod_tab
|
|
189
|
+
;
|
|
190
|
+
continue;
|
|
191
|
+
} else {
|
|
192
|
+
archi_owner = is_ability_def.decl_link.find_parent_of_type(uni.Archetype)
|
|
193
|
+
if is_ability_def.decl_link
|
|
194
|
+
else None
|
|
195
|
+
;
|
|
196
|
+
temp_tab = archi_owner.sym_tab
|
|
197
|
+
if archi_owner and archi_owner.sym_tab
|
|
198
|
+
else temp_tab
|
|
199
|
+
;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
} symb = temp_tab.lookup(symbol); if symb {
|
|
203
|
+
fetc_tab = symb.fetch_sym_tab;
|
|
204
|
+
if fetc_tab {
|
|
205
|
+
temp_tab = fetc_tab;
|
|
206
|
+
} else {
|
|
207
|
+
temp_tab = symb.defn[0].type_sym_tab
|
|
208
|
+
if symb.defn[0].type_sym_tab
|
|
209
|
+
else temp_tab
|
|
210
|
+
;
|
|
211
|
+
}
|
|
212
|
+
} else {
|
|
213
|
+
break;
|
|
214
|
+
} }
|
|
215
|
+
completion_items += utils.collect_all_symbols_in_scope(
|
|
216
|
+
temp_tab, up_tree=False
|
|
217
|
+
);
|
|
218
|
+
if isinstance(temp_tab, uni.Archetype) and temp_tab.base_classes {
|
|
219
|
+
base = [];
|
|
220
|
+
for base_name in temp_tab.base_classes { if isinstance(base_name, uni.Name) and base_name.sym {
|
|
221
|
+
base.append(base_name.sym);
|
|
222
|
+
} }
|
|
223
|
+
for base_class_symbol in base { if base_class_symbol.fetch_sym_tab {
|
|
224
|
+
completion_items += utils.collect_all_symbols_in_scope(
|
|
225
|
+
base_class_symbol.fetch_sym_tab, up_tree=False
|
|
226
|
+
);
|
|
227
|
+
} }
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
} elif node_selected and node_selected.find_parent_of_type(uni.Archetype)
|
|
231
|
+
or node_selected.find_parent_of_type(uni.ImplDef)
|
|
232
|
+
{
|
|
233
|
+
self_symbol = [lspt.CompletionItem(label='self', kind=lspt.CompletionItemKind.Variable)];
|
|
234
|
+
} else {
|
|
235
|
+
self_symbol = [];
|
|
236
|
+
}
|
|
237
|
+
return lspt.CompletionList(is_incomplete=False, items=completion_items);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
"""Rename module."""
|
|
241
|
+
def rename_module(self: JacLangServer, old_path: str, new_path: str) -> None {
|
|
242
|
+
if old_path in self.mod.hub and new_path != old_path {
|
|
243
|
+
self.mod.hub[new_path] = self.mod.hub[old_path];
|
|
244
|
+
self.sem_managers[new_path] = self.sem_managers[old_path];
|
|
245
|
+
del (self.mod.hub[old_path],) ;
|
|
246
|
+
del (self.sem_managers[old_path],) ;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
"""Delete module."""
|
|
251
|
+
def delete_module(self: JacLangServer, uri: str) -> None {
|
|
252
|
+
if uri in self.mod.hub {
|
|
253
|
+
del (self.mod.hub[uri],) ;
|
|
254
|
+
}
|
|
255
|
+
if uri in self.sem_managers {
|
|
256
|
+
del (self.sem_managers[uri],) ;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
"""Return formatted jac."""
|
|
261
|
+
def formatted_jac(self: JacLangServer, file_path: str) -> <>list[lspt.TextEdit] {
|
|
262
|
+
try { document = self.workspace.get_text_document(file_path); formatted_text = JacProgram.jac_str_formatter(
|
|
263
|
+
source_str=document.source, file_path=document.path
|
|
264
|
+
); } except Exception as e { self.log_error(f"'Error during formatting: '{e}"); formatted_text = document.source; }
|
|
265
|
+
return [lspt.TextEdit(
|
|
266
|
+
range=lspt.Range(
|
|
267
|
+
start=lspt.Position(line=0, character=0), end=lspt.Position(
|
|
268
|
+
line=(len(document.source.splitlines()) + 1), character=0
|
|
269
|
+
)
|
|
270
|
+
), new_text=formatted_text
|
|
271
|
+
)];
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
"""Return hover information for a file."""
|
|
275
|
+
def get_hover_info(
|
|
276
|
+
self: JacLangServer, file_path: str, position: lspt.Position
|
|
277
|
+
) -> Optional[lspt.Hover] {
|
|
278
|
+
file_path_fs = file_path.removeprefix('file://');
|
|
279
|
+
if file_path_fs not in self.mod.hub {
|
|
280
|
+
return None;
|
|
281
|
+
}
|
|
282
|
+
sem_mgr = self.sem_managers.get(file_path_fs);
|
|
283
|
+
if not sem_mgr {
|
|
284
|
+
return None;
|
|
285
|
+
}
|
|
286
|
+
token_index = utils.find_index(
|
|
287
|
+
sem_mgr.sem_tokens, position.line, position.character
|
|
288
|
+
);
|
|
289
|
+
if token_index is None {
|
|
290
|
+
return None;
|
|
291
|
+
}
|
|
292
|
+
node_selected = sem_mgr.static_sem_tokens[token_index][3];
|
|
293
|
+
value = self.get_node_info(node_selected) if node_selected else None ;
|
|
294
|
+
if value {
|
|
295
|
+
return lspt.Hover(
|
|
296
|
+
contents=lspt.MarkupContent(
|
|
297
|
+
kind=lspt.MarkupKind.PlainText, value=f"{value}"
|
|
298
|
+
)
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
return None;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
"""Extract meaningful information from the AST node."""
|
|
305
|
+
def get_node_info(self: JacLangServer, <>node: uni.AstSymbolNode) -> Optional[str] {
|
|
306
|
+
try { if isinstance(<>node, uni.NameAtom) {
|
|
307
|
+
<>node = <>node.name_of;
|
|
308
|
+
} access = (<>node.sym.access.value + ' ') if <>node.sym else None ; node_info = f"'('{access if access else '' }{<>node.sym_category.value}') '{<>node.sym_name}"; if <>node.name_spec.clean_type {
|
|
309
|
+
node_info += f"': '{<>node.name_spec.clean_type}";
|
|
310
|
+
} if isinstance(<>node, uni.AstDocNode) and <>node.doc {
|
|
311
|
+
node_info += f"'\n'{<>node.doc.value}";
|
|
312
|
+
} if isinstance(<>node, uni.Ability) and <>node.signature {
|
|
313
|
+
node_info += f"'\n'{<>node.signature.unparse()}";
|
|
314
|
+
} } except AttributeError as e { self.log_warning(f"'Attribute error when accessing node attributes: '{e}"); }
|
|
315
|
+
return node_info.strip();
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
"""Return document symbols for a file."""
|
|
319
|
+
def get_outline(self: JacLangServer, file_path: str) -> <>list[lspt.DocumentSymbol] {
|
|
320
|
+
file_path_fs = file_path.removeprefix('file://');
|
|
321
|
+
if file_path_fs in self.mod.hub
|
|
322
|
+
and (root_node := self.mod.hub[file_path_fs].sym_tab)
|
|
323
|
+
{
|
|
324
|
+
return utils.get_symbols_for_outline(root_node);
|
|
325
|
+
}
|
|
326
|
+
return [];
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
"""Return definition location for a file."""
|
|
330
|
+
def get_definition(
|
|
331
|
+
self: JacLangServer, file_path: str, position: lspt.Position
|
|
332
|
+
) -> Optional[lspt.Location] {
|
|
333
|
+
file_path_fs = file_path.removeprefix('file://');
|
|
334
|
+
if file_path_fs not in self.mod.hub {
|
|
335
|
+
return None;
|
|
336
|
+
}
|
|
337
|
+
sem_mgr = self.sem_managers.get(file_path_fs);
|
|
338
|
+
if not sem_mgr {
|
|
339
|
+
return None;
|
|
340
|
+
}
|
|
341
|
+
token_index = utils.find_index(
|
|
342
|
+
sem_mgr.sem_tokens, position.line, position.character
|
|
343
|
+
);
|
|
344
|
+
if token_index is None {
|
|
345
|
+
return None;
|
|
346
|
+
}
|
|
347
|
+
node_selected = sem_mgr.static_sem_tokens[token_index][3];
|
|
348
|
+
if node_selected {
|
|
349
|
+
if isinstance(node_selected, uni.Name)
|
|
350
|
+
and node_selected.parent
|
|
351
|
+
and isinstance(node_selected.parent, uni.ModulePath)
|
|
352
|
+
{
|
|
353
|
+
spec = node_selected.parent.parent.abs_path;
|
|
354
|
+
if spec {
|
|
355
|
+
spec = spec[ 5 : ] if spec.startswith('File:') else spec ;
|
|
356
|
+
return lspt.Location(
|
|
357
|
+
uri=uris.from_fs_path(spec), range=lspt.Range(
|
|
358
|
+
start=lspt.Position(line=0, character=0), end=lspt.Position(line=0, character=0)
|
|
359
|
+
)
|
|
360
|
+
);
|
|
361
|
+
} else {
|
|
362
|
+
return None;
|
|
363
|
+
}
|
|
364
|
+
} elif node_selected.parent
|
|
365
|
+
and isinstance(node_selected.parent, uni.ModuleItem)
|
|
366
|
+
{
|
|
367
|
+
path = node_selected.parent.abs_path
|
|
368
|
+
or node_selected.parent.from_mod_path.abs_path
|
|
369
|
+
;
|
|
370
|
+
loc_range = (0,0,0,0);
|
|
371
|
+
if path and loc_range {
|
|
372
|
+
path = path[ 5 : ] if path.startswith('File:') else path ;
|
|
373
|
+
return lspt.Location(
|
|
374
|
+
uri=uris.from_fs_path(path), range=lspt.Range(
|
|
375
|
+
start=lspt.Position(
|
|
376
|
+
line=loc_range[0], character=loc_range[1]
|
|
377
|
+
), end=lspt.Position(line=loc_range[2], character=loc_range[3])
|
|
378
|
+
)
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
} elif isinstance(node_selected, uni.ElementStmt) {
|
|
382
|
+
return None;
|
|
383
|
+
}
|
|
384
|
+
decl_node = node_selected.parent.body.target
|
|
385
|
+
if node_selected.parent
|
|
386
|
+
and isinstance(node_selected.parent, uni.AstImplNeedingNode)
|
|
387
|
+
and isinstance(node_selected.parent.body, uni.ImplDef)
|
|
388
|
+
|
|
389
|
+
else node_selected.sym.decl
|
|
390
|
+
if node_selected.sym and node_selected.sym.decl
|
|
391
|
+
else node_selected
|
|
392
|
+
|
|
393
|
+
;
|
|
394
|
+
if isinstance(decl_node, list) {
|
|
395
|
+
valid_path = decl_node[0].loc.mod_path;
|
|
396
|
+
} else {
|
|
397
|
+
valid_path = decl_node.loc.mod_path;
|
|
398
|
+
}
|
|
399
|
+
decl_uri = uris.from_fs_path(valid_path);
|
|
400
|
+
if isinstance(decl_node, list) {
|
|
401
|
+
valid_range = decl_node[0].loc;
|
|
402
|
+
} else {
|
|
403
|
+
valid_range = decl_node.loc;
|
|
404
|
+
}
|
|
405
|
+
try { decl_range = utils.create_range(valid_range); } except ValueError { return None; }
|
|
406
|
+
decl_location = lspt.Location(uri=decl_uri, range=decl_range);
|
|
407
|
+
return decl_location;
|
|
408
|
+
} else {
|
|
409
|
+
return None;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
"""Return references for a file."""
|
|
414
|
+
def get_references(
|
|
415
|
+
self: JacLangServer, file_path: str, position: lspt.Position
|
|
416
|
+
) -> <>list[lspt.Location] {
|
|
417
|
+
file_path_fs = file_path.removeprefix('file://');
|
|
418
|
+
if file_path_fs not in self.mod.hub {
|
|
419
|
+
return [];
|
|
420
|
+
}
|
|
421
|
+
sem_mgr = self.sem_managers.get(file_path_fs);
|
|
422
|
+
if not sem_mgr {
|
|
423
|
+
return [];
|
|
424
|
+
}
|
|
425
|
+
index1 = utils.find_index(sem_mgr.sem_tokens, position.line, position.character);
|
|
426
|
+
if index1 is None {
|
|
427
|
+
return [];
|
|
428
|
+
}
|
|
429
|
+
node_selected = sem_mgr.static_sem_tokens[index1][3];
|
|
430
|
+
if node_selected and node_selected.sym {
|
|
431
|
+
list_of_references: <>list[lspt.Location] = [ lspt.Location(
|
|
432
|
+
uri=uris.from_fs_path(<>node.loc.mod_path), range=utils.create_range(<>node.loc)
|
|
433
|
+
) for <>node in node_selected.sym.uses ];
|
|
434
|
+
return list_of_references;
|
|
435
|
+
}
|
|
436
|
+
return [];
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
"""Rename a symbol in a file."""
|
|
440
|
+
def rename_symbol(
|
|
441
|
+
self: JacLangServer, file_path: str, position: lspt.Position, new_name: str
|
|
442
|
+
) -> Optional[lspt.WorkspaceEdit] {
|
|
443
|
+
file_path_fs = file_path.removeprefix('file://');
|
|
444
|
+
if file_path_fs not in self.mod.hub {
|
|
445
|
+
return None;
|
|
446
|
+
}
|
|
447
|
+
sem_mgr = self.sem_managers.get(file_path_fs);
|
|
448
|
+
if not sem_mgr {
|
|
449
|
+
return None;
|
|
450
|
+
}
|
|
451
|
+
index1 = utils.find_index(sem_mgr.sem_tokens, position.line, position.character);
|
|
452
|
+
if index1 is None {
|
|
453
|
+
return None;
|
|
454
|
+
}
|
|
455
|
+
node_selected = sem_mgr.static_sem_tokens[index1][3];
|
|
456
|
+
if node_selected and node_selected.sym {
|
|
457
|
+
changes: <>dict[(str,<>list[lspt.TextEdit])] = {};
|
|
458
|
+
for <>node in [*node_selected.sym.uses,node_selected.sym.defn[0]] { key = uris.from_fs_path(<>node.loc.mod_path); new_edit = lspt.TextEdit(
|
|
459
|
+
range=utils.create_range(<>node.loc), new_text=new_name
|
|
460
|
+
); utils.add_unique_text_edit(changes, key, new_edit); }
|
|
461
|
+
return lspt.WorkspaceEdit(changes=changes);
|
|
462
|
+
}
|
|
463
|
+
return None;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
"""Return semantic tokens for a file."""
|
|
467
|
+
def get_semantic_tokens(self: JacLangServer, file_path: str) -> lspt.SemanticTokens {
|
|
468
|
+
file_path_fs = file_path.removeprefix('file://');
|
|
469
|
+
sem_mgr = self.sem_managers.get(file_path_fs);
|
|
470
|
+
if not sem_mgr {
|
|
471
|
+
return lspt.SemanticTokens(data=[]);
|
|
472
|
+
}
|
|
473
|
+
return lspt.SemanticTokens(data=sem_mgr.sem_tokens);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
"""Log an error message."""
|
|
477
|
+
def log_error(self: JacLangServer, message: str) -> None {
|
|
478
|
+
self.show_message_log(message, lspt.MessageType.Error);
|
|
479
|
+
self.show_message(message, lspt.MessageType.Error);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
"""Log a warning message."""
|
|
483
|
+
def log_warning(self: JacLangServer, message: str) -> None {
|
|
484
|
+
self.show_message_log(message, lspt.MessageType.Warning);
|
|
485
|
+
self.show_message(message, lspt.MessageType.Warning);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
"""Log an info message."""
|
|
489
|
+
def log_info(self: JacLangServer, message: str) -> None {
|
|
490
|
+
self.show_message_log(message, lspt.MessageType.Info);
|
|
491
|
+
self.show_message(message, lspt.MessageType.Info);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
"""Log a message."""
|
|
495
|
+
def log_py(self: JacLangServer, message: str) -> None {
|
|
496
|
+
logging.info(message);
|
|
497
|
+
}
|
|
498
|
+
}
|