jaclang 0.7.11__py3-none-any.whl → 0.7.14__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 +10 -2
- jaclang/compiler/absyntree.py +19 -6
- jaclang/compiler/parser.py +6 -1
- jaclang/compiler/passes/main/import_pass.py +1 -0
- jaclang/compiler/passes/main/pyast_gen_pass.py +239 -40
- jaclang/compiler/passes/main/pyast_load_pass.py +4 -1
- jaclang/compiler/passes/main/tests/test_import_pass.py +5 -1
- jaclang/compiler/passes/main/type_check_pass.py +0 -17
- jaclang/compiler/passes/tool/fuse_comments_pass.py +14 -2
- jaclang/compiler/passes/tool/jac_formatter_pass.py +22 -10
- jaclang/compiler/tests/test_importer.py +1 -1
- jaclang/core/importer.py +126 -89
- jaclang/langserve/engine.py +173 -169
- jaclang/langserve/server.py +19 -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 +77 -64
- jaclang/langserve/utils.py +266 -0
- jaclang/plugin/default.py +4 -2
- jaclang/plugin/feature.py +2 -2
- jaclang/plugin/spec.py +2 -2
- jaclang/tests/fixtures/blankwithentry.jac +3 -0
- jaclang/tests/fixtures/deep/one_lev.jac +3 -0
- jaclang/tests/fixtures/needs_import.jac +1 -1
- jaclang/tests/test_cli.py +6 -6
- jaclang/tests/test_language.py +9 -0
- jaclang/tests/test_man_code.py +17 -0
- {jaclang-0.7.11.dist-info → jaclang-0.7.14.dist-info}/METADATA +1 -1
- {jaclang-0.7.11.dist-info → jaclang-0.7.14.dist-info}/RECORD +31 -30
- {jaclang-0.7.11.dist-info → jaclang-0.7.14.dist-info}/WHEEL +0 -0
- {jaclang-0.7.11.dist-info → jaclang-0.7.14.dist-info}/entry_points.txt +0 -0
jaclang/langserve/engine.py
CHANGED
|
@@ -5,23 +5,26 @@ 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
|
-
from jaclang.compiler.passes.transform import Alert
|
|
19
17
|
from jaclang.langserve.utils import (
|
|
18
|
+
collect_all_symbols_in_scope,
|
|
20
19
|
collect_symbols,
|
|
21
20
|
create_range,
|
|
22
21
|
find_deepest_symbol_node_at_pos,
|
|
22
|
+
gen_diagnostics,
|
|
23
23
|
get_item_path,
|
|
24
24
|
get_mod_path,
|
|
25
|
+
locate_affected_token,
|
|
26
|
+
parse_symbol_path,
|
|
27
|
+
resolve_completion_symbol_table,
|
|
25
28
|
)
|
|
26
29
|
from jaclang.vendor.pygls import uris
|
|
27
30
|
from jaclang.vendor.pygls.server import LanguageServer
|
|
@@ -29,32 +32,17 @@ from jaclang.vendor.pygls.server import LanguageServer
|
|
|
29
32
|
import lsprotocol.types as lspt
|
|
30
33
|
|
|
31
34
|
|
|
32
|
-
class ALev(IntEnum):
|
|
33
|
-
"""Analysis Level successfully completed."""
|
|
34
|
-
|
|
35
|
-
QUICK = 1
|
|
36
|
-
DEEP = 2
|
|
37
|
-
TYPE = 3
|
|
38
|
-
|
|
39
|
-
|
|
40
35
|
class ModuleInfo:
|
|
41
36
|
"""Module IR and Stats."""
|
|
42
37
|
|
|
43
38
|
def __init__(
|
|
44
39
|
self,
|
|
45
40
|
ir: ast.Module,
|
|
46
|
-
|
|
47
|
-
warnings: list[Alert],
|
|
48
|
-
alev: ALev,
|
|
49
|
-
parent: Optional[ModuleInfo] = None,
|
|
41
|
+
impl_parent: Optional[ModuleInfo] = None,
|
|
50
42
|
) -> None:
|
|
51
43
|
"""Initialize module info."""
|
|
52
44
|
self.ir = ir
|
|
53
|
-
self.
|
|
54
|
-
self.warnings = warnings
|
|
55
|
-
self.alev = alev
|
|
56
|
-
self.parent: Optional[ModuleInfo] = parent
|
|
57
|
-
self.diagnostics = self.gen_diagnostics()
|
|
45
|
+
self.impl_parent: Optional[ModuleInfo] = impl_parent
|
|
58
46
|
self.sem_tokens: list[int] = self.gen_sem_tokens()
|
|
59
47
|
|
|
60
48
|
@property
|
|
@@ -62,57 +50,6 @@ class ModuleInfo:
|
|
|
62
50
|
"""Return uri."""
|
|
63
51
|
return uris.from_fs_path(self.ir.loc.mod_path)
|
|
64
52
|
|
|
65
|
-
def update_with(
|
|
66
|
-
self,
|
|
67
|
-
build: Pass,
|
|
68
|
-
alev: ALev,
|
|
69
|
-
refresh: bool = False,
|
|
70
|
-
mod_override: Optional[ast.Module] = None,
|
|
71
|
-
) -> None:
|
|
72
|
-
"""Update module info."""
|
|
73
|
-
target_mod = mod_override if mod_override else build.ir
|
|
74
|
-
if not isinstance(target_mod, ast.Module):
|
|
75
|
-
return
|
|
76
|
-
self.ir = target_mod # if alev > ALev.QUICK else self.ir
|
|
77
|
-
if refresh:
|
|
78
|
-
self.errors = build.errors_had
|
|
79
|
-
self.warnings = build.warnings_had
|
|
80
|
-
else:
|
|
81
|
-
self.errors += [
|
|
82
|
-
i
|
|
83
|
-
for i in build.errors_had
|
|
84
|
-
if i not in self.errors
|
|
85
|
-
if i.loc.mod_path == target_mod.loc.mod_path
|
|
86
|
-
]
|
|
87
|
-
self.warnings += [
|
|
88
|
-
i
|
|
89
|
-
for i in build.warnings_had
|
|
90
|
-
if i not in self.warnings
|
|
91
|
-
if i.loc.mod_path == target_mod.loc.mod_path
|
|
92
|
-
]
|
|
93
|
-
self.alev = alev
|
|
94
|
-
self.diagnostics = self.gen_diagnostics()
|
|
95
|
-
if self.alev == ALev.TYPE:
|
|
96
|
-
self.sem_tokens = self.gen_sem_tokens()
|
|
97
|
-
|
|
98
|
-
def gen_diagnostics(self) -> list[lspt.Diagnostic]:
|
|
99
|
-
"""Return diagnostics."""
|
|
100
|
-
return [
|
|
101
|
-
lspt.Diagnostic(
|
|
102
|
-
range=create_range(error.loc),
|
|
103
|
-
message=error.msg,
|
|
104
|
-
severity=lspt.DiagnosticSeverity.Error,
|
|
105
|
-
)
|
|
106
|
-
for error in self.errors
|
|
107
|
-
] + [
|
|
108
|
-
lspt.Diagnostic(
|
|
109
|
-
range=create_range(warning.loc),
|
|
110
|
-
message=warning.msg,
|
|
111
|
-
severity=lspt.DiagnosticSeverity.Warning,
|
|
112
|
-
)
|
|
113
|
-
for warning in self.warnings
|
|
114
|
-
]
|
|
115
|
-
|
|
116
53
|
def gen_sem_tokens(self) -> list[int]:
|
|
117
54
|
"""Return semantic tokens."""
|
|
118
55
|
tokens = []
|
|
@@ -134,6 +71,76 @@ class ModuleInfo:
|
|
|
134
71
|
prev_line, prev_col = line, col_start
|
|
135
72
|
return tokens
|
|
136
73
|
|
|
74
|
+
def update_sem_tokens(
|
|
75
|
+
self, content_changes: lspt.DidChangeTextDocumentParams
|
|
76
|
+
) -> list[int]:
|
|
77
|
+
"""Update semantic tokens on change."""
|
|
78
|
+
for change in [
|
|
79
|
+
x
|
|
80
|
+
for x in content_changes.content_changes
|
|
81
|
+
if isinstance(x, lspt.TextDocumentContentChangeEvent_Type1)
|
|
82
|
+
]:
|
|
83
|
+
change_start_line = change.range.start.line
|
|
84
|
+
change_start_char = change.range.start.character
|
|
85
|
+
change_end_line = change.range.end.line
|
|
86
|
+
change_end_char = change.range.end.character
|
|
87
|
+
|
|
88
|
+
line_delta = change.text.count("\n") - (change_end_line - change_start_line)
|
|
89
|
+
if line_delta == 0:
|
|
90
|
+
char_delta = len(change.text) - (change_end_char - change_start_char)
|
|
91
|
+
else:
|
|
92
|
+
last_newline_index = change.text.rfind("\n")
|
|
93
|
+
char_delta = (
|
|
94
|
+
len(change.text)
|
|
95
|
+
- last_newline_index
|
|
96
|
+
- 1
|
|
97
|
+
- change_end_char
|
|
98
|
+
+ change_start_char
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
changed_token_index = locate_affected_token(
|
|
102
|
+
self.sem_tokens,
|
|
103
|
+
change_start_line,
|
|
104
|
+
change_start_char,
|
|
105
|
+
change_end_line,
|
|
106
|
+
change_end_char,
|
|
107
|
+
)
|
|
108
|
+
if changed_token_index:
|
|
109
|
+
self.sem_tokens[changed_token_index + 2] = max(
|
|
110
|
+
1, self.sem_tokens[changed_token_index + 2] + char_delta
|
|
111
|
+
)
|
|
112
|
+
if (
|
|
113
|
+
len(self.sem_tokens) > changed_token_index + 5
|
|
114
|
+
and self.sem_tokens[changed_token_index + 5] == 0
|
|
115
|
+
):
|
|
116
|
+
next_token_index = changed_token_index + 5
|
|
117
|
+
self.sem_tokens[next_token_index + 1] = max(
|
|
118
|
+
0, self.sem_tokens[next_token_index + 1] + char_delta
|
|
119
|
+
)
|
|
120
|
+
return self.sem_tokens
|
|
121
|
+
|
|
122
|
+
current_token_index = 0
|
|
123
|
+
line_offset = 0
|
|
124
|
+
while current_token_index < len(self.sem_tokens):
|
|
125
|
+
token_line_number = self.sem_tokens[current_token_index] + line_offset
|
|
126
|
+
token_start_pos = self.sem_tokens[current_token_index + 1]
|
|
127
|
+
|
|
128
|
+
if token_line_number > change_start_line or (
|
|
129
|
+
token_line_number == change_start_line
|
|
130
|
+
and token_start_pos >= change_start_char
|
|
131
|
+
):
|
|
132
|
+
self.sem_tokens[current_token_index] += line_delta
|
|
133
|
+
if token_line_number == change_start_line:
|
|
134
|
+
self.sem_tokens[current_token_index + 1] += char_delta
|
|
135
|
+
if token_line_number > change_end_line or (
|
|
136
|
+
token_line_number == change_end_line
|
|
137
|
+
and token_start_pos >= change_end_char
|
|
138
|
+
):
|
|
139
|
+
break
|
|
140
|
+
line_offset += self.sem_tokens[current_token_index]
|
|
141
|
+
current_token_index += 5
|
|
142
|
+
return self.sem_tokens
|
|
143
|
+
|
|
137
144
|
|
|
138
145
|
class JacLangServer(LanguageServer):
|
|
139
146
|
"""Class for managing workspace."""
|
|
@@ -143,68 +150,26 @@ class JacLangServer(LanguageServer):
|
|
|
143
150
|
super().__init__("jac-lsp", "v0.1")
|
|
144
151
|
self.modules: dict[str, ModuleInfo] = {}
|
|
145
152
|
self.executor = ThreadPoolExecutor()
|
|
146
|
-
|
|
147
|
-
async def push_diagnostics(self, file_path: str) -> None:
|
|
148
|
-
"""Push diagnostics for a file."""
|
|
149
|
-
if file_path in self.modules:
|
|
150
|
-
self.publish_diagnostics(
|
|
151
|
-
file_path,
|
|
152
|
-
self.modules[file_path].diagnostics,
|
|
153
|
-
)
|
|
154
|
-
|
|
155
|
-
def unwind_to_parent(self, file_path: str) -> str:
|
|
156
|
-
"""Unwind to parent."""
|
|
157
|
-
orig_file_path = file_path
|
|
158
|
-
if file_path in self.modules:
|
|
159
|
-
while cur := self.modules[file_path].parent:
|
|
160
|
-
file_path = cur.uri
|
|
161
|
-
if file_path == orig_file_path and (
|
|
162
|
-
discover := self.modules[file_path].ir.annexable_by
|
|
163
|
-
):
|
|
164
|
-
file_path = uris.from_fs_path(discover)
|
|
165
|
-
self.quick_check(file_path)
|
|
166
|
-
return file_path
|
|
153
|
+
self.tasks: dict[str, asyncio.Task] = {}
|
|
167
154
|
|
|
168
155
|
def update_modules(
|
|
169
|
-
self, file_path: str, build: Pass,
|
|
156
|
+
self, file_path: str, build: Pass, refresh: bool = False
|
|
170
157
|
) -> None:
|
|
171
158
|
"""Update modules."""
|
|
172
159
|
if not isinstance(build.ir, ast.Module):
|
|
173
160
|
self.log_error("Error with module build.")
|
|
174
161
|
return
|
|
175
|
-
|
|
176
|
-
self.modules[file_path].
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
ir=build.ir,
|
|
180
|
-
errors=[
|
|
181
|
-
i
|
|
182
|
-
for i in build.errors_had
|
|
183
|
-
if i.loc.mod_path == uris.to_fs_path(file_path)
|
|
184
|
-
],
|
|
185
|
-
warnings=[
|
|
186
|
-
i
|
|
187
|
-
for i in build.warnings_had
|
|
188
|
-
if i.loc.mod_path == uris.to_fs_path(file_path)
|
|
189
|
-
],
|
|
190
|
-
alev=alev,
|
|
191
|
-
)
|
|
162
|
+
keep_parent = (
|
|
163
|
+
self.modules[file_path].impl_parent if file_path in self.modules else None
|
|
164
|
+
)
|
|
165
|
+
self.modules[file_path] = ModuleInfo(ir=build.ir, impl_parent=keep_parent)
|
|
192
166
|
for p in build.ir.mod_deps.keys():
|
|
193
167
|
uri = uris.from_fs_path(p)
|
|
194
|
-
if
|
|
195
|
-
self.modules[uri].update_with(
|
|
196
|
-
build, alev, mod_override=build.ir.mod_deps[p], refresh=refresh
|
|
197
|
-
)
|
|
198
|
-
else:
|
|
168
|
+
if file_path != uri:
|
|
199
169
|
self.modules[uri] = ModuleInfo(
|
|
200
170
|
ir=build.ir.mod_deps[p],
|
|
201
|
-
|
|
202
|
-
warnings=[i for i in build.warnings_had if i.loc.mod_path == p],
|
|
203
|
-
alev=alev,
|
|
171
|
+
impl_parent=self.modules[file_path],
|
|
204
172
|
)
|
|
205
|
-
self.modules[uri].parent = (
|
|
206
|
-
self.modules[file_path] if file_path != uri else None
|
|
207
|
-
)
|
|
208
173
|
|
|
209
174
|
def quick_check(self, file_path: str) -> bool:
|
|
210
175
|
"""Rebuild a file."""
|
|
@@ -213,72 +178,107 @@ class JacLangServer(LanguageServer):
|
|
|
213
178
|
build = jac_str_to_pass(
|
|
214
179
|
jac_str=document.source, file_path=document.path, schedule=[]
|
|
215
180
|
)
|
|
181
|
+
self.publish_diagnostics(
|
|
182
|
+
file_path,
|
|
183
|
+
gen_diagnostics(file_path, build.errors_had, build.warnings_had),
|
|
184
|
+
)
|
|
185
|
+
return len(build.errors_had) == 0
|
|
216
186
|
except Exception as e:
|
|
217
187
|
self.log_error(f"Error during syntax check: {e}")
|
|
218
188
|
return False
|
|
219
|
-
self.update_modules(file_path, build, ALev.QUICK, refresh=True)
|
|
220
|
-
return len(self.modules[file_path].errors) == 0
|
|
221
189
|
|
|
222
|
-
def deep_check(self, file_path: str) -> bool:
|
|
190
|
+
def deep_check(self, file_path: str, annex_view: Optional[str] = None) -> bool:
|
|
223
191
|
"""Rebuild a file and its dependencies."""
|
|
224
|
-
if file_path not in self.modules:
|
|
225
|
-
self.quick_check(file_path)
|
|
226
192
|
try:
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
193
|
+
document = self.workspace.get_text_document(file_path)
|
|
194
|
+
if file_path in self.modules and (
|
|
195
|
+
parent := self.modules[file_path].impl_parent
|
|
196
|
+
):
|
|
197
|
+
return self.deep_check(
|
|
198
|
+
uris.from_fs_path(parent.ir.loc.mod_path), annex_view=file_path
|
|
199
|
+
)
|
|
200
|
+
build = jac_str_to_pass(
|
|
201
|
+
jac_str=document.source,
|
|
202
|
+
file_path=document.path,
|
|
203
|
+
schedule=py_code_gen_typed,
|
|
204
|
+
)
|
|
205
|
+
self.update_modules(file_path, build)
|
|
206
|
+
if discover := self.modules[file_path].ir.annexable_by:
|
|
207
|
+
return self.deep_check(
|
|
208
|
+
uris.from_fs_path(discover), annex_view=file_path
|
|
209
|
+
)
|
|
234
210
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
ir=self.modules[file_path].ir, schedule=type_checker_sched
|
|
211
|
+
self.publish_diagnostics(
|
|
212
|
+
file_path,
|
|
213
|
+
gen_diagnostics(
|
|
214
|
+
annex_view if annex_view else file_path,
|
|
215
|
+
build.errors_had,
|
|
216
|
+
build.warnings_had,
|
|
217
|
+
),
|
|
243
218
|
)
|
|
219
|
+
return len(build.errors_had) == 0
|
|
244
220
|
except Exception as e:
|
|
245
|
-
self.log_error(f"Error during
|
|
221
|
+
self.log_error(f"Error during deep check: {e}")
|
|
246
222
|
return False
|
|
247
|
-
self.update_modules(file_path, build, ALev.TYPE)
|
|
248
|
-
return len(self.modules[file_path].errors) == 0
|
|
249
223
|
|
|
250
|
-
async def
|
|
224
|
+
async def launch_quick_check(self, uri: str) -> None:
|
|
251
225
|
"""Analyze and publish diagnostics."""
|
|
252
|
-
|
|
253
|
-
success = await asyncio.get_event_loop().run_in_executor(
|
|
226
|
+
await asyncio.get_event_loop().run_in_executor(
|
|
254
227
|
self.executor, self.quick_check, uri
|
|
255
228
|
)
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
229
|
+
|
|
230
|
+
async def launch_deep_check(self, uri: str) -> None:
|
|
231
|
+
"""Analyze and publish diagnostics."""
|
|
232
|
+
|
|
233
|
+
async def run_in_executor(
|
|
234
|
+
func: Callable[[str, Optional[str]], bool],
|
|
235
|
+
file_path: str,
|
|
236
|
+
annex_view: Optional[str] = None,
|
|
237
|
+
) -> None:
|
|
238
|
+
loop = asyncio.get_event_loop()
|
|
239
|
+
await loop.run_in_executor(self.executor, func, file_path, annex_view)
|
|
240
|
+
|
|
241
|
+
if uri in self.tasks and not self.tasks[uri].done():
|
|
242
|
+
self.log_py(f"Canceling {uri} deep check...")
|
|
243
|
+
self.tasks[uri].cancel()
|
|
244
|
+
del self.tasks[uri]
|
|
245
|
+
self.log_py(f"Analyzing {uri}...")
|
|
246
|
+
task = asyncio.create_task(run_in_executor(self.deep_check, uri))
|
|
247
|
+
self.tasks[uri] = task
|
|
248
|
+
await task
|
|
267
249
|
|
|
268
250
|
def get_completion(
|
|
269
|
-
self, file_path: str, position: lspt.Position
|
|
251
|
+
self, file_path: str, position: lspt.Position, completion_trigger: Optional[str]
|
|
270
252
|
) -> lspt.CompletionList:
|
|
271
253
|
"""Return completion for a file."""
|
|
272
|
-
|
|
254
|
+
completion_items = []
|
|
273
255
|
document = self.workspace.get_text_document(file_path)
|
|
274
|
-
current_line = document.lines[position.line]
|
|
275
|
-
|
|
256
|
+
current_line = document.lines[position.line]
|
|
257
|
+
current_pos = position.character
|
|
258
|
+
current_symbol_path = parse_symbol_path(current_line, current_pos)
|
|
259
|
+
node_selected = find_deepest_symbol_node_at_pos(
|
|
260
|
+
self.modules[file_path].ir,
|
|
261
|
+
position.line,
|
|
262
|
+
position.character - 2,
|
|
263
|
+
)
|
|
276
264
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
265
|
+
mod_tab = (
|
|
266
|
+
self.modules[file_path].ir.sym_tab
|
|
267
|
+
if not node_selected
|
|
268
|
+
else node_selected.sym_tab
|
|
269
|
+
)
|
|
270
|
+
current_tab = self.modules[file_path].ir._sym_tab
|
|
271
|
+
current_symbol_table = mod_tab
|
|
272
|
+
if completion_trigger == ".":
|
|
273
|
+
completion_items = resolve_completion_symbol_table(
|
|
274
|
+
mod_tab, current_symbol_path, current_tab
|
|
275
|
+
)
|
|
276
|
+
else:
|
|
277
|
+
try: # noqa SIM105
|
|
278
|
+
completion_items = collect_all_symbols_in_scope(current_symbol_table)
|
|
279
|
+
except AttributeError:
|
|
280
|
+
pass
|
|
281
|
+
return lspt.CompletionList(is_incomplete=False, items=completion_items)
|
|
282
282
|
|
|
283
283
|
def rename_module(self, old_path: str, new_path: str) -> None:
|
|
284
284
|
"""Rename module."""
|
|
@@ -325,6 +325,8 @@ class JacLangServer(LanguageServer):
|
|
|
325
325
|
self, file_path: str, position: lspt.Position
|
|
326
326
|
) -> Optional[lspt.Hover]:
|
|
327
327
|
"""Return hover information for a file."""
|
|
328
|
+
if file_path not in self.modules:
|
|
329
|
+
return None
|
|
328
330
|
node_selected = find_deepest_symbol_node_at_pos(
|
|
329
331
|
self.modules[file_path].ir, position.line, position.character
|
|
330
332
|
)
|
|
@@ -371,6 +373,8 @@ class JacLangServer(LanguageServer):
|
|
|
371
373
|
self, file_path: str, position: lspt.Position
|
|
372
374
|
) -> Optional[lspt.Location]:
|
|
373
375
|
"""Return definition location for a file."""
|
|
376
|
+
if file_path not in self.modules:
|
|
377
|
+
return None
|
|
374
378
|
node_selected: Optional[ast.AstSymbolNode] = find_deepest_symbol_node_at_pos(
|
|
375
379
|
self.modules[file_path].ir, position.line, position.character
|
|
376
380
|
)
|
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,18 +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)
|
|
24
23
|
ls.lsp.send_request(lspt.WORKSPACE_SEMANTIC_TOKENS_REFRESH)
|
|
25
24
|
|
|
26
25
|
|
|
27
26
|
@server.feature(lspt.TEXT_DOCUMENT_DID_CHANGE)
|
|
28
|
-
@debounce(0.3)
|
|
29
27
|
async def did_change(
|
|
30
28
|
ls: JacLangServer, params: lspt.DidChangeTextDocumentParams
|
|
31
29
|
) -> None:
|
|
32
30
|
"""Check syntax on change."""
|
|
33
|
-
await ls.
|
|
34
|
-
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)
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
@server.feature(lspt.TEXT_DOCUMENT_FORMATTING)
|
|
@@ -86,11 +86,15 @@ def did_delete_files(ls: JacLangServer, params: lspt.DeleteFilesParams) -> None:
|
|
|
86
86
|
|
|
87
87
|
@server.feature(
|
|
88
88
|
lspt.TEXT_DOCUMENT_COMPLETION,
|
|
89
|
-
lspt.CompletionOptions(trigger_characters=[".", ":", ""]),
|
|
89
|
+
lspt.CompletionOptions(trigger_characters=[".", ":", "a-zA-Z0-9"]),
|
|
90
90
|
)
|
|
91
91
|
def completion(ls: JacLangServer, params: lspt.CompletionParams) -> lspt.CompletionList:
|
|
92
92
|
"""Provide completion."""
|
|
93
|
-
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
|
+
)
|
|
94
98
|
|
|
95
99
|
|
|
96
100
|
@server.feature(lspt.TEXT_DOCUMENT_HOVER, lspt.HoverOptions(work_done_progress=True))
|
|
@@ -134,6 +138,14 @@ def semantic_tokens_full(
|
|
|
134
138
|
ls: JacLangServer, params: lspt.SemanticTokensParams
|
|
135
139
|
) -> lspt.SemanticTokens:
|
|
136
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
|
|
137
149
|
return ls.get_semantic_tokens(params.text_document.uri)
|
|
138
150
|
|
|
139
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;
|