jaclang 0.8.9__py3-none-any.whl → 0.8.10__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 +147 -25
- jaclang/cli/cmdreg.py +144 -8
- jaclang/compiler/__init__.py +6 -1
- jaclang/compiler/codeinfo.py +16 -1
- jaclang/compiler/constant.py +33 -13
- jaclang/compiler/jac.lark +130 -31
- jaclang/compiler/larkparse/jac_parser.py +2 -2
- jaclang/compiler/parser.py +567 -176
- jaclang/compiler/passes/__init__.py +2 -1
- jaclang/compiler/passes/ast_gen/__init__.py +5 -0
- jaclang/compiler/passes/ast_gen/base_ast_gen_pass.py +54 -0
- jaclang/compiler/passes/ast_gen/jsx_processor.py +344 -0
- jaclang/compiler/passes/ecmascript/__init__.py +25 -0
- jaclang/compiler/passes/ecmascript/es_unparse.py +576 -0
- jaclang/compiler/passes/ecmascript/esast_gen_pass.py +2068 -0
- jaclang/compiler/passes/ecmascript/estree.py +972 -0
- jaclang/compiler/passes/ecmascript/tests/__init__.py +1 -0
- jaclang/compiler/passes/ecmascript/tests/fixtures/advanced_language_features.jac +170 -0
- jaclang/compiler/passes/ecmascript/tests/fixtures/class_separate_impl.impl.jac +30 -0
- jaclang/compiler/passes/ecmascript/tests/fixtures/class_separate_impl.jac +14 -0
- jaclang/compiler/passes/ecmascript/tests/fixtures/client_jsx.jac +89 -0
- jaclang/compiler/passes/ecmascript/tests/fixtures/core_language_features.jac +195 -0
- jaclang/compiler/passes/ecmascript/tests/test_esast_gen_pass.py +167 -0
- jaclang/compiler/passes/ecmascript/tests/test_js_generation.py +239 -0
- jaclang/compiler/passes/main/__init__.py +0 -3
- jaclang/compiler/passes/main/annex_pass.py +23 -1
- jaclang/compiler/passes/main/pyast_gen_pass.py +324 -234
- jaclang/compiler/passes/main/pyast_load_pass.py +46 -11
- jaclang/compiler/passes/main/pyjac_ast_link_pass.py +2 -0
- jaclang/compiler/passes/main/sym_tab_build_pass.py +18 -1
- jaclang/compiler/passes/main/tests/fixtures/autoimpl.cl.jac +7 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_arity.jac +3 -0
- jaclang/compiler/passes/main/tests/fixtures/checker_class_construct.jac +33 -0
- jaclang/compiler/passes/main/tests/fixtures/defuse_modpath.jac +7 -0
- jaclang/compiler/passes/main/tests/fixtures/member_access_type_resolve.jac +2 -1
- jaclang/compiler/passes/main/tests/test_checker_pass.py +31 -2
- jaclang/compiler/passes/main/tests/test_def_use_pass.py +12 -0
- jaclang/compiler/passes/main/tests/test_import_pass.py +23 -4
- jaclang/compiler/passes/main/tests/test_pyast_gen_pass.py +25 -0
- jaclang/compiler/passes/main/type_checker_pass.py +7 -0
- jaclang/compiler/passes/tool/doc_ir_gen_pass.py +115 -0
- jaclang/compiler/passes/tool/fuse_comments_pass.py +1 -10
- jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +4 -1
- jaclang/compiler/passes/transform.py +9 -1
- jaclang/compiler/passes/uni_pass.py +5 -7
- jaclang/compiler/program.py +22 -25
- jaclang/compiler/tests/test_client_codegen.py +113 -0
- jaclang/compiler/tests/test_importer.py +12 -10
- jaclang/compiler/tests/test_parser.py +249 -3
- jaclang/compiler/type_system/type_evaluator.jac +169 -50
- jaclang/compiler/type_system/type_utils.py +1 -1
- jaclang/compiler/type_system/types.py +6 -0
- jaclang/compiler/unitree.py +430 -84
- jaclang/langserve/engine.jac +224 -288
- jaclang/langserve/sem_manager.jac +12 -8
- jaclang/langserve/server.jac +48 -48
- jaclang/langserve/tests/fixtures/greet.py +17 -0
- jaclang/langserve/tests/fixtures/md_path.jac +22 -0
- jaclang/langserve/tests/fixtures/user.jac +15 -0
- jaclang/langserve/tests/test_server.py +66 -371
- jaclang/lib.py +1 -1
- jaclang/runtimelib/client_bundle.py +169 -0
- jaclang/runtimelib/client_runtime.jac +586 -0
- jaclang/runtimelib/constructs.py +2 -0
- jaclang/runtimelib/machine.py +259 -100
- jaclang/runtimelib/meta_importer.py +111 -22
- jaclang/runtimelib/mtp.py +15 -0
- jaclang/runtimelib/server.py +1089 -0
- jaclang/runtimelib/tests/fixtures/client_app.jac +18 -0
- jaclang/runtimelib/tests/fixtures/custom_access_validation.jac +1 -1
- jaclang/runtimelib/tests/fixtures/savable_object.jac +4 -5
- jaclang/runtimelib/tests/fixtures/serve_api.jac +75 -0
- jaclang/runtimelib/tests/test_client_bundle.py +55 -0
- jaclang/runtimelib/tests/test_client_render.py +63 -0
- jaclang/runtimelib/tests/test_serve.py +1069 -0
- jaclang/settings.py +0 -2
- jaclang/tests/fixtures/iife_functions.jac +142 -0
- jaclang/tests/fixtures/iife_functions_client.jac +143 -0
- jaclang/tests/fixtures/multistatement_lambda.jac +116 -0
- jaclang/tests/fixtures/multistatement_lambda_client.jac +113 -0
- jaclang/tests/fixtures/needs_import_dup.jac +6 -4
- jaclang/tests/fixtures/py_run.py +7 -5
- jaclang/tests/fixtures/pyfunc_fstr.py +2 -2
- jaclang/tests/fixtures/simple_lambda_test.jac +12 -0
- jaclang/tests/test_cli.py +1 -1
- jaclang/tests/test_language.py +10 -39
- jaclang/tests/test_reference.py +17 -2
- jaclang/utils/NonGPT.py +375 -0
- jaclang/utils/helpers.py +44 -16
- jaclang/utils/lang_tools.py +31 -4
- jaclang/utils/tests/test_lang_tools.py +1 -1
- jaclang/utils/treeprinter.py +8 -3
- {jaclang-0.8.9.dist-info → jaclang-0.8.10.dist-info}/METADATA +3 -3
- {jaclang-0.8.9.dist-info → jaclang-0.8.10.dist-info}/RECORD +96 -66
- jaclang/compiler/passes/main/binder_pass.py +0 -594
- jaclang/compiler/passes/main/tests/fixtures/sym_binder.jac +0 -47
- jaclang/compiler/passes/main/tests/test_binder_pass.py +0 -111
- jaclang/langserve/tests/session.jac +0 -294
- jaclang/langserve/tests/test_dev_server.py +0 -80
- jaclang/runtimelib/importer.py +0 -351
- jaclang/tests/test_typecheck.py +0 -542
- {jaclang-0.8.9.dist-info → jaclang-0.8.10.dist-info}/WHEEL +0 -0
- {jaclang-0.8.9.dist-info → jaclang-0.8.10.dist-info}/entry_points.txt +0 -0
jaclang/langserve/engine.jac
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import asyncio;
|
|
4
4
|
import logging;
|
|
5
|
+
import os;
|
|
6
|
+
import sys;
|
|
5
7
|
import time;
|
|
8
|
+
import threading;
|
|
6
9
|
import from concurrent.futures { ThreadPoolExecutor }
|
|
7
10
|
import from typing { Callable, Optional }
|
|
8
11
|
|
|
@@ -11,14 +14,19 @@ import from jaclang { JacMachineInterface as Jac }
|
|
|
11
14
|
import from jaclang.compiler.constant { SymbolType }
|
|
12
15
|
import from jaclang.compiler.program { JacProgram }
|
|
13
16
|
import from jaclang.compiler.type_system.type_utils { get_completion_items }
|
|
14
|
-
import from jaclang.compiler.type_system.types {
|
|
17
|
+
import from jaclang.compiler.type_system.types {
|
|
18
|
+
ClassType,
|
|
19
|
+
FunctionType,
|
|
20
|
+
ModuleType,
|
|
21
|
+
TypeBase
|
|
22
|
+
}
|
|
15
23
|
import from jaclang.compiler.unitree { UniScopeNode }
|
|
16
|
-
import from sem_manager { SemTokManager }
|
|
24
|
+
import from .sem_manager { SemTokManager }
|
|
17
25
|
import from jaclang.vendor.pygls { uris }
|
|
18
26
|
import from jaclang.vendor.pygls.server { LanguageServer }
|
|
19
27
|
|
|
20
28
|
import lsprotocol.types as lspt;
|
|
21
|
-
import utils
|
|
29
|
+
import from . { utils }
|
|
22
30
|
|
|
23
31
|
|
|
24
32
|
"""Handles Jac module, semantic manager, and alert management."""
|
|
@@ -49,10 +57,16 @@ class ModuleManager {
|
|
|
49
57
|
|
|
50
58
|
"""Remove errors and warnings for a specific file from the lists."""
|
|
51
59
|
def clear_alerts_for_file(self: ModuleManager, file_path_fs: str) -> None {
|
|
52
|
-
self.program.errors_had =
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
60
|
+
self.program.errors_had = [
|
|
61
|
+
e
|
|
62
|
+
for e in self.program.errors_had
|
|
63
|
+
if e.loc.mod_path != file_path_fs
|
|
64
|
+
];
|
|
65
|
+
self.program.warnings_had = [
|
|
66
|
+
w
|
|
67
|
+
for w in self.program.warnings_had
|
|
68
|
+
if w.loc.mod_path != file_path_fs
|
|
69
|
+
];
|
|
56
70
|
}
|
|
57
71
|
}
|
|
58
72
|
|
|
@@ -63,22 +77,28 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
63
77
|
def init(self: JacLangServer) -> None {
|
|
64
78
|
LanguageServer.init(self, 'jac-lsp', 'v0.1');
|
|
65
79
|
JacProgram.init(self);
|
|
66
|
-
self.executor = ThreadPoolExecutor();
|
|
67
|
-
self.tasks: dict[(str, asyncio.Task)] = {};
|
|
68
80
|
self.sem_managers: dict[(str, SemTokManager)] = {};
|
|
69
81
|
self.module_manager = ModuleManager(self, self.sem_managers);
|
|
70
|
-
|
|
71
|
-
|
|
82
|
+
|
|
83
|
+
# Threading support - per file thread with cancellation token
|
|
84
|
+
self.type_check_threads: dict[str, threading.Thread] = {};
|
|
85
|
+
self.cancel_tokens: dict[str, threading.Event] = {};
|
|
86
|
+
|
|
87
|
+
self.debounce_timers: dict[str, threading.Timer] = {};
|
|
88
|
+
|
|
89
|
+
# Disable logging library from logging anything
|
|
90
|
+
# logging.getLogger().setLevel(logging.CRITICAL);
|
|
72
91
|
}
|
|
73
92
|
|
|
74
93
|
"""Return diagnostics for all files as a dict {uri: diagnostics}."""
|
|
75
|
-
@
|
|
76
|
-
def diagnostics(self: JacLangServer,
|
|
94
|
+
@property
|
|
95
|
+
def diagnostics(self: JacLangServer,) -> dict[str, list] {
|
|
77
96
|
result = {};
|
|
78
97
|
for file_path in self.mod.hub {
|
|
79
98
|
uri = uris.from_fs_path(file_path);
|
|
80
|
-
result[uri] =
|
|
81
|
-
|
|
99
|
+
result[uri] = utils.gen_diagnostics(
|
|
100
|
+
file_path, self.errors_had, self.warnings_had
|
|
101
|
+
);
|
|
82
102
|
}
|
|
83
103
|
return result;
|
|
84
104
|
}
|
|
@@ -95,114 +115,112 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
95
115
|
|
|
96
116
|
"""Update modules in JacProgram's hub and semantic managers."""
|
|
97
117
|
def update_modules(
|
|
98
|
-
self: JacLangServer,
|
|
99
|
-
file_path: str,
|
|
100
|
-
build: uni.Module,
|
|
101
|
-
need: bool = True
|
|
118
|
+
self: JacLangServer, file_path: str, build: uni.Module, need: bool = True
|
|
102
119
|
) -> None {
|
|
103
120
|
self.log_py(f"'Updating modules for '{file_path}");
|
|
104
121
|
self.module_manager.update(file_path, build, update_annexed=need);
|
|
105
122
|
}
|
|
106
123
|
|
|
107
|
-
"""Rebuild a file (syntax only)."""
|
|
108
|
-
def quick_check(self: JacLangServer, file_path: str) -> bool {
|
|
109
|
-
try {
|
|
110
|
-
document = self.workspace.get_text_document(file_path);
|
|
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://"
|
|
117
|
-
self.publish_diagnostics(
|
|
118
|
-
file_path,
|
|
119
|
-
utils.gen_diagnostics(fs_path, self.errors_had, self.warnings_had)
|
|
120
|
-
);
|
|
121
|
-
build_errors =
|
|
122
|
-
[ e for e in self.errors_had if e.loc.mod_path == fs_path];
|
|
123
|
-
return len(build_errors) == 0;
|
|
124
|
-
} except Exception as e {
|
|
125
|
-
self.log_error(f"'Error during syntax check: '{e}");
|
|
126
|
-
return False;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
124
|
"""Rebuild a file and its dependencies (typecheck)."""
|
|
131
|
-
def
|
|
132
|
-
self: JacLangServer,
|
|
133
|
-
file_path: str,
|
|
134
|
-
annex_view: Optional[str] = None
|
|
125
|
+
def type_check_file(
|
|
126
|
+
self: JacLangServer, file_path: str,cancel_token: Event | None = None, annex_view: Optional[str] = None
|
|
135
127
|
) -> bool {
|
|
136
128
|
try {
|
|
137
129
|
start_time = time.time();
|
|
138
130
|
document = self.workspace.get_text_document(file_path);
|
|
139
131
|
fs_path = document.path;
|
|
140
|
-
|
|
141
132
|
self._clear_alerts_for_file(fs_path);
|
|
142
|
-
|
|
133
|
+
|
|
134
|
+
t = time.time();
|
|
135
|
+
f = file_path.split("/")[-1].split("\\")[-1];
|
|
136
|
+
self.debug("Type checking started for " + f + " t=" + str(t));
|
|
137
|
+
|
|
138
|
+
build = self.compile(
|
|
139
|
+
use_str=document.source, file_path=document.path, type_check=True
|
|
140
|
+
);
|
|
141
|
+
self.debug(f" 'Type checking for '{f}' done {str(time.time())} in '{(time.time() - t)}' seconds.'");
|
|
142
|
+
if cancel_token and cancel_token.is_set(){
|
|
143
|
+
self.debug(" ** Type checking canceled for " + f + " t=" + str(time.time()));
|
|
144
|
+
self.debug(f" 'Type checking for '{f}' was canceled in '{(time.time() - t)}' seconds.'");
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
143
148
|
self.update_modules(fs_path, build);
|
|
144
149
|
if build.annexable_by {
|
|
145
|
-
return self.
|
|
146
|
-
uris.from_fs_path(build.annexable_by),
|
|
147
|
-
annex_view=fs_path
|
|
150
|
+
return self.type_check_file(
|
|
151
|
+
uris.from_fs_path(build.annexable_by), cancel_token=cancel_token, annex_view=fs_path
|
|
148
152
|
);
|
|
149
153
|
}
|
|
150
154
|
self.publish_diagnostics(
|
|
151
|
-
|
|
152
|
-
|
|
155
|
+
(
|
|
156
|
+
# to display diagnostic , it need URI starts with "file://"
|
|
157
|
+
uris.from_fs_path(
|
|
158
|
+
annex_view
|
|
159
|
+
)
|
|
160
|
+
if annex_view
|
|
161
|
+
else uris.from_fs_path(fs_path)
|
|
162
|
+
),
|
|
153
163
|
utils.gen_diagnostics(
|
|
154
|
-
annex_view if annex_view else fs_path,
|
|
164
|
+
(annex_view if annex_view else fs_path),
|
|
155
165
|
self.errors_had,
|
|
156
166
|
self.warnings_had
|
|
157
167
|
)
|
|
158
168
|
);
|
|
159
169
|
if annex_view {
|
|
160
170
|
self.publish_diagnostics(
|
|
161
|
-
uris.from_fs_path(fs_path)
|
|
171
|
+
uris.from_fs_path(fs_path),
|
|
162
172
|
utils.gen_diagnostics(fs_path, self.errors_had, self.warnings_had)
|
|
163
173
|
);
|
|
164
174
|
}
|
|
165
175
|
self.log_py(
|
|
166
176
|
f"'PROFILE: Deep check took '{(time.time() - start_time)}' seconds.'"
|
|
167
177
|
);
|
|
168
|
-
return len(self.errors_had) == 0;
|
|
169
178
|
} except Exception as e {
|
|
170
|
-
|
|
171
|
-
|
|
179
|
+
if cancel_token and not cancel_token.is_set() {
|
|
180
|
+
self.log_py(f"'Error during type check: '{e}");
|
|
181
|
+
}
|
|
172
182
|
}
|
|
173
183
|
}
|
|
174
184
|
|
|
175
|
-
"""
|
|
176
|
-
async def
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
return False;
|
|
181
|
-
} except Exception as e {
|
|
182
|
-
self.log_error(f" 'Error during quick check launch: ' {e} ");
|
|
183
|
-
return False;
|
|
185
|
+
"""Type check with thread cancellation and proper debouncing."""
|
|
186
|
+
async def type_check(self: JacLangServer, file_path: str) -> None {
|
|
187
|
+
# Cancel existing debounce timer for this file
|
|
188
|
+
if file_path in self.debounce_timers {
|
|
189
|
+
self.debounce_timers[file_path].cancel();
|
|
184
190
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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);
|
|
191
|
+
|
|
192
|
+
# Cancel existing type check thread for this file if running
|
|
193
|
+
if file_path in self.cancel_tokens {
|
|
194
|
+
self.cancel_tokens[file_path].set();
|
|
195
195
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
196
|
+
|
|
197
|
+
# Create new cancellation token for the upcoming type check
|
|
198
|
+
self.cancel_tokens[file_path] = threading.Event();
|
|
199
|
+
|
|
200
|
+
# Create a debounced function that will run the actual type check
|
|
201
|
+
def debounced_type_check() -> None {
|
|
202
|
+
# Start new thread for type checking
|
|
203
|
+
print(" debounced_type_check ->", time.time(), file=sys.stderr);
|
|
204
|
+
self.type_check_threads[file_path] = threading.Thread(
|
|
205
|
+
target=self.type_check_file,
|
|
206
|
+
args=(file_path, self.cancel_tokens[file_path])
|
|
207
|
+
);
|
|
208
|
+
self.type_check_threads[file_path].start();
|
|
209
|
+
self.type_check_threads[file_path].join();
|
|
202
210
|
}
|
|
211
|
+
|
|
212
|
+
# Set up debounce timer with the actual type check function
|
|
213
|
+
self.debounce_timers[file_path] = threading.Timer(0.5, debounced_type_check);
|
|
214
|
+
self.debounce_timers[file_path].start();
|
|
215
|
+
|
|
216
|
+
# Wait for the current type check thread to finish (if any)
|
|
217
|
+
await asyncio.to_thread(self.debounce_timers[file_path].join);
|
|
218
|
+
|
|
203
219
|
}
|
|
204
220
|
|
|
205
|
-
def get_token_at_position(
|
|
221
|
+
def get_token_at_position(
|
|
222
|
+
self: JacLangServer, file_path: str, position: lspt.Position
|
|
223
|
+
) -> Optional[uni.AstNode] {
|
|
206
224
|
fs_path = uris.to_fs_path(file_path);
|
|
207
225
|
if fs_path not in self.mod.hub {
|
|
208
226
|
return None;
|
|
@@ -211,8 +229,9 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
211
229
|
if not sem_mgr {
|
|
212
230
|
return None;
|
|
213
231
|
}
|
|
214
|
-
token_index =
|
|
215
|
-
|
|
232
|
+
token_index = utils.find_index(
|
|
233
|
+
sem_mgr.sem_tokens, position.line, position.character
|
|
234
|
+
);
|
|
216
235
|
if token_index is None {
|
|
217
236
|
return None;
|
|
218
237
|
}
|
|
@@ -221,9 +240,8 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
221
240
|
}
|
|
222
241
|
|
|
223
242
|
def debug(self: JacLangServer, msg: str) -> None {
|
|
224
|
-
|
|
243
|
+
print(f"[DEBUG] {msg}", file=sys.stderr);
|
|
225
244
|
self.log_py(f"[DEBUG] {msg}");
|
|
226
|
-
self.log_py("[DEBUG] " + ("-" * 80));
|
|
227
245
|
}
|
|
228
246
|
|
|
229
247
|
"""Return completion for a file."""
|
|
@@ -234,15 +252,18 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
234
252
|
completion_trigger: Optional[str]
|
|
235
253
|
) -> lspt.CompletionList {
|
|
236
254
|
self.debug("getting completion for " + file_uri + " at " + str(position));
|
|
237
|
-
|
|
238
255
|
try {
|
|
239
256
|
file_path = uris.to_fs_path(file_uri);
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
257
|
+
if (
|
|
258
|
+
node_at_pos := self.get_node_at_position(
|
|
259
|
+
file_path, position.line, position.character - 1
|
|
260
|
+
)
|
|
261
|
+
) {
|
|
262
|
+
self.debug(
|
|
263
|
+
"found the node at pos " + str(position) + " " + str(node_at_pos)
|
|
264
|
+
);
|
|
244
265
|
# For each trigger character we need to handle the completion differently
|
|
245
|
-
|
|
266
|
+
if isinstance(node_at_pos, uni.Token) {
|
|
246
267
|
if node_at_pos.name == "DOT" {
|
|
247
268
|
member_access = node_at_pos.parent;
|
|
248
269
|
self.debug("found dot " + str(member_access));
|
|
@@ -250,32 +271,35 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
250
271
|
self.debug("before returning the list");
|
|
251
272
|
return self.get_completion_of_node(member_access.target);
|
|
252
273
|
}
|
|
253
|
-
|
|
254
274
|
# FIXME: This is wrong but imma do it anyways like this for now.
|
|
255
|
-
|
|
256
|
-
|
|
275
|
+
} elif node_at_pos.name == "NAME" {
|
|
257
276
|
# Name of atom trailer.
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
277
|
+
if (
|
|
278
|
+
node_at_pos.parent
|
|
279
|
+
and isinstance(node_at_pos.parent, uni.AtomTrailer)
|
|
280
|
+
) {
|
|
281
|
+
self.debug(
|
|
282
|
+
"found name in atom trailer " + str(node_at_pos.parent)
|
|
283
|
+
);
|
|
284
|
+
return self.get_completion_of_node(
|
|
285
|
+
node_at_pos.parent.target
|
|
286
|
+
);
|
|
261
287
|
}
|
|
262
|
-
|
|
263
288
|
# Just a name field.
|
|
264
|
-
|
|
289
|
+
if scope_node := node_at_pos.find_parent_of_type(
|
|
290
|
+
uni.UniScopeNode
|
|
291
|
+
) {
|
|
265
292
|
self.debug("found name in scope node " + str(scope_node));
|
|
266
293
|
return self.get_completion_of_node(scope_node);
|
|
267
294
|
}
|
|
268
295
|
}
|
|
269
296
|
}
|
|
270
297
|
}
|
|
271
|
-
|
|
272
298
|
return lspt.CompletionList(is_incomplete=False, items=[]);
|
|
273
|
-
|
|
274
299
|
} except Exception as e {
|
|
275
300
|
self.log_py(f"'Error during completion: '{e}");
|
|
276
301
|
return lspt.CompletionList(is_incomplete=False, items=[]);
|
|
277
302
|
}
|
|
278
|
-
|
|
279
303
|
self.debug("returning empty list");
|
|
280
304
|
}
|
|
281
305
|
|
|
@@ -286,7 +310,9 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
286
310
|
return None;
|
|
287
311
|
}
|
|
288
312
|
|
|
289
|
-
def get_node_at_position(
|
|
313
|
+
def get_node_at_position(
|
|
314
|
+
self: JacLangServer, file_path: str, line: int, col: int
|
|
315
|
+
) -> Optional[uni.AstNode] {
|
|
290
316
|
if (ast := self.get_ast_of_file(file_path)) {
|
|
291
317
|
for ast_node in ast._in_mod_nodes {
|
|
292
318
|
if not isinstance(ast_node, uni.Token) {
|
|
@@ -300,7 +326,9 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
300
326
|
return None;
|
|
301
327
|
}
|
|
302
328
|
|
|
303
|
-
def get_completion_of_node(
|
|
329
|
+
def get_completion_of_node(
|
|
330
|
+
self: JacLangServer, node: uni.AstNode
|
|
331
|
+
) -> lspt.CompletionList {
|
|
304
332
|
if (node_type := self.get_node_type(node)) {
|
|
305
333
|
self.debug("found type " + str(node_type));
|
|
306
334
|
return self.get_completion_items_of(node_type);
|
|
@@ -308,7 +336,6 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
308
336
|
self.debug("found scope node " + str(node));
|
|
309
337
|
return self.get_completion_items_of(node);
|
|
310
338
|
}
|
|
311
|
-
|
|
312
339
|
self.debug("no type found for node " + str(node));
|
|
313
340
|
return lspt.CompletionList(is_incomplete=False, items=[]);
|
|
314
341
|
}
|
|
@@ -325,12 +352,13 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
325
352
|
}
|
|
326
353
|
|
|
327
354
|
"""Get type members for completion."""
|
|
328
|
-
def get_completion_items_of(
|
|
355
|
+
def get_completion_items_of(
|
|
356
|
+
self: JacLangServer, ty: TypeBase | uni.UniScopeNode
|
|
357
|
+
) -> lspt.CompletionList {
|
|
329
358
|
evaluator = self.get_type_evaluator();
|
|
330
359
|
self.debug("getting completion of " + str(ty));
|
|
331
360
|
items = get_completion_items(ty);
|
|
332
361
|
self.debug("completion items are " + str(items));
|
|
333
|
-
|
|
334
362
|
items: list[lspt.CompletionItem] = [];
|
|
335
363
|
for item in get_completion_items(ty) {
|
|
336
364
|
detail: lspt.CompletionItemLabelDetails | None = None;
|
|
@@ -339,22 +367,16 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
339
367
|
}
|
|
340
368
|
items.append(
|
|
341
369
|
lspt.CompletionItem(
|
|
342
|
-
label=item.label,
|
|
343
|
-
kind=item.kind,
|
|
344
|
-
label_details=detail,
|
|
370
|
+
label=item.label, kind=item.kind, label_details=detail,
|
|
345
371
|
)
|
|
346
372
|
);
|
|
347
373
|
}
|
|
348
|
-
|
|
349
|
-
return lspt.CompletionList(
|
|
350
|
-
is_incomplete=False,
|
|
351
|
-
items=items,
|
|
352
|
-
);
|
|
374
|
+
return lspt.CompletionList(is_incomplete=False, items=items,);
|
|
353
375
|
}
|
|
354
376
|
|
|
355
377
|
"""Rename module."""
|
|
356
378
|
def rename_module(self: JacLangServer, old_path: str, new_path: str) -> None {
|
|
357
|
-
if old_path in self.mod.hub and new_path != old_path
|
|
379
|
+
if old_path in self.mod.hub and new_path != old_path {
|
|
358
380
|
self.mod.hub[new_path] = self.mod.hub[old_path];
|
|
359
381
|
self.sem_managers[new_path] = self.sem_managers[old_path];
|
|
360
382
|
del (self.mod.hub[old_path], ) ;
|
|
@@ -376,41 +398,36 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
376
398
|
def formatted_jac(self: JacLangServer, file_path: str) -> list[lspt.TextEdit] {
|
|
377
399
|
try {
|
|
378
400
|
document = self.workspace.get_text_document(file_path);
|
|
379
|
-
formatted_text =
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
file_path=document.path
|
|
383
|
-
);
|
|
401
|
+
formatted_text = JacProgram.jac_str_formatter(
|
|
402
|
+
source_str=document.source, file_path=document.path
|
|
403
|
+
);
|
|
384
404
|
} except Exception as e {
|
|
385
405
|
self.log_error(f"'Error during formatting: '{e}");
|
|
386
406
|
formatted_text = document.source;
|
|
387
407
|
}
|
|
388
|
-
return
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
)
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
408
|
+
return [
|
|
409
|
+
lspt.TextEdit(
|
|
410
|
+
range=lspt.Range(
|
|
411
|
+
start=lspt.Position(line=0, character=0),
|
|
412
|
+
end=lspt.Position(
|
|
413
|
+
line=(len(document.source.splitlines()) + 1), character=0
|
|
414
|
+
)
|
|
415
|
+
),
|
|
416
|
+
new_text=formatted_text
|
|
417
|
+
)
|
|
418
|
+
];
|
|
399
419
|
}
|
|
400
420
|
|
|
401
421
|
"""Return hover information for a file."""
|
|
402
422
|
def get_hover_info(
|
|
403
|
-
self: JacLangServer,
|
|
404
|
-
file_path: str,
|
|
405
|
-
position: lspt.Position
|
|
423
|
+
self: JacLangServer, file_path: str, position: lspt.Position
|
|
406
424
|
) -> Optional[lspt.Hover] {
|
|
407
425
|
node_selected = self.get_token_at_position(file_path, position);
|
|
408
426
|
value = self.get_node_info(node_selected) if node_selected else None;
|
|
409
427
|
if value {
|
|
410
428
|
return lspt.Hover(
|
|
411
429
|
contents=lspt.MarkupContent(
|
|
412
|
-
kind=lspt.MarkupKind.PlainText,
|
|
413
|
-
value=f"{value}"
|
|
430
|
+
kind=lspt.MarkupKind.PlainText, value=f"{value}"
|
|
414
431
|
)
|
|
415
432
|
);
|
|
416
433
|
}
|
|
@@ -418,24 +435,28 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
418
435
|
}
|
|
419
436
|
|
|
420
437
|
"""Extract meaningful information from the AST node."""
|
|
421
|
-
def get_node_info(
|
|
438
|
+
def get_node_info(
|
|
439
|
+
self: JacLangServer, sym_node: uni.AstSymbolNode
|
|
440
|
+
) -> Optional[str] {
|
|
422
441
|
try {
|
|
423
442
|
if isinstance(sym_node, uni.NameAtom) {
|
|
424
443
|
sym_node = sym_node.name_of;
|
|
425
444
|
}
|
|
426
445
|
access = (sym_node.sym.access.value + ' ') if sym_node.sym else None;
|
|
427
|
-
node_info =
|
|
428
|
-
f"'('{access if access else ''}{sym_node.sym_category.value}') '{sym_node.sym_name}";
|
|
446
|
+
node_info = f"'('{(access if access else '')}{sym_node.sym_category.value}') '{sym_node.sym_name}";
|
|
429
447
|
if sym_node.name_spec.clean_type {
|
|
430
448
|
node_info += f"': '{sym_node.name_spec.clean_type}";
|
|
431
449
|
}
|
|
432
|
-
if
|
|
450
|
+
if (
|
|
451
|
+
isinstance(sym_node, uni.AstSymbolNode)
|
|
452
|
+
and isinstance(sym_node.name_spec.type, ClassType)
|
|
453
|
+
) {
|
|
433
454
|
node_info += f"': '{sym_node.name_spec.type.shared.class_name}";
|
|
434
455
|
}
|
|
435
|
-
if isinstance(sym_node, uni.AstDocNode) and sym_node.doc
|
|
456
|
+
if isinstance(sym_node, uni.AstDocNode) and sym_node.doc {
|
|
436
457
|
node_info += f"'\n'{sym_node.doc.value}";
|
|
437
458
|
}
|
|
438
|
-
if isinstance(sym_node, uni.Ability) and sym_node.signature
|
|
459
|
+
if isinstance(sym_node, uni.Ability) and sym_node.signature {
|
|
439
460
|
node_info += f"'\n'{sym_node.signature.unparse()}";
|
|
440
461
|
}
|
|
441
462
|
} except AttributeError as e {
|
|
@@ -447,9 +468,7 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
447
468
|
"""Return document symbols for a file."""
|
|
448
469
|
def get_outline(self: JacLangServer, file_path: str) -> list[lspt.DocumentSymbol] {
|
|
449
470
|
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)
|
|
452
|
-
{
|
|
471
|
+
if fs_path in self.mod.hub and (root_node := self.mod.hub[fs_path].sym_tab) {
|
|
453
472
|
return utils.get_symbols_for_outline(root_node);
|
|
454
473
|
}
|
|
455
474
|
return [];
|
|
@@ -457,116 +476,70 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
457
476
|
|
|
458
477
|
"""Return definition location for a file."""
|
|
459
478
|
def get_definition(
|
|
460
|
-
self: JacLangServer,
|
|
461
|
-
file_path: str,
|
|
462
|
-
position: lspt.Position
|
|
479
|
+
self: JacLangServer, file_path: str, position: lspt.Position
|
|
463
480
|
) -> Optional[lspt.Location] {
|
|
481
|
+
def make_location(node_: uni.UniNode, is_module: bool = False) -> lspt.Location {
|
|
482
|
+
mod_path = node_.type.file_uri if is_module else node_.loc.mod_path;
|
|
483
|
+
decl_range = (
|
|
484
|
+
utils.create_range(node_.loc)
|
|
485
|
+
if not is_module
|
|
486
|
+
else lspt.Range(
|
|
487
|
+
start=lspt.Position(line=0, character=0),
|
|
488
|
+
end=lspt.Position(line=0, character=0)
|
|
489
|
+
)
|
|
490
|
+
);
|
|
491
|
+
return lspt.Location(
|
|
492
|
+
uri=uris.from_fs_path(str(mod_path)), range=decl_range
|
|
493
|
+
);
|
|
494
|
+
}
|
|
464
495
|
node_selected = self.get_token_at_position(file_path, position);
|
|
465
|
-
if node_selected {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
if isinstance(node_selected.sym, uni.NameAtom) {
|
|
482
|
-
node_selected = node_selected.name_of;
|
|
483
|
-
}
|
|
484
|
-
elif isinstance(node_selected, uni.Name)
|
|
485
|
-
and node_selected.parent
|
|
486
|
-
and isinstance(node_selected.parent, uni.ModulePath)
|
|
487
|
-
{
|
|
488
|
-
spec = node_selected.parent.parent.abs_path;
|
|
489
|
-
if spec {
|
|
490
|
-
spec = spec[ 5 : ] if spec.startswith('File:') else spec;
|
|
491
|
-
return lspt.Location(
|
|
492
|
-
uri=uris.from_fs_path(spec),
|
|
493
|
-
range=lspt.Range(
|
|
494
|
-
start=lspt.Position(line=0, character=0),
|
|
495
|
-
end=lspt.Position(line=0, character=0)
|
|
496
|
-
)
|
|
497
|
-
);
|
|
498
|
-
} else {
|
|
499
|
-
return None;
|
|
500
|
-
}
|
|
501
|
-
} elif node_selected.parent
|
|
502
|
-
and isinstance(node_selected.parent, uni.ModuleItem)
|
|
503
|
-
{
|
|
504
|
-
path =
|
|
505
|
-
node_selected.parent.abs_path
|
|
506
|
-
or node_selected.parent.from_mod_path.abs_path
|
|
507
|
-
;
|
|
508
|
-
loc_range = (0, 0, 0, 0);
|
|
509
|
-
if path and loc_range {
|
|
510
|
-
path = path[ 5 : ] if path.startswith('File:') else path;
|
|
511
|
-
return lspt.Location(
|
|
512
|
-
uri=uris.from_fs_path(path),
|
|
513
|
-
range=lspt.Range(
|
|
514
|
-
start=lspt.Position(
|
|
515
|
-
line=loc_range[0],
|
|
516
|
-
character=loc_range[1]
|
|
517
|
-
),
|
|
518
|
-
end=lspt.Position(line=loc_range[2], character=loc_range[3])
|
|
519
|
-
)
|
|
520
|
-
);
|
|
521
|
-
}
|
|
522
|
-
} elif isinstance(node_selected, uni.ElementStmt) {
|
|
523
|
-
return None;
|
|
524
|
-
}
|
|
525
|
-
decl_node =
|
|
526
|
-
node_selected.parent.body.target
|
|
527
|
-
if node_selected.parent
|
|
528
|
-
and isinstance(node_selected.parent, uni.AstImplNeedingNode)
|
|
529
|
-
and isinstance(node_selected.parent.body, uni.ImplDef)
|
|
530
|
-
|
|
531
|
-
else node_selected.sym.decl
|
|
532
|
-
if node_selected.sym and node_selected.sym.decl
|
|
533
|
-
else node_selected;
|
|
534
|
-
if isinstance(decl_node, list) {
|
|
535
|
-
valid_path = decl_node[0].loc.mod_path;
|
|
536
|
-
} else {
|
|
537
|
-
valid_path = decl_node.loc.mod_path;
|
|
538
|
-
}
|
|
539
|
-
decl_uri = uris.from_fs_path(valid_path);
|
|
540
|
-
if isinstance(decl_node, list) {
|
|
541
|
-
valid_range = decl_node[0].loc;
|
|
542
|
-
} else {
|
|
543
|
-
valid_range = decl_node.loc;
|
|
544
|
-
}
|
|
545
|
-
try {
|
|
546
|
-
decl_range = utils.create_range(valid_range);
|
|
547
|
-
} except ValueError {
|
|
548
|
-
return None;
|
|
496
|
+
if not node_selected {
|
|
497
|
+
return None;
|
|
498
|
+
}
|
|
499
|
+
node_type = node_selected.type;
|
|
500
|
+
####################################################################
|
|
501
|
+
## Handle go to def for types and variables ##
|
|
502
|
+
####################################################################
|
|
503
|
+
if node_type {
|
|
504
|
+
if isinstance(node_type, ModuleType) {
|
|
505
|
+
return make_location(node_selected, is_module=True);
|
|
506
|
+
} elif isinstance(node_type, ClassType) {
|
|
507
|
+
return make_location(node_selected.sym.decl);
|
|
508
|
+
} elif isinstance(node_type, FunctionType) {
|
|
509
|
+
return make_location(node_selected.sym.decl);
|
|
549
510
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
511
|
+
}
|
|
512
|
+
####################################################################
|
|
513
|
+
## Ad-hoc handling for impl-def go to def ##
|
|
514
|
+
####################################################################
|
|
515
|
+
parent = node_selected.parent;
|
|
516
|
+
if not parent {
|
|
553
517
|
return None;
|
|
554
518
|
}
|
|
519
|
+
if isinstance(parent, uni.ImplDef) and parent.decl_link {
|
|
520
|
+
return make_location(parent.decl_link.sym.decl);
|
|
521
|
+
}
|
|
522
|
+
if isinstance(parent, uni.AstImplNeedingNode) and parent.body {
|
|
523
|
+
return make_location(parent.body.sym.decl);
|
|
524
|
+
}
|
|
525
|
+
if not node_selected or not node_selected.type {
|
|
526
|
+
return None;
|
|
527
|
+
}
|
|
528
|
+
return None;
|
|
555
529
|
}
|
|
556
530
|
|
|
557
531
|
"""Return references for a file."""
|
|
558
532
|
def get_references(
|
|
559
|
-
self: JacLangServer,
|
|
560
|
-
file_path: str,
|
|
561
|
-
position: lspt.Position
|
|
533
|
+
self: JacLangServer, file_path: str, position: lspt.Position
|
|
562
534
|
) -> list[lspt.Location] {
|
|
563
535
|
node_selected = self.get_token_at_position(file_path, position);
|
|
564
|
-
if node_selected and node_selected.sym
|
|
565
|
-
list_of_references: list[lspt.Location] =
|
|
566
|
-
|
|
536
|
+
if node_selected and node_selected.sym {
|
|
537
|
+
list_of_references: list[lspt.Location] = [
|
|
538
|
+
lspt.Location(
|
|
567
539
|
uri=uris.from_fs_path(cur_node.loc.mod_path),
|
|
568
540
|
range=utils.create_range(cur_node.loc)
|
|
569
|
-
) for cur_node in node_selected.sym.uses
|
|
541
|
+
) for cur_node in node_selected.sym.uses
|
|
542
|
+
];
|
|
570
543
|
return list_of_references;
|
|
571
544
|
}
|
|
572
545
|
return [];
|
|
@@ -574,21 +547,16 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
574
547
|
|
|
575
548
|
"""Rename a symbol in a file."""
|
|
576
549
|
def rename_symbol(
|
|
577
|
-
self: JacLangServer,
|
|
578
|
-
file_path: str,
|
|
579
|
-
position: lspt.Position,
|
|
580
|
-
new_name: str
|
|
550
|
+
self: JacLangServer, file_path: str, position: lspt.Position, new_name: str
|
|
581
551
|
) -> Optional[lspt.WorkspaceEdit] {
|
|
582
552
|
node_selected = self.get_token_at_position(file_path, position);
|
|
583
|
-
if node_selected and node_selected.sym
|
|
553
|
+
if node_selected and node_selected.sym {
|
|
584
554
|
changes: dict[(str, list[lspt.TextEdit])] = {};
|
|
585
555
|
for node in [*node_selected.sym.uses, node_selected.sym.defn[0]] {
|
|
586
556
|
key = uris.from_fs_path(node.loc.mod_path);
|
|
587
|
-
new_edit =
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
new_text=new_name
|
|
591
|
-
);
|
|
557
|
+
new_edit = lspt.TextEdit(
|
|
558
|
+
range=utils.create_range(node.loc), new_text=new_name
|
|
559
|
+
);
|
|
592
560
|
utils.add_unique_text_edit(changes, key, new_edit);
|
|
593
561
|
}
|
|
594
562
|
return lspt.WorkspaceEdit(changes=changes);
|
|
@@ -629,36 +597,4 @@ class JacLangServer(JacProgram , LanguageServer) {
|
|
|
629
597
|
logging.info(message);
|
|
630
598
|
}
|
|
631
599
|
|
|
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
|
-
|
|
664
600
|
}
|