jaclang 0.8.6__py3-none-any.whl → 0.8.8__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.md +3 -3
- jaclang/cli/cli.py +37 -37
- jaclang/cli/cmdreg.py +45 -140
- jaclang/compiler/constant.py +0 -1
- jaclang/compiler/jac.lark +3 -6
- jaclang/compiler/larkparse/jac_parser.py +2 -2
- jaclang/compiler/parser.py +213 -34
- jaclang/compiler/passes/main/__init__.py +2 -4
- jaclang/compiler/passes/main/def_use_pass.py +0 -4
- jaclang/compiler/passes/main/predynamo_pass.py +221 -0
- jaclang/compiler/passes/main/pyast_gen_pass.py +83 -55
- jaclang/compiler/passes/main/pyast_load_pass.py +66 -40
- jaclang/compiler/passes/main/sym_tab_build_pass.py +1 -1
- jaclang/compiler/passes/main/tests/fixtures/checker/import_sym.jac +2 -0
- jaclang/compiler/passes/main/tests/fixtures/checker/import_sym_test.jac +6 -0
- jaclang/compiler/passes/main/tests/fixtures/checker/imported_sym.jac +5 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_arg_param_match.jac +37 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_arity.jac +18 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_binary_op.jac +21 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_call_expr_class.jac +12 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_cat_is_animal.jac +18 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_cyclic_symbol.jac +4 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_expr_call.jac +9 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_float.jac +7 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_import_missing_module.jac +13 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_magic_call.jac +17 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_mod_path.jac +8 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_param_types.jac +11 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_self_type.jac +9 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_sym_inherit.jac +42 -0
- jaclang/compiler/passes/main/tests/fixtures/predynamo_fix3.jac +43 -0
- jaclang/compiler/passes/main/tests/fixtures/predynamo_where_assign.jac +13 -0
- jaclang/compiler/passes/main/tests/fixtures/predynamo_where_return.jac +11 -0
- jaclang/compiler/passes/main/tests/test_checker_pass.py +265 -0
- jaclang/compiler/passes/main/tests/test_predynamo_pass.py +57 -0
- jaclang/compiler/passes/main/type_checker_pass.py +36 -61
- jaclang/compiler/passes/tool/doc_ir_gen_pass.py +204 -44
- jaclang/compiler/passes/tool/jac_formatter_pass.py +119 -69
- jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +3 -3
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +4 -5
- jaclang/compiler/passes/tool/tests/fixtures/tagbreak.jac +171 -11
- jaclang/compiler/passes/transform.py +12 -8
- jaclang/compiler/program.py +14 -6
- jaclang/compiler/tests/fixtures/jac_import_py_files.py +4 -0
- jaclang/compiler/tests/fixtures/jac_module.jac +3 -0
- jaclang/compiler/tests/fixtures/multiple_syntax_errors.jac +10 -0
- jaclang/compiler/tests/fixtures/python_module.py +1 -0
- jaclang/compiler/tests/test_importer.py +39 -0
- jaclang/compiler/tests/test_parser.py +49 -0
- jaclang/compiler/type_system/operations.py +104 -0
- jaclang/compiler/type_system/type_evaluator.py +470 -47
- jaclang/compiler/type_system/type_utils.py +246 -0
- jaclang/compiler/type_system/types.py +58 -2
- jaclang/compiler/unitree.py +79 -94
- jaclang/langserve/engine.jac +253 -230
- jaclang/langserve/server.jac +46 -15
- jaclang/langserve/tests/fixtures/circle.jac +3 -3
- jaclang/langserve/tests/fixtures/circle_err.jac +3 -3
- jaclang/langserve/tests/fixtures/circle_pure.test.jac +3 -3
- jaclang/langserve/tests/fixtures/completion_test_err.jac +10 -0
- jaclang/langserve/tests/server_test/circle_template.jac +80 -0
- jaclang/langserve/tests/server_test/glob_template.jac +4 -0
- jaclang/langserve/tests/server_test/test_lang_serve.py +154 -312
- jaclang/langserve/tests/server_test/utils.py +153 -116
- jaclang/langserve/tests/test_dev_server.py +1 -1
- jaclang/langserve/tests/test_server.py +30 -86
- jaclang/langserve/utils.jac +56 -63
- jaclang/runtimelib/machine.py +7 -0
- jaclang/runtimelib/meta_importer.py +27 -1
- jaclang/runtimelib/tests/fixtures/custom_access_validation.jac +1 -1
- jaclang/runtimelib/tests/fixtures/savable_object.jac +2 -2
- jaclang/settings.py +18 -14
- jaclang/tests/fixtures/abc_check.jac +3 -3
- jaclang/tests/fixtures/arch_rel_import_creation.jac +12 -12
- jaclang/tests/fixtures/chandra_bugs2.jac +3 -3
- jaclang/tests/fixtures/create_dynamic_archetype.jac +13 -13
- jaclang/tests/fixtures/jac_run_py_bugs.py +18 -0
- jaclang/tests/fixtures/jac_run_py_import.py +13 -0
- jaclang/tests/fixtures/lambda_arg_annotation.jac +15 -0
- jaclang/tests/fixtures/lambda_self.jac +18 -0
- jaclang/tests/fixtures/maxfail_run_test.jac +4 -4
- jaclang/tests/fixtures/params/param_syntax_err.jac +9 -0
- jaclang/tests/fixtures/params/test_complex_params.jac +42 -0
- jaclang/tests/fixtures/params/test_failing_kwonly.jac +207 -0
- jaclang/tests/fixtures/params/test_failing_posonly.jac +116 -0
- jaclang/tests/fixtures/params/test_failing_varargs.jac +300 -0
- jaclang/tests/fixtures/params/test_kwonly_params.jac +29 -0
- jaclang/tests/fixtures/py2jac_params.py +8 -0
- jaclang/tests/fixtures/run_test.jac +4 -4
- jaclang/tests/test_cli.py +103 -18
- jaclang/tests/test_language.py +74 -16
- jaclang/utils/helpers.py +47 -2
- jaclang/utils/module_resolver.py +11 -1
- jaclang/utils/test.py +8 -0
- jaclang/utils/treeprinter.py +0 -18
- {jaclang-0.8.6.dist-info → jaclang-0.8.8.dist-info}/METADATA +3 -3
- {jaclang-0.8.6.dist-info → jaclang-0.8.8.dist-info}/RECORD +99 -62
- {jaclang-0.8.6.dist-info → jaclang-0.8.8.dist-info}/WHEEL +1 -1
- jaclang/compiler/passes/main/inheritance_pass.py +0 -131
- jaclang/langserve/dev_engine.jac +0 -645
- jaclang/langserve/dev_server.jac +0 -201
- jaclang/langserve/tests/server_test/code_test.py +0 -0
- {jaclang-0.8.6.dist-info → jaclang-0.8.8.dist-info}/entry_points.txt +0 -0
jaclang/langserve/engine.jac
CHANGED
|
@@ -8,8 +8,10 @@ import from typing { Callable, Optional }
|
|
|
8
8
|
|
|
9
9
|
import jaclang.compiler.unitree as uni;
|
|
10
10
|
import from jaclang { JacMachineInterface as Jac }
|
|
11
|
-
import from jaclang.compiler.constant {SymbolType}
|
|
11
|
+
import from jaclang.compiler.constant { SymbolType }
|
|
12
12
|
import from jaclang.compiler.program { JacProgram }
|
|
13
|
+
import from jaclang.compiler.type_system.type_utils { get_completion_items }
|
|
14
|
+
import from jaclang.compiler.type_system.types { ClassType, TypeBase }
|
|
13
15
|
import from jaclang.compiler.unitree { UniScopeNode }
|
|
14
16
|
import from sem_manager { SemTokManager }
|
|
15
17
|
import from jaclang.vendor.pygls { uris }
|
|
@@ -22,7 +24,7 @@ import utils;
|
|
|
22
24
|
"""Handles Jac module, semantic manager, and alert management."""
|
|
23
25
|
class ModuleManager {
|
|
24
26
|
"""Initialize ModuleManager."""
|
|
25
|
-
def init(self: ModuleManager, program: JacProgram, sem_managers:
|
|
27
|
+
def init(self: ModuleManager, program: JacProgram, sem_managers: dict) -> None {
|
|
26
28
|
self.program = program;
|
|
27
29
|
self.sem_managers = sem_managers;
|
|
28
30
|
}
|
|
@@ -34,7 +36,6 @@ class ModuleManager {
|
|
|
34
36
|
build: uni.Module,
|
|
35
37
|
update_annexed: bool = True
|
|
36
38
|
) -> None {
|
|
37
|
-
file_path = file_path.removeprefix('file://');
|
|
38
39
|
self.program.mod.hub[file_path] = build;
|
|
39
40
|
if update_annexed {
|
|
40
41
|
self.sem_managers[file_path] = SemTokManager(ir=build);
|
|
@@ -63,31 +64,32 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
63
64
|
LanguageServer.init(self, 'jac-lsp', 'v0.1');
|
|
64
65
|
JacProgram.init(self);
|
|
65
66
|
self.executor = ThreadPoolExecutor();
|
|
66
|
-
self.tasks:
|
|
67
|
-
self.sem_managers:
|
|
67
|
+
self.tasks: dict[(str, asyncio.Task)] = {};
|
|
68
|
+
self.sem_managers: dict[(str, SemTokManager)] = {};
|
|
68
69
|
self.module_manager = ModuleManager(self, self.sem_managers);
|
|
70
|
+
self._quick_tasks: dict[str, asyncio.Task] = {};
|
|
71
|
+
self._deep_tasks: dict[str, asyncio.Task] = {};
|
|
69
72
|
}
|
|
70
73
|
|
|
71
74
|
"""Return diagnostics for all files as a dict {uri: diagnostics}."""
|
|
72
75
|
@ property
|
|
73
|
-
def diagnostics(self: JacLangServer, ) ->
|
|
76
|
+
def diagnostics(self: JacLangServer, ) -> dict[str, list] {
|
|
74
77
|
result = {};
|
|
75
78
|
for file_path in self.mod.hub {
|
|
76
79
|
uri = uris.from_fs_path(file_path);
|
|
77
80
|
result[uri] =
|
|
78
|
-
utils.gen_diagnostics(
|
|
81
|
+
utils.gen_diagnostics(file_path, self.errors_had, self.warnings_had);
|
|
79
82
|
}
|
|
80
83
|
return result;
|
|
81
84
|
}
|
|
82
85
|
|
|
83
86
|
"""Remove errors and warnings for a specific file from the lists."""
|
|
84
|
-
def _clear_alerts_for_file(self: JacLangServer,
|
|
85
|
-
self.module_manager.clear_alerts_for_file(
|
|
87
|
+
def _clear_alerts_for_file(self: JacLangServer, file_path: str) -> None {
|
|
88
|
+
self.module_manager.clear_alerts_for_file(file_path);
|
|
86
89
|
}
|
|
87
90
|
|
|
88
91
|
"""Get IR for a file path."""
|
|
89
92
|
def get_ir(self: JacLangServer, file_path: str) -> Optional[uni.Module] {
|
|
90
|
-
file_path = file_path.removeprefix('file://');
|
|
91
93
|
return self.mod.hub.get(file_path);
|
|
92
94
|
}
|
|
93
95
|
|
|
@@ -105,17 +107,19 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
105
107
|
"""Rebuild a file (syntax only)."""
|
|
106
108
|
def quick_check(self: JacLangServer, file_path: str) -> bool {
|
|
107
109
|
try {
|
|
108
|
-
file_path_fs = file_path.removeprefix('file://');
|
|
109
110
|
document = self.workspace.get_text_document(file_path);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
self.
|
|
111
|
+
fs_path = document.path;
|
|
112
|
+
|
|
113
|
+
self._clear_alerts_for_file(fs_path);
|
|
114
|
+
build = self.compile(use_str=document.source, file_path=fs_path);
|
|
115
|
+
self.update_modules(fs_path, build, need=False);
|
|
116
|
+
# to display diagnostics, it needs URI starts with "file://"
|
|
113
117
|
self.publish_diagnostics(
|
|
114
118
|
file_path,
|
|
115
|
-
utils.gen_diagnostics(
|
|
119
|
+
utils.gen_diagnostics(fs_path, self.errors_had, self.warnings_had)
|
|
116
120
|
);
|
|
117
121
|
build_errors =
|
|
118
|
-
[ e for e in self.errors_had if e.loc.mod_path ==
|
|
122
|
+
[ e for e in self.errors_had if e.loc.mod_path == fs_path];
|
|
119
123
|
return len(build_errors) == 0;
|
|
120
124
|
} except Exception as e {
|
|
121
125
|
self.log_error(f"'Error during syntax check: '{e}");
|
|
@@ -131,29 +135,31 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
131
135
|
) -> bool {
|
|
132
136
|
try {
|
|
133
137
|
start_time = time.time();
|
|
134
|
-
file_path_fs = file_path.removeprefix('file://');
|
|
135
138
|
document = self.workspace.get_text_document(file_path);
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
self.
|
|
139
|
+
fs_path = document.path;
|
|
140
|
+
|
|
141
|
+
self._clear_alerts_for_file(fs_path);
|
|
142
|
+
build = self.compile(use_str=document.source, file_path=document.path,type_check=True);
|
|
143
|
+
self.update_modules(fs_path, build);
|
|
139
144
|
if build.annexable_by {
|
|
140
145
|
return self.deep_check(
|
|
141
146
|
uris.from_fs_path(build.annexable_by),
|
|
142
|
-
annex_view=
|
|
147
|
+
annex_view=fs_path
|
|
143
148
|
);
|
|
144
149
|
}
|
|
145
150
|
self.publish_diagnostics(
|
|
146
|
-
|
|
151
|
+
# to display diagnostic , it need URI starts with "file://"
|
|
152
|
+
uris.from_fs_path(annex_view) if annex_view else uris.from_fs_path(fs_path) ,
|
|
147
153
|
utils.gen_diagnostics(
|
|
148
|
-
annex_view if annex_view else
|
|
154
|
+
annex_view if annex_view else fs_path,
|
|
149
155
|
self.errors_had,
|
|
150
156
|
self.warnings_had
|
|
151
157
|
)
|
|
152
158
|
);
|
|
153
159
|
if annex_view {
|
|
154
160
|
self.publish_diagnostics(
|
|
155
|
-
|
|
156
|
-
utils.gen_diagnostics(
|
|
161
|
+
uris.from_fs_path(fs_path) ,
|
|
162
|
+
utils.gen_diagnostics(fs_path, self.errors_had, self.warnings_had)
|
|
157
163
|
);
|
|
158
164
|
}
|
|
159
165
|
self.log_py(
|
|
@@ -166,151 +172,184 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
166
172
|
}
|
|
167
173
|
}
|
|
168
174
|
|
|
169
|
-
"""Analyze and publish diagnostics."""
|
|
170
|
-
async def launch_quick_check(self: JacLangServer, uri: str) -> bool {
|
|
171
|
-
|
|
172
|
-
self.
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
175
|
+
"""Analyze and publish diagnostics with debouncing."""
|
|
176
|
+
async def launch_quick_check(self: JacLangServer, uri: str, delay: float = 0.5) -> bool {
|
|
177
|
+
try {
|
|
178
|
+
return await self._debounced_run(self.quick_check, uri, delay, self._quick_tasks);
|
|
179
|
+
} except asyncio.CancelledError {
|
|
180
|
+
return False;
|
|
181
|
+
} except Exception as e {
|
|
182
|
+
self.log_error(f" 'Error during quick check launch: ' {e} ");
|
|
183
|
+
return False;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
"""Analyze and publish diagnostics with debouncing."""
|
|
187
|
+
async def launch_deep_check(
|
|
188
|
+
self: JacLangServer,
|
|
189
|
+
uri: str,
|
|
190
|
+
delay: float = 1.0,
|
|
191
|
+
annex_view: Optional[str] = None
|
|
192
|
+
) -> bool {
|
|
193
|
+
def deep_check_wrapper(file_uri: str) -> bool {
|
|
194
|
+
return self.deep_check(file_uri, annex_view);
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
return await self._debounced_run(deep_check_wrapper, uri, delay, self._deep_tasks);
|
|
198
|
+
} except asyncio.CancelledError {
|
|
199
|
+
return False;
|
|
200
|
+
} except Exception as e {
|
|
201
|
+
self.log_error(f" 'Error during deep check launch: ' {e} ");
|
|
202
|
+
}
|
|
176
203
|
}
|
|
177
204
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
file_path: str,
|
|
183
|
-
annex_view: Optional[str] = None
|
|
184
|
-
) -> None {
|
|
185
|
-
loop = asyncio.get_event_loop();
|
|
186
|
-
await loop.run_in_executor(self.executor, func, file_path, annex_view);
|
|
205
|
+
def get_token_at_position(self: JacLangServer, file_path: str, position: lspt.Position) -> Optional[uni.AstNode] {
|
|
206
|
+
fs_path = uris.to_fs_path(file_path);
|
|
207
|
+
if fs_path not in self.mod.hub {
|
|
208
|
+
return None;
|
|
187
209
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
del (self.tasks[uri], ) ;
|
|
210
|
+
sem_mgr = self.sem_managers.get(fs_path);
|
|
211
|
+
if not sem_mgr {
|
|
212
|
+
return None;
|
|
192
213
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
214
|
+
token_index =
|
|
215
|
+
utils.find_index(sem_mgr.sem_tokens, position.line, position.character);
|
|
216
|
+
if token_index is None {
|
|
217
|
+
return None;
|
|
218
|
+
}
|
|
219
|
+
node_selected = sem_mgr.static_sem_tokens[token_index][3];
|
|
220
|
+
return node_selected;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
def debug(self: JacLangServer, msg: str) -> None {
|
|
224
|
+
self.log_py("[DEBUG] " + ("-" * 80));
|
|
225
|
+
self.log_py(f"[DEBUG] {msg}");
|
|
226
|
+
self.log_py("[DEBUG] " + ("-" * 80));
|
|
197
227
|
}
|
|
198
228
|
|
|
199
229
|
"""Return completion for a file."""
|
|
200
230
|
def get_completion(
|
|
201
231
|
self: JacLangServer,
|
|
202
|
-
|
|
232
|
+
file_uri: str,
|
|
203
233
|
position: lspt.Position,
|
|
204
234
|
completion_trigger: Optional[str]
|
|
205
235
|
) -> lspt.CompletionList {
|
|
236
|
+
self.debug("getting completion for " + file_uri + " at " + str(position));
|
|
237
|
+
|
|
206
238
|
try {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
if
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
assert isinstance(builtin_tab, UniScopeNode) ;
|
|
221
|
-
completion_items = [];
|
|
222
|
-
node_selected =
|
|
223
|
-
utils.find_deepest_symbol_node_at_pos(
|
|
224
|
-
mod_ir,
|
|
225
|
-
position.line,
|
|
226
|
-
(position.character - 2)
|
|
227
|
-
);
|
|
228
|
-
mod_tab = mod_ir.sym_tab if not node_selected else node_selected.sym_tab;
|
|
229
|
-
current_symbol_table = mod_tab;
|
|
230
|
-
if completion_trigger == '.' {
|
|
231
|
-
if current_symbol_path {
|
|
232
|
-
temp_tab = mod_tab;
|
|
233
|
-
for symbol in current_symbol_path {
|
|
234
|
-
if symbol == 'self' {
|
|
235
|
-
is_ability_def =
|
|
236
|
-
temp_tab
|
|
237
|
-
if isinstance(temp_tab, uni.ImplDef)
|
|
238
|
-
else temp_tab.find_parent_of_type(uni.ImplDef);
|
|
239
|
-
if not is_ability_def {
|
|
240
|
-
archi_owner =
|
|
241
|
-
mod_tab.find_parent_of_type(uni.Archetype);
|
|
242
|
-
temp_tab =
|
|
243
|
-
archi_owner.sym_tab
|
|
244
|
-
if archi_owner and archi_owner.sym_tab
|
|
245
|
-
else mod_tab;
|
|
246
|
-
continue;
|
|
247
|
-
} else {
|
|
248
|
-
archi_owner =
|
|
249
|
-
is_ability_def.decl_link.find_parent_of_type(
|
|
250
|
-
uni.Archetype
|
|
251
|
-
)
|
|
252
|
-
if is_ability_def.decl_link
|
|
253
|
-
else None;
|
|
254
|
-
temp_tab =
|
|
255
|
-
archi_owner.sym_tab
|
|
256
|
-
if archi_owner and archi_owner.sym_tab
|
|
257
|
-
else temp_tab;
|
|
258
|
-
continue;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
symb = temp_tab.lookup(symbol);
|
|
262
|
-
if symb {
|
|
263
|
-
fetc_tab = symb.symbol_table;
|
|
264
|
-
if fetc_tab {
|
|
265
|
-
temp_tab = fetc_tab;
|
|
266
|
-
} else {
|
|
267
|
-
temp_tab =
|
|
268
|
-
symb.defn[0].type_sym_tab
|
|
269
|
-
if symb.defn[0].type_sym_tab
|
|
270
|
-
else temp_tab;
|
|
271
|
-
}
|
|
272
|
-
} else {
|
|
273
|
-
break;
|
|
239
|
+
file_path = uris.to_fs_path(file_uri);
|
|
240
|
+
|
|
241
|
+
if (node_at_pos := self.get_node_at_position(file_path, position.line, position.character - 1)) {
|
|
242
|
+
self.debug("found the node at pos " + str(position) + " " + str(node_at_pos));
|
|
243
|
+
|
|
244
|
+
# For each trigger character we need to handle the completion differently
|
|
245
|
+
if isinstance(node_at_pos, uni.Token) {
|
|
246
|
+
if node_at_pos.name == "DOT" {
|
|
247
|
+
member_access = node_at_pos.parent;
|
|
248
|
+
self.debug("found dot " + str(member_access));
|
|
249
|
+
if isinstance(member_access, uni.AtomTrailer) {
|
|
250
|
+
self.debug("before returning the list");
|
|
251
|
+
return self.get_completion_of_node(member_access.target);
|
|
274
252
|
}
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
if isinstance(base_name, uni.Name) and base_name.sym {
|
|
284
|
-
base.append(base_name.sym);
|
|
285
|
-
}
|
|
253
|
+
|
|
254
|
+
# FIXME: This is wrong but imma do it anyways like this for now.
|
|
255
|
+
} elif node_at_pos.name == "NAME" {
|
|
256
|
+
|
|
257
|
+
# Name of atom trailer.
|
|
258
|
+
if node_at_pos.parent and isinstance(node_at_pos.parent, uni.AtomTrailer) {
|
|
259
|
+
self.debug("found name in atom trailer " + str(node_at_pos.parent));
|
|
260
|
+
return self.get_completion_of_node(node_at_pos.parent.target);
|
|
286
261
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
);
|
|
293
|
-
}
|
|
262
|
+
|
|
263
|
+
# Just a name field.
|
|
264
|
+
if scope_node := node_at_pos.find_parent_of_type(uni.UniScopeNode) {
|
|
265
|
+
self.debug("found name in scope node " + str(scope_node));
|
|
266
|
+
return self.get_completion_of_node(scope_node);
|
|
294
267
|
}
|
|
295
268
|
}
|
|
296
269
|
}
|
|
297
|
-
} elif node_selected and node_selected.find_parent_of_type(uni.Archetype)
|
|
298
|
-
or node_selected.find_parent_of_type(uni.ImplDef)
|
|
299
|
-
{
|
|
300
|
-
self_symbol =
|
|
301
|
-
|
|
302
|
-
[lspt.CompletionItem(
|
|
303
|
-
label='self',
|
|
304
|
-
kind=lspt.CompletionItemKind.Variable
|
|
305
|
-
)];
|
|
306
|
-
} else {
|
|
307
|
-
self_symbol = [];
|
|
308
270
|
}
|
|
309
|
-
|
|
271
|
+
|
|
272
|
+
return lspt.CompletionList(is_incomplete=False, items=[]);
|
|
273
|
+
|
|
310
274
|
} except Exception as e {
|
|
311
275
|
self.log_py(f"'Error during completion: '{e}");
|
|
312
276
|
return lspt.CompletionList(is_incomplete=False, items=[]);
|
|
313
277
|
}
|
|
278
|
+
|
|
279
|
+
self.debug("returning empty list");
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
def get_ast_of_file(self: JacLangServer, file_path: str) -> Optional[uni.AstNode] {
|
|
283
|
+
if file_path in self.mod.hub {
|
|
284
|
+
return self.mod.hub[file_path];
|
|
285
|
+
}
|
|
286
|
+
return None;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
def get_node_at_position(self: JacLangServer, file_path: str, line:int, col: int) -> Optional[uni.AstNode] {
|
|
290
|
+
if (ast := self.get_ast_of_file(file_path)) {
|
|
291
|
+
for ast_node in ast._in_mod_nodes {
|
|
292
|
+
if not isinstance(ast_node, uni.Token) {
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
if (utils.position_within_node(ast_node, line + 1, col + 1)) {
|
|
296
|
+
return ast_node;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
return None;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
def get_completion_of_node(self: JacLangServer, node: uni.AstNode) -> lspt.CompletionList {
|
|
304
|
+
if (node_type := self.get_node_type(node)) {
|
|
305
|
+
self.debug("found type " + str(node_type));
|
|
306
|
+
return self.get_completion_items_of(node_type);
|
|
307
|
+
} elif isinstance(node, UniScopeNode) {
|
|
308
|
+
self.debug("found scope node " + str(node));
|
|
309
|
+
return self.get_completion_items_of(node);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
self.debug("no type found for node " + str(node));
|
|
313
|
+
return lspt.CompletionList(is_incomplete=False, items=[]);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
"""Return the type of an AST node if it has one."""
|
|
317
|
+
def get_node_type(self: JacLangServer, n: uni.AstNode) -> Optional[TypeBase] {
|
|
318
|
+
if isinstance(n, uni.Expr) {
|
|
319
|
+
typ = self.get_type_evaluator().get_type_of_expression(n);
|
|
320
|
+
self.debug("found type " + str(typ) + " for expr " + str(n));
|
|
321
|
+
return typ;
|
|
322
|
+
}
|
|
323
|
+
self.debug("no type found for node " + str(n));
|
|
324
|
+
return None;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
"""Get type members for completion."""
|
|
328
|
+
def get_completion_items_of(self: JacLangServer, ty: TypeBase | uni.UniScopeNode) -> lspt.CompletionList {
|
|
329
|
+
evaluator = self.get_type_evaluator();
|
|
330
|
+
self.debug("getting completion of " + str(ty));
|
|
331
|
+
items = get_completion_items(ty);
|
|
332
|
+
self.debug("completion items are " + str(items));
|
|
333
|
+
|
|
334
|
+
items: list[lspt.CompletionItem] = [];
|
|
335
|
+
for item in get_completion_items(ty) {
|
|
336
|
+
detail: lspt.CompletionItemLabelDetails | None = None;
|
|
337
|
+
if item.detail {
|
|
338
|
+
detail = lspt.CompletionItemLabelDetails(detail=item.detail);
|
|
339
|
+
}
|
|
340
|
+
items.append(
|
|
341
|
+
lspt.CompletionItem(
|
|
342
|
+
label=item.label,
|
|
343
|
+
kind=item.kind,
|
|
344
|
+
label_details=detail,
|
|
345
|
+
)
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return lspt.CompletionList(
|
|
350
|
+
is_incomplete=False,
|
|
351
|
+
items=items,
|
|
352
|
+
);
|
|
314
353
|
}
|
|
315
354
|
|
|
316
355
|
"""Rename module."""
|
|
@@ -334,7 +373,7 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
334
373
|
}
|
|
335
374
|
|
|
336
375
|
"""Return formatted jac."""
|
|
337
|
-
def formatted_jac(self: JacLangServer, file_path: str) ->
|
|
376
|
+
def formatted_jac(self: JacLangServer, file_path: str) -> list[lspt.TextEdit] {
|
|
338
377
|
try {
|
|
339
378
|
document = self.workspace.get_text_document(file_path);
|
|
340
379
|
formatted_text =
|
|
@@ -365,20 +404,7 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
365
404
|
file_path: str,
|
|
366
405
|
position: lspt.Position
|
|
367
406
|
) -> Optional[lspt.Hover] {
|
|
368
|
-
|
|
369
|
-
if file_path_fs not in self.mod.hub {
|
|
370
|
-
return None;
|
|
371
|
-
}
|
|
372
|
-
sem_mgr = self.sem_managers.get(file_path_fs);
|
|
373
|
-
if not sem_mgr {
|
|
374
|
-
return None;
|
|
375
|
-
}
|
|
376
|
-
token_index =
|
|
377
|
-
utils.find_index(sem_mgr.sem_tokens, position.line, position.character);
|
|
378
|
-
if token_index is None {
|
|
379
|
-
return None;
|
|
380
|
-
}
|
|
381
|
-
node_selected = sem_mgr.static_sem_tokens[token_index][3];
|
|
407
|
+
node_selected = self.get_token_at_position(file_path, position);
|
|
382
408
|
value = self.get_node_info(node_selected) if node_selected else None;
|
|
383
409
|
if value {
|
|
384
410
|
return lspt.Hover(
|
|
@@ -392,22 +418,25 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
392
418
|
}
|
|
393
419
|
|
|
394
420
|
"""Extract meaningful information from the AST node."""
|
|
395
|
-
def get_node_info(self: JacLangServer,
|
|
421
|
+
def get_node_info(self: JacLangServer, sym_node: uni.AstSymbolNode) -> Optional[str] {
|
|
396
422
|
try {
|
|
397
|
-
if isinstance(
|
|
398
|
-
|
|
423
|
+
if isinstance(sym_node, uni.NameAtom) {
|
|
424
|
+
sym_node = sym_node.name_of;
|
|
399
425
|
}
|
|
400
|
-
access = (
|
|
426
|
+
access = (sym_node.sym.access.value + ' ') if sym_node.sym else None;
|
|
401
427
|
node_info =
|
|
402
|
-
f"'('{access if access else ''}{
|
|
403
|
-
if
|
|
404
|
-
node_info += f"': '{
|
|
428
|
+
f"'('{access if access else ''}{sym_node.sym_category.value}') '{sym_node.sym_name}";
|
|
429
|
+
if sym_node.name_spec.clean_type {
|
|
430
|
+
node_info += f"': '{sym_node.name_spec.clean_type}";
|
|
405
431
|
}
|
|
406
|
-
if isinstance(
|
|
407
|
-
node_info += f"'
|
|
432
|
+
if isinstance(sym_node,uni.AstSymbolNode) and isinstance(sym_node.name_spec.type,ClassType) {
|
|
433
|
+
node_info += f"': '{sym_node.name_spec.type.shared.class_name}";
|
|
408
434
|
}
|
|
409
|
-
if isinstance(
|
|
410
|
-
node_info += f"'\n'{
|
|
435
|
+
if isinstance(sym_node, uni.AstDocNode) and sym_node.doc {
|
|
436
|
+
node_info += f"'\n'{sym_node.doc.value}";
|
|
437
|
+
}
|
|
438
|
+
if isinstance(sym_node, uni.Ability) and sym_node.signature {
|
|
439
|
+
node_info += f"'\n'{sym_node.signature.unparse()}";
|
|
411
440
|
}
|
|
412
441
|
} except AttributeError as e {
|
|
413
442
|
self.log_warning(f"'Attribute error when accessing node attributes: '{e}");
|
|
@@ -416,10 +445,10 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
416
445
|
}
|
|
417
446
|
|
|
418
447
|
"""Return document symbols for a file."""
|
|
419
|
-
def get_outline(self: JacLangServer, file_path: str) ->
|
|
420
|
-
|
|
421
|
-
if
|
|
422
|
-
and (root_node := self.mod.hub[
|
|
448
|
+
def get_outline(self: JacLangServer, file_path: str) -> list[lspt.DocumentSymbol] {
|
|
449
|
+
fs_path = uris.to_fs_path(file_path);
|
|
450
|
+
if fs_path in self.mod.hub
|
|
451
|
+
and (root_node := self.mod.hub[fs_path].sym_tab)
|
|
423
452
|
{
|
|
424
453
|
return utils.get_symbols_for_outline(root_node);
|
|
425
454
|
}
|
|
@@ -432,20 +461,7 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
432
461
|
file_path: str,
|
|
433
462
|
position: lspt.Position
|
|
434
463
|
) -> Optional[lspt.Location] {
|
|
435
|
-
|
|
436
|
-
if file_path_fs not in self.mod.hub {
|
|
437
|
-
return None;
|
|
438
|
-
}
|
|
439
|
-
sem_mgr = self.sem_managers.get(file_path_fs);
|
|
440
|
-
if not sem_mgr {
|
|
441
|
-
return None;
|
|
442
|
-
}
|
|
443
|
-
token_index =
|
|
444
|
-
utils.find_index(sem_mgr.sem_tokens, position.line, position.character);
|
|
445
|
-
if token_index is None {
|
|
446
|
-
return None;
|
|
447
|
-
}
|
|
448
|
-
node_selected = sem_mgr.static_sem_tokens[token_index][3];
|
|
464
|
+
node_selected = self.get_token_at_position(file_path, position);
|
|
449
465
|
if node_selected {
|
|
450
466
|
if (node_selected.sym.sym_type == SymbolType.MODULE) {
|
|
451
467
|
spec = node_selected.sym.decl.parent.resolve_relative_path();
|
|
@@ -543,27 +559,14 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
543
559
|
self: JacLangServer,
|
|
544
560
|
file_path: str,
|
|
545
561
|
position: lspt.Position
|
|
546
|
-
) ->
|
|
547
|
-
|
|
548
|
-
if file_path_fs not in self.mod.hub {
|
|
549
|
-
return [];
|
|
550
|
-
}
|
|
551
|
-
sem_mgr = self.sem_managers.get(file_path_fs);
|
|
552
|
-
if not sem_mgr {
|
|
553
|
-
return [];
|
|
554
|
-
}
|
|
555
|
-
index1 =
|
|
556
|
-
utils.find_index(sem_mgr.sem_tokens, position.line, position.character);
|
|
557
|
-
if index1 is None {
|
|
558
|
-
return [];
|
|
559
|
-
}
|
|
560
|
-
node_selected = sem_mgr.static_sem_tokens[index1][3];
|
|
562
|
+
) -> list[lspt.Location] {
|
|
563
|
+
node_selected = self.get_token_at_position(file_path, position);
|
|
561
564
|
if node_selected and node_selected.sym {
|
|
562
|
-
list_of_references:
|
|
565
|
+
list_of_references: list[lspt.Location] =
|
|
563
566
|
[ lspt.Location(
|
|
564
|
-
uri=uris.from_fs_path(
|
|
565
|
-
range=utils.create_range(
|
|
566
|
-
) for
|
|
567
|
+
uri=uris.from_fs_path(cur_node.loc.mod_path),
|
|
568
|
+
range=utils.create_range(cur_node.loc)
|
|
569
|
+
) for cur_node in node_selected.sym.uses ];
|
|
567
570
|
return list_of_references;
|
|
568
571
|
}
|
|
569
572
|
return [];
|
|
@@ -576,27 +579,14 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
576
579
|
position: lspt.Position,
|
|
577
580
|
new_name: str
|
|
578
581
|
) -> Optional[lspt.WorkspaceEdit] {
|
|
579
|
-
|
|
580
|
-
if file_path_fs not in self.mod.hub {
|
|
581
|
-
return None;
|
|
582
|
-
}
|
|
583
|
-
sem_mgr = self.sem_managers.get(file_path_fs);
|
|
584
|
-
if not sem_mgr {
|
|
585
|
-
return None;
|
|
586
|
-
}
|
|
587
|
-
index1 =
|
|
588
|
-
utils.find_index(sem_mgr.sem_tokens, position.line, position.character);
|
|
589
|
-
if index1 is None {
|
|
590
|
-
return None;
|
|
591
|
-
}
|
|
592
|
-
node_selected = sem_mgr.static_sem_tokens[index1][3];
|
|
582
|
+
node_selected = self.get_token_at_position(file_path, position);
|
|
593
583
|
if node_selected and node_selected.sym {
|
|
594
|
-
changes:
|
|
595
|
-
for
|
|
596
|
-
key = uris.from_fs_path(
|
|
584
|
+
changes: dict[(str, list[lspt.TextEdit])] = {};
|
|
585
|
+
for node in [*node_selected.sym.uses, node_selected.sym.defn[0]] {
|
|
586
|
+
key = uris.from_fs_path(node.loc.mod_path);
|
|
597
587
|
new_edit =
|
|
598
588
|
lspt.TextEdit(
|
|
599
|
-
range=utils.create_range(
|
|
589
|
+
range=utils.create_range(node.loc),
|
|
600
590
|
new_text=new_name
|
|
601
591
|
);
|
|
602
592
|
utils.add_unique_text_edit(changes, key, new_edit);
|
|
@@ -608,8 +598,8 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
608
598
|
|
|
609
599
|
"""Return semantic tokens for a file."""
|
|
610
600
|
def get_semantic_tokens(self: JacLangServer, file_path: str) -> lspt.SemanticTokens {
|
|
611
|
-
|
|
612
|
-
sem_mgr = self.sem_managers.get(
|
|
601
|
+
fs_path = uris.to_fs_path(file_path);
|
|
602
|
+
sem_mgr = self.sem_managers.get(fs_path);
|
|
613
603
|
if not sem_mgr {
|
|
614
604
|
return lspt.SemanticTokens(data=[]);
|
|
615
605
|
}
|
|
@@ -638,4 +628,37 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
638
628
|
def log_py(self: JacLangServer, message: str) -> None {
|
|
639
629
|
logging.info(message);
|
|
640
630
|
}
|
|
631
|
+
|
|
632
|
+
"""Run a function with debouncing per file."""
|
|
633
|
+
async def _debounced_run(
|
|
634
|
+
self: JacLangServer,
|
|
635
|
+
func: Callable,
|
|
636
|
+
file_uri: str,
|
|
637
|
+
delay: float,
|
|
638
|
+
task_dict: dict[(str, asyncio.Task)]
|
|
639
|
+
) -> None {
|
|
640
|
+
if ((file_uri in task_dict) and not task_dict[file_uri].done() ) {
|
|
641
|
+
task_dict[file_uri].cancel();
|
|
642
|
+
self.log_py(
|
|
643
|
+
f" {func.__name__} ' was cancelled due to debounce for ' {file_uri} "
|
|
644
|
+
);
|
|
645
|
+
}
|
|
646
|
+
async def wrapper() {
|
|
647
|
+
try {
|
|
648
|
+
await asyncio.sleep(delay);
|
|
649
|
+
loop = asyncio.get_event_loop();
|
|
650
|
+
result = await loop.run_in_executor(None, func, file_uri);
|
|
651
|
+
return result;
|
|
652
|
+
} except asyncio.CancelledError {
|
|
653
|
+
self.log_py(
|
|
654
|
+
f" {func.__name__} ' was cancelled due to debounce for ' {file_uri} "
|
|
655
|
+
);
|
|
656
|
+
raise ;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
new_task = asyncio.create_task(wrapper());
|
|
660
|
+
task_dict[file_uri] = new_task;
|
|
661
|
+
return await new_task;
|
|
662
|
+
}
|
|
663
|
+
|
|
641
664
|
}
|