jaclang 0.7.9__py3-none-any.whl → 0.7.13__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 +13 -3
- jaclang/compiler/absyntree.py +10 -2
- jaclang/compiler/parser.py +2 -1
- jaclang/compiler/passes/ir_pass.py +9 -0
- jaclang/compiler/passes/main/import_pass.py +15 -2
- jaclang/compiler/passes/main/pyast_gen_pass.py +238 -32
- jaclang/compiler/passes/main/pyast_load_pass.py +3 -1
- jaclang/compiler/passes/main/tests/test_import_pass.py +13 -0
- jaclang/compiler/passes/main/tests/test_pyast_gen_pass.py +2 -2
- jaclang/compiler/passes/main/tests/test_sub_node_pass.py +1 -3
- jaclang/compiler/passes/main/type_check_pass.py +0 -17
- jaclang/compiler/passes/tool/tests/test_unparse_validate.py +1 -2
- jaclang/compiler/tests/test_importer.py +1 -1
- jaclang/core/importer.py +233 -62
- jaclang/langserve/engine.py +182 -136
- jaclang/langserve/server.py +29 -7
- jaclang/langserve/tests/fixtures/base_module_structure.jac +28 -2
- jaclang/langserve/tests/fixtures/import_include_statements.jac +1 -1
- jaclang/langserve/tests/test_server.py +73 -66
- jaclang/langserve/utils.py +266 -0
- jaclang/plugin/default.py +7 -4
- jaclang/plugin/feature.py +3 -3
- jaclang/plugin/spec.py +3 -3
- jaclang/settings.py +1 -0
- jaclang/tests/fixtures/deep/one_lev.jac +6 -4
- jaclang/tests/fixtures/needs_import.jac +1 -1
- jaclang/tests/test_cli.py +7 -9
- jaclang/tests/test_language.py +9 -13
- jaclang/tests/test_man_code.py +8 -10
- jaclang/utils/helpers.py +3 -3
- jaclang/utils/test.py +8 -0
- {jaclang-0.7.9.dist-info → jaclang-0.7.13.dist-info}/METADATA +2 -2
- {jaclang-0.7.9.dist-info → jaclang-0.7.13.dist-info}/RECORD +35 -35
- {jaclang-0.7.9.dist-info → jaclang-0.7.13.dist-info}/WHEEL +0 -0
- {jaclang-0.7.9.dist-info → jaclang-0.7.13.dist-info}/entry_points.txt +0 -0
jaclang/langserve/engine.py
CHANGED
|
@@ -5,23 +5,27 @@ from __future__ import annotations
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import logging
|
|
7
7
|
from concurrent.futures import ThreadPoolExecutor
|
|
8
|
-
from
|
|
9
|
-
from typing import Optional
|
|
8
|
+
from typing import Callable, Optional
|
|
10
9
|
|
|
11
10
|
|
|
12
11
|
import jaclang.compiler.absyntree as ast
|
|
13
|
-
from jaclang.compiler.compile import
|
|
12
|
+
from jaclang.compiler.compile import jac_str_to_pass
|
|
14
13
|
from jaclang.compiler.parser import JacParser
|
|
15
14
|
from jaclang.compiler.passes import Pass
|
|
16
|
-
from jaclang.compiler.passes.main.schedules import
|
|
15
|
+
from jaclang.compiler.passes.main.schedules import py_code_gen_typed
|
|
17
16
|
from jaclang.compiler.passes.tool import FuseCommentsPass, JacFormatPass
|
|
18
17
|
from jaclang.compiler.passes.transform import Alert
|
|
19
18
|
from jaclang.langserve.utils import (
|
|
19
|
+
collect_all_symbols_in_scope,
|
|
20
20
|
collect_symbols,
|
|
21
21
|
create_range,
|
|
22
22
|
find_deepest_symbol_node_at_pos,
|
|
23
|
+
gen_diagnostics,
|
|
23
24
|
get_item_path,
|
|
24
25
|
get_mod_path,
|
|
26
|
+
locate_affected_token,
|
|
27
|
+
parse_symbol_path,
|
|
28
|
+
resolve_completion_symbol_table,
|
|
25
29
|
)
|
|
26
30
|
from jaclang.vendor.pygls import uris
|
|
27
31
|
from jaclang.vendor.pygls.server import LanguageServer
|
|
@@ -29,14 +33,6 @@ from jaclang.vendor.pygls.server import LanguageServer
|
|
|
29
33
|
import lsprotocol.types as lspt
|
|
30
34
|
|
|
31
35
|
|
|
32
|
-
class ALev(IntEnum):
|
|
33
|
-
"""Analysis Level successfully completed."""
|
|
34
|
-
|
|
35
|
-
QUICK = 1
|
|
36
|
-
DEEP = 2
|
|
37
|
-
TYPE = 3
|
|
38
|
-
|
|
39
|
-
|
|
40
36
|
class ModuleInfo:
|
|
41
37
|
"""Module IR and Stats."""
|
|
42
38
|
|
|
@@ -45,16 +41,11 @@ class ModuleInfo:
|
|
|
45
41
|
ir: ast.Module,
|
|
46
42
|
errors: list[Alert],
|
|
47
43
|
warnings: list[Alert],
|
|
48
|
-
alev: ALev,
|
|
49
44
|
parent: Optional[ModuleInfo] = None,
|
|
50
45
|
) -> None:
|
|
51
46
|
"""Initialize module info."""
|
|
52
47
|
self.ir = ir
|
|
53
|
-
self.errors = errors
|
|
54
|
-
self.warnings = warnings
|
|
55
|
-
self.alev = alev
|
|
56
48
|
self.parent: Optional[ModuleInfo] = parent
|
|
57
|
-
self.diagnostics = self.gen_diagnostics()
|
|
58
49
|
self.sem_tokens: list[int] = self.gen_sem_tokens()
|
|
59
50
|
|
|
60
51
|
@property
|
|
@@ -62,43 +53,6 @@ class ModuleInfo:
|
|
|
62
53
|
"""Return uri."""
|
|
63
54
|
return uris.from_fs_path(self.ir.loc.mod_path)
|
|
64
55
|
|
|
65
|
-
@property
|
|
66
|
-
def has_syntax_error(self) -> bool:
|
|
67
|
-
"""Return if there are syntax errors."""
|
|
68
|
-
return len(self.errors) > 0 and self.alev == ALev.QUICK
|
|
69
|
-
|
|
70
|
-
def update_with(self, new_info: ModuleInfo, refresh: bool = False) -> None:
|
|
71
|
-
"""Update module info."""
|
|
72
|
-
self.ir = new_info.ir
|
|
73
|
-
if refresh:
|
|
74
|
-
self.errors = new_info.errors
|
|
75
|
-
self.warnings = new_info.warnings
|
|
76
|
-
else:
|
|
77
|
-
self.errors += [i for i in new_info.errors if i not in self.errors]
|
|
78
|
-
self.warnings += [i for i in new_info.warnings if i not in self.warnings]
|
|
79
|
-
self.alev = new_info.alev
|
|
80
|
-
self.diagnostics = self.gen_diagnostics()
|
|
81
|
-
if self.alev == ALev.TYPE:
|
|
82
|
-
self.sem_tokens = self.gen_sem_tokens()
|
|
83
|
-
|
|
84
|
-
def gen_diagnostics(self) -> list[lspt.Diagnostic]:
|
|
85
|
-
"""Return diagnostics."""
|
|
86
|
-
return [
|
|
87
|
-
lspt.Diagnostic(
|
|
88
|
-
range=create_range(error.loc),
|
|
89
|
-
message=error.msg,
|
|
90
|
-
severity=lspt.DiagnosticSeverity.Error,
|
|
91
|
-
)
|
|
92
|
-
for error in self.errors
|
|
93
|
-
] + [
|
|
94
|
-
lspt.Diagnostic(
|
|
95
|
-
range=create_range(warning.loc),
|
|
96
|
-
message=warning.msg,
|
|
97
|
-
severity=lspt.DiagnosticSeverity.Warning,
|
|
98
|
-
)
|
|
99
|
-
for warning in self.warnings
|
|
100
|
-
]
|
|
101
|
-
|
|
102
56
|
def gen_sem_tokens(self) -> list[int]:
|
|
103
57
|
"""Return semantic tokens."""
|
|
104
58
|
tokens = []
|
|
@@ -120,6 +74,76 @@ class ModuleInfo:
|
|
|
120
74
|
prev_line, prev_col = line, col_start
|
|
121
75
|
return tokens
|
|
122
76
|
|
|
77
|
+
def update_sem_tokens(
|
|
78
|
+
self, content_changes: lspt.DidChangeTextDocumentParams
|
|
79
|
+
) -> list[int]:
|
|
80
|
+
"""Update semantic tokens on change."""
|
|
81
|
+
for change in [
|
|
82
|
+
x
|
|
83
|
+
for x in content_changes.content_changes
|
|
84
|
+
if isinstance(x, lspt.TextDocumentContentChangeEvent_Type1)
|
|
85
|
+
]:
|
|
86
|
+
change_start_line = change.range.start.line
|
|
87
|
+
change_start_char = change.range.start.character
|
|
88
|
+
change_end_line = change.range.end.line
|
|
89
|
+
change_end_char = change.range.end.character
|
|
90
|
+
|
|
91
|
+
line_delta = change.text.count("\n") - (change_end_line - change_start_line)
|
|
92
|
+
if line_delta == 0:
|
|
93
|
+
char_delta = len(change.text) - (change_end_char - change_start_char)
|
|
94
|
+
else:
|
|
95
|
+
last_newline_index = change.text.rfind("\n")
|
|
96
|
+
char_delta = (
|
|
97
|
+
len(change.text)
|
|
98
|
+
- last_newline_index
|
|
99
|
+
- 1
|
|
100
|
+
- change_end_char
|
|
101
|
+
+ change_start_char
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
changed_token_index = locate_affected_token(
|
|
105
|
+
self.sem_tokens,
|
|
106
|
+
change_start_line,
|
|
107
|
+
change_start_char,
|
|
108
|
+
change_end_line,
|
|
109
|
+
change_end_char,
|
|
110
|
+
)
|
|
111
|
+
if changed_token_index:
|
|
112
|
+
self.sem_tokens[changed_token_index + 2] = max(
|
|
113
|
+
1, self.sem_tokens[changed_token_index + 2] + char_delta
|
|
114
|
+
)
|
|
115
|
+
if (
|
|
116
|
+
len(self.sem_tokens) > changed_token_index + 5
|
|
117
|
+
and self.sem_tokens[changed_token_index + 5] == 0
|
|
118
|
+
):
|
|
119
|
+
next_token_index = changed_token_index + 5
|
|
120
|
+
self.sem_tokens[next_token_index + 1] = max(
|
|
121
|
+
0, self.sem_tokens[next_token_index + 1] + char_delta
|
|
122
|
+
)
|
|
123
|
+
return self.sem_tokens
|
|
124
|
+
|
|
125
|
+
current_token_index = 0
|
|
126
|
+
line_offset = 0
|
|
127
|
+
while current_token_index < len(self.sem_tokens):
|
|
128
|
+
token_line_number = self.sem_tokens[current_token_index] + line_offset
|
|
129
|
+
token_start_pos = self.sem_tokens[current_token_index + 1]
|
|
130
|
+
|
|
131
|
+
if token_line_number > change_start_line or (
|
|
132
|
+
token_line_number == change_start_line
|
|
133
|
+
and token_start_pos >= change_start_char
|
|
134
|
+
):
|
|
135
|
+
self.sem_tokens[current_token_index] += line_delta
|
|
136
|
+
if token_line_number == change_start_line:
|
|
137
|
+
self.sem_tokens[current_token_index + 1] += char_delta
|
|
138
|
+
if token_line_number > change_end_line or (
|
|
139
|
+
token_line_number == change_end_line
|
|
140
|
+
and token_start_pos >= change_end_char
|
|
141
|
+
):
|
|
142
|
+
break
|
|
143
|
+
line_offset += self.sem_tokens[current_token_index]
|
|
144
|
+
current_token_index += 5
|
|
145
|
+
return self.sem_tokens
|
|
146
|
+
|
|
123
147
|
|
|
124
148
|
class JacLangServer(LanguageServer):
|
|
125
149
|
"""Class for managing workspace."""
|
|
@@ -129,36 +153,16 @@ class JacLangServer(LanguageServer):
|
|
|
129
153
|
super().__init__("jac-lsp", "v0.1")
|
|
130
154
|
self.modules: dict[str, ModuleInfo] = {}
|
|
131
155
|
self.executor = ThreadPoolExecutor()
|
|
132
|
-
|
|
133
|
-
async def push_diagnostics(self, file_path: str) -> None:
|
|
134
|
-
"""Push diagnostics for a file."""
|
|
135
|
-
if file_path in self.modules:
|
|
136
|
-
self.publish_diagnostics(
|
|
137
|
-
file_path,
|
|
138
|
-
self.modules[file_path].diagnostics,
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
def unwind_to_parent(self, file_path: str) -> str:
|
|
142
|
-
"""Unwind to parent."""
|
|
143
|
-
orig_file_path = file_path
|
|
144
|
-
if file_path in self.modules:
|
|
145
|
-
while cur := self.modules[file_path].parent:
|
|
146
|
-
file_path = cur.uri
|
|
147
|
-
if file_path == orig_file_path and (
|
|
148
|
-
discover := self.modules[file_path].ir.annexable_by
|
|
149
|
-
):
|
|
150
|
-
file_path = uris.from_fs_path(discover)
|
|
151
|
-
self.quick_check(file_path)
|
|
152
|
-
return file_path
|
|
156
|
+
self.tasks: dict[str, asyncio.Task] = {}
|
|
153
157
|
|
|
154
158
|
def update_modules(
|
|
155
|
-
self, file_path: str, build: Pass,
|
|
159
|
+
self, file_path: str, build: Pass, refresh: bool = False
|
|
156
160
|
) -> None:
|
|
157
161
|
"""Update modules."""
|
|
158
162
|
if not isinstance(build.ir, ast.Module):
|
|
159
163
|
self.log_error("Error with module build.")
|
|
160
164
|
return
|
|
161
|
-
|
|
165
|
+
self.modules[file_path] = ModuleInfo(
|
|
162
166
|
ir=build.ir,
|
|
163
167
|
errors=[
|
|
164
168
|
i
|
|
@@ -170,24 +174,14 @@ class JacLangServer(LanguageServer):
|
|
|
170
174
|
for i in build.warnings_had
|
|
171
175
|
if i.loc.mod_path == uris.to_fs_path(file_path)
|
|
172
176
|
],
|
|
173
|
-
alev=alev,
|
|
174
177
|
)
|
|
175
|
-
if file_path in self.modules:
|
|
176
|
-
self.modules[file_path].update_with(new_mod, refresh=refresh)
|
|
177
|
-
else:
|
|
178
|
-
self.modules[file_path] = new_mod
|
|
179
178
|
for p in build.ir.mod_deps.keys():
|
|
180
179
|
uri = uris.from_fs_path(p)
|
|
181
|
-
|
|
180
|
+
self.modules[uri] = ModuleInfo(
|
|
182
181
|
ir=build.ir.mod_deps[p],
|
|
183
182
|
errors=[i for i in build.errors_had if i.loc.mod_path == p],
|
|
184
183
|
warnings=[i for i in build.warnings_had if i.loc.mod_path == p],
|
|
185
|
-
alev=alev,
|
|
186
184
|
)
|
|
187
|
-
if not refresh and uri in self.modules:
|
|
188
|
-
self.modules[uri].update_with(new_mod)
|
|
189
|
-
else:
|
|
190
|
-
self.modules[uri] = new_mod
|
|
191
185
|
self.modules[uri].parent = (
|
|
192
186
|
self.modules[file_path] if file_path != uri else None
|
|
193
187
|
)
|
|
@@ -199,72 +193,100 @@ class JacLangServer(LanguageServer):
|
|
|
199
193
|
build = jac_str_to_pass(
|
|
200
194
|
jac_str=document.source, file_path=document.path, schedule=[]
|
|
201
195
|
)
|
|
196
|
+
self.publish_diagnostics(
|
|
197
|
+
file_path,
|
|
198
|
+
gen_diagnostics(file_path, build.errors_had, build.warnings_had),
|
|
199
|
+
)
|
|
200
|
+
return len(build.errors_had) == 0
|
|
202
201
|
except Exception as e:
|
|
203
202
|
self.log_error(f"Error during syntax check: {e}")
|
|
204
203
|
return False
|
|
205
|
-
self.update_modules(file_path, build, ALev.QUICK, refresh=True)
|
|
206
|
-
return len(self.modules[file_path].errors) == 0
|
|
207
|
-
|
|
208
|
-
def deep_check(self, file_path: str) -> bool:
|
|
209
|
-
"""Rebuild a file and its dependencies."""
|
|
210
|
-
if file_path not in self.modules:
|
|
211
|
-
self.quick_check(file_path)
|
|
212
|
-
try:
|
|
213
|
-
file_path = self.unwind_to_parent(file_path)
|
|
214
|
-
build = jac_ir_to_pass(ir=self.modules[file_path].ir)
|
|
215
|
-
except Exception as e:
|
|
216
|
-
self.log_error(f"Error during syntax check: {e}")
|
|
217
|
-
return False
|
|
218
|
-
self.update_modules(file_path, build, ALev.DEEP)
|
|
219
|
-
return len(self.modules[file_path].errors) == 0
|
|
220
204
|
|
|
221
|
-
def
|
|
205
|
+
def deep_check(self, file_path: str, annex_view: Optional[str] = None) -> bool:
|
|
222
206
|
"""Rebuild a file and its dependencies."""
|
|
223
|
-
if file_path not in self.modules:
|
|
224
|
-
self.deep_check(file_path)
|
|
225
207
|
try:
|
|
226
|
-
|
|
227
|
-
build =
|
|
228
|
-
|
|
208
|
+
document = self.workspace.get_text_document(file_path)
|
|
209
|
+
build = jac_str_to_pass(
|
|
210
|
+
jac_str=document.source,
|
|
211
|
+
file_path=document.path,
|
|
212
|
+
schedule=py_code_gen_typed,
|
|
213
|
+
)
|
|
214
|
+
self.update_modules(file_path, build)
|
|
215
|
+
if discover := self.modules[file_path].ir.annexable_by:
|
|
216
|
+
return self.deep_check(
|
|
217
|
+
uris.from_fs_path(discover), annex_view=file_path
|
|
218
|
+
)
|
|
219
|
+
self.publish_diagnostics(
|
|
220
|
+
file_path,
|
|
221
|
+
gen_diagnostics(
|
|
222
|
+
annex_view if annex_view else file_path,
|
|
223
|
+
build.errors_had,
|
|
224
|
+
build.warnings_had,
|
|
225
|
+
),
|
|
229
226
|
)
|
|
227
|
+
return len(build.errors_had) == 0
|
|
230
228
|
except Exception as e:
|
|
231
|
-
self.log_error(f"Error during
|
|
229
|
+
self.log_error(f"Error during deep check: {e}")
|
|
232
230
|
return False
|
|
233
|
-
self.update_modules(file_path, build, ALev.TYPE)
|
|
234
|
-
return len(self.modules[file_path].errors) == 0
|
|
235
231
|
|
|
236
|
-
async def
|
|
232
|
+
async def launch_quick_check(self, uri: str) -> None:
|
|
237
233
|
"""Analyze and publish diagnostics."""
|
|
238
|
-
|
|
239
|
-
success = await asyncio.get_event_loop().run_in_executor(
|
|
234
|
+
await asyncio.get_event_loop().run_in_executor(
|
|
240
235
|
self.executor, self.quick_check, uri
|
|
241
236
|
)
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
237
|
+
|
|
238
|
+
async def launch_deep_check(self, uri: str) -> None:
|
|
239
|
+
"""Analyze and publish diagnostics."""
|
|
240
|
+
|
|
241
|
+
async def run_in_executor(
|
|
242
|
+
func: Callable[[str, Optional[str]], bool],
|
|
243
|
+
file_path: str,
|
|
244
|
+
annex_view: Optional[str] = None,
|
|
245
|
+
) -> None:
|
|
246
|
+
loop = asyncio.get_event_loop()
|
|
247
|
+
await loop.run_in_executor(self.executor, func, file_path, annex_view)
|
|
248
|
+
|
|
249
|
+
if uri in self.tasks and not self.tasks[uri].done():
|
|
250
|
+
self.log_py(f"Canceling {uri} deep check...")
|
|
251
|
+
self.tasks[uri].cancel()
|
|
252
|
+
del self.tasks[uri]
|
|
253
|
+
self.log_py(f"Analyzing {uri}...")
|
|
254
|
+
task = asyncio.create_task(run_in_executor(self.deep_check, uri))
|
|
255
|
+
self.tasks[uri] = task
|
|
256
|
+
await task
|
|
253
257
|
|
|
254
258
|
def get_completion(
|
|
255
|
-
self, file_path: str, position: lspt.Position
|
|
259
|
+
self, file_path: str, position: lspt.Position, completion_trigger: Optional[str]
|
|
256
260
|
) -> lspt.CompletionList:
|
|
257
261
|
"""Return completion for a file."""
|
|
258
|
-
|
|
262
|
+
completion_items = []
|
|
259
263
|
document = self.workspace.get_text_document(file_path)
|
|
260
|
-
current_line = document.lines[position.line]
|
|
261
|
-
|
|
264
|
+
current_line = document.lines[position.line]
|
|
265
|
+
current_pos = position.character
|
|
266
|
+
current_symbol_path = parse_symbol_path(current_line, current_pos)
|
|
267
|
+
node_selected = find_deepest_symbol_node_at_pos(
|
|
268
|
+
self.modules[file_path].ir,
|
|
269
|
+
position.line,
|
|
270
|
+
position.character - 2,
|
|
271
|
+
)
|
|
262
272
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
273
|
+
mod_tab = (
|
|
274
|
+
self.modules[file_path].ir.sym_tab
|
|
275
|
+
if not node_selected
|
|
276
|
+
else node_selected.sym_tab
|
|
277
|
+
)
|
|
278
|
+
current_tab = self.modules[file_path].ir._sym_tab
|
|
279
|
+
current_symbol_table = mod_tab
|
|
280
|
+
if completion_trigger == ".":
|
|
281
|
+
completion_items = resolve_completion_symbol_table(
|
|
282
|
+
mod_tab, current_symbol_path, current_tab
|
|
283
|
+
)
|
|
284
|
+
else:
|
|
285
|
+
try: # noqa SIM105
|
|
286
|
+
completion_items = collect_all_symbols_in_scope(current_symbol_table)
|
|
287
|
+
except AttributeError:
|
|
288
|
+
pass
|
|
289
|
+
return lspt.CompletionList(is_incomplete=False, items=completion_items)
|
|
268
290
|
|
|
269
291
|
def rename_module(self, old_path: str, new_path: str) -> None:
|
|
270
292
|
"""Rename module."""
|
|
@@ -311,6 +333,8 @@ class JacLangServer(LanguageServer):
|
|
|
311
333
|
self, file_path: str, position: lspt.Position
|
|
312
334
|
) -> Optional[lspt.Hover]:
|
|
313
335
|
"""Return hover information for a file."""
|
|
336
|
+
if file_path not in self.modules:
|
|
337
|
+
return None
|
|
314
338
|
node_selected = find_deepest_symbol_node_at_pos(
|
|
315
339
|
self.modules[file_path].ir, position.line, position.character
|
|
316
340
|
)
|
|
@@ -347,7 +371,9 @@ class JacLangServer(LanguageServer):
|
|
|
347
371
|
|
|
348
372
|
def get_document_symbols(self, file_path: str) -> list[lspt.DocumentSymbol]:
|
|
349
373
|
"""Return document symbols for a file."""
|
|
350
|
-
if
|
|
374
|
+
if file_path in self.modules and (
|
|
375
|
+
root_node := self.modules[file_path].ir._sym_tab
|
|
376
|
+
):
|
|
351
377
|
return collect_symbols(root_node)
|
|
352
378
|
return []
|
|
353
379
|
|
|
@@ -355,6 +381,8 @@ class JacLangServer(LanguageServer):
|
|
|
355
381
|
self, file_path: str, position: lspt.Position
|
|
356
382
|
) -> Optional[lspt.Location]:
|
|
357
383
|
"""Return definition location for a file."""
|
|
384
|
+
if file_path not in self.modules:
|
|
385
|
+
return None
|
|
358
386
|
node_selected: Optional[ast.AstSymbolNode] = find_deepest_symbol_node_at_pos(
|
|
359
387
|
self.modules[file_path].ir, position.line, position.character
|
|
360
388
|
)
|
|
@@ -419,6 +447,24 @@ class JacLangServer(LanguageServer):
|
|
|
419
447
|
else:
|
|
420
448
|
return None
|
|
421
449
|
|
|
450
|
+
def get_references(
|
|
451
|
+
self, file_path: str, position: lspt.Position
|
|
452
|
+
) -> list[lspt.Location]:
|
|
453
|
+
"""Return references for a file."""
|
|
454
|
+
node_selected = find_deepest_symbol_node_at_pos(
|
|
455
|
+
self.modules[file_path].ir, position.line, position.character
|
|
456
|
+
)
|
|
457
|
+
if node_selected and node_selected.sym:
|
|
458
|
+
list_of_references: list[lspt.Location] = [
|
|
459
|
+
lspt.Location(
|
|
460
|
+
uri=uris.from_fs_path(node.loc.mod_path),
|
|
461
|
+
range=create_range(node.loc),
|
|
462
|
+
)
|
|
463
|
+
for node in node_selected.sym.uses
|
|
464
|
+
]
|
|
465
|
+
return list_of_references
|
|
466
|
+
return []
|
|
467
|
+
|
|
422
468
|
def get_semantic_tokens(self, file_path: str) -> lspt.SemanticTokens:
|
|
423
469
|
"""Return semantic tokens for a file."""
|
|
424
470
|
if file_path not in self.modules:
|
jaclang/langserve/server.py
CHANGED
|
@@ -9,7 +9,6 @@ from jaclang.compiler.constant import (
|
|
|
9
9
|
JacSemTokenType as SemTokType,
|
|
10
10
|
)
|
|
11
11
|
from jaclang.langserve.engine import JacLangServer
|
|
12
|
-
from jaclang.langserve.utils import debounce
|
|
13
12
|
|
|
14
13
|
import lsprotocol.types as lspt
|
|
15
14
|
|
|
@@ -20,14 +19,19 @@ server = JacLangServer()
|
|
|
20
19
|
@server.feature(lspt.TEXT_DOCUMENT_DID_SAVE)
|
|
21
20
|
async def did_open(ls: JacLangServer, params: lspt.DidOpenTextDocumentParams) -> None:
|
|
22
21
|
"""Check syntax on change."""
|
|
23
|
-
await ls.
|
|
22
|
+
await ls.launch_deep_check(params.text_document.uri)
|
|
23
|
+
ls.lsp.send_request(lspt.WORKSPACE_SEMANTIC_TOKENS_REFRESH)
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
@server.feature(lspt.TEXT_DOCUMENT_DID_CHANGE)
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
async def did_change(
|
|
28
|
+
ls: JacLangServer, params: lspt.DidChangeTextDocumentParams
|
|
29
|
+
) -> None:
|
|
29
30
|
"""Check syntax on change."""
|
|
30
|
-
await ls.
|
|
31
|
+
await ls.launch_quick_check(file_path := params.text_document.uri)
|
|
32
|
+
if file_path in ls.modules:
|
|
33
|
+
ls.modules[file_path].update_sem_tokens(params)
|
|
34
|
+
ls.lsp.send_request(lspt.WORKSPACE_SEMANTIC_TOKENS_REFRESH)
|
|
31
35
|
|
|
32
36
|
|
|
33
37
|
@server.feature(lspt.TEXT_DOCUMENT_FORMATTING)
|
|
@@ -82,11 +86,15 @@ def did_delete_files(ls: JacLangServer, params: lspt.DeleteFilesParams) -> None:
|
|
|
82
86
|
|
|
83
87
|
@server.feature(
|
|
84
88
|
lspt.TEXT_DOCUMENT_COMPLETION,
|
|
85
|
-
lspt.CompletionOptions(trigger_characters=[".", ":", ""]),
|
|
89
|
+
lspt.CompletionOptions(trigger_characters=[".", ":", "a-zA-Z0-9"]),
|
|
86
90
|
)
|
|
87
91
|
def completion(ls: JacLangServer, params: lspt.CompletionParams) -> lspt.CompletionList:
|
|
88
92
|
"""Provide completion."""
|
|
89
|
-
return ls.get_completion(
|
|
93
|
+
return ls.get_completion(
|
|
94
|
+
params.text_document.uri,
|
|
95
|
+
params.position,
|
|
96
|
+
params.context.trigger_character if params.context else None,
|
|
97
|
+
)
|
|
90
98
|
|
|
91
99
|
|
|
92
100
|
@server.feature(lspt.TEXT_DOCUMENT_HOVER, lspt.HoverOptions(work_done_progress=True))
|
|
@@ -113,6 +121,12 @@ def definition(
|
|
|
113
121
|
return ls.get_definition(params.text_document.uri, params.position)
|
|
114
122
|
|
|
115
123
|
|
|
124
|
+
@server.feature(lspt.TEXT_DOCUMENT_REFERENCES)
|
|
125
|
+
def references(ls: JacLangServer, params: lspt.ReferenceParams) -> list[lspt.Location]:
|
|
126
|
+
"""Provide references."""
|
|
127
|
+
return ls.get_references(params.text_document.uri, params.position)
|
|
128
|
+
|
|
129
|
+
|
|
116
130
|
@server.feature(
|
|
117
131
|
lspt.TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL,
|
|
118
132
|
lspt.SemanticTokensLegend(
|
|
@@ -124,6 +138,14 @@ def semantic_tokens_full(
|
|
|
124
138
|
ls: JacLangServer, params: lspt.SemanticTokensParams
|
|
125
139
|
) -> lspt.SemanticTokens:
|
|
126
140
|
"""Provide semantic tokens."""
|
|
141
|
+
# import logging
|
|
142
|
+
|
|
143
|
+
# logging.info("\nGetting semantic tokens\n")
|
|
144
|
+
# # logging.info(ls.get_semantic_tokens(params.text_document.uri))
|
|
145
|
+
# i = 0
|
|
146
|
+
# while i < len(ls.get_semantic_tokens(params.text_document.uri).data):
|
|
147
|
+
# logging.info(ls.get_semantic_tokens(params.text_document.uri).data[i : i + 5])
|
|
148
|
+
# i += 5
|
|
127
149
|
return ls.get_semantic_tokens(params.text_document.uri)
|
|
128
150
|
|
|
129
151
|
|
|
@@ -21,8 +21,34 @@ with entry:__main__ {
|
|
|
21
21
|
|
|
22
22
|
glob x: int = 10;
|
|
23
23
|
|
|
24
|
-
enum
|
|
24
|
+
enum Colorenum {
|
|
25
25
|
RED,
|
|
26
26
|
GREEN,
|
|
27
27
|
BLUE
|
|
28
|
-
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
obj Colour1 {
|
|
31
|
+
has color1: Colorenum,
|
|
32
|
+
point1: int;
|
|
33
|
+
|
|
34
|
+
can get_color1 -> Colorenum;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
:obj:Colour1:can:get_color1 -> Colorenum {
|
|
38
|
+
return self.color;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
obj red :Colour1: {
|
|
42
|
+
has base_colorred: Colorenum = Color.RED,
|
|
43
|
+
pointred: int = 10;
|
|
44
|
+
obj color2 {
|
|
45
|
+
has color22: Color = Colorenum.BLUE,
|
|
46
|
+
point22: int = 20;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
with entry:__main__ {
|
|
51
|
+
r = red(color1=Color.GREEN, point1=20);
|
|
52
|
+
print(r.get_color1());
|
|
53
|
+
print(r.color2.color22);
|
|
54
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import:py os;
|
|
2
2
|
import:py from math, sqrt as square_root;
|
|
3
3
|
import:py datetime as dt;
|
|
4
|
-
import:jac from base_module_structure,
|
|
4
|
+
import:jac from base_module_structure, add_numbers as adsd, subtract,x,Colorenum as clr;
|
|
5
5
|
import:jac base_module_structure as base_module_structure;
|
|
6
6
|
import:py from py_import,add1 as ss, sub1 as subtract1,apple,Orange1;
|