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
|
@@ -39,36 +39,6 @@ class TestJacLangServer(TestCase):
|
|
|
39
39
|
],
|
|
40
40
|
)
|
|
41
41
|
|
|
42
|
-
def test_syntax_diagnostics(self) -> None:
|
|
43
|
-
"""Test diagnostics."""
|
|
44
|
-
lsp = JacLangServer()
|
|
45
|
-
# Set up the workspace path to "fixtures/"
|
|
46
|
-
workspace_path = self.fixture_abs_path("")
|
|
47
|
-
workspace = Workspace(workspace_path, lsp)
|
|
48
|
-
lsp.lsp._workspace = workspace
|
|
49
|
-
circle_file = uris.from_fs_path(self.fixture_abs_path("circle_err.jac"))
|
|
50
|
-
lsp.quick_check(circle_file)
|
|
51
|
-
self.assertEqual(len(lsp.modules), 1)
|
|
52
|
-
self.assertEqual(lsp.modules[circle_file].diagnostics[0].range.start.line, 22)
|
|
53
|
-
|
|
54
|
-
def test_doesnt_run_if_syntax_error(self) -> None:
|
|
55
|
-
"""Test that the server doesn't run if there is a syntax error."""
|
|
56
|
-
lsp = JacLangServer()
|
|
57
|
-
# Set up the workspace path to "fixtures/"
|
|
58
|
-
workspace_path = self.fixture_abs_path("")
|
|
59
|
-
workspace = Workspace(workspace_path, lsp)
|
|
60
|
-
lsp.lsp._workspace = workspace
|
|
61
|
-
circle_file = uris.from_fs_path(self.fixture_abs_path("circle_err.jac"))
|
|
62
|
-
lsp.quick_check(circle_file)
|
|
63
|
-
self.assertEqual(len(lsp.modules), 1)
|
|
64
|
-
self.assertEqual(lsp.modules[circle_file].diagnostics[0].range.start.line, 22)
|
|
65
|
-
lsp.deep_check(circle_file)
|
|
66
|
-
# self.assertEqual(len(lsp.modules), 1)
|
|
67
|
-
self.assertEqual(lsp.modules[circle_file].diagnostics[0].range.start.line, 22)
|
|
68
|
-
lsp.type_check(circle_file)
|
|
69
|
-
# self.assertEqual(len(lsp.modules), 1)
|
|
70
|
-
self.assertEqual(lsp.modules[circle_file].diagnostics[0].range.start.line, 22)
|
|
71
|
-
|
|
72
42
|
def test_impl_stay_connected(self) -> None:
|
|
73
43
|
"""Test that the server doesn't run if there is a syntax error."""
|
|
74
44
|
lsp = JacLangServer()
|
|
@@ -80,15 +50,13 @@ class TestJacLangServer(TestCase):
|
|
|
80
50
|
circle_impl_file = uris.from_fs_path(
|
|
81
51
|
self.fixture_abs_path("circle_pure.impl.jac")
|
|
82
52
|
)
|
|
83
|
-
lsp.quick_check(circle_file)
|
|
84
53
|
lsp.deep_check(circle_file)
|
|
85
|
-
lsp.type_check(circle_file)
|
|
86
54
|
pos = lspt.Position(20, 8)
|
|
87
55
|
self.assertIn(
|
|
88
56
|
"Circle class inherits from Shape.",
|
|
89
57
|
lsp.get_hover_info(circle_file, pos).contents.value,
|
|
90
58
|
)
|
|
91
|
-
lsp.
|
|
59
|
+
lsp.deep_check(circle_impl_file)
|
|
92
60
|
pos = lspt.Position(8, 11)
|
|
93
61
|
self.assertIn(
|
|
94
62
|
"ability) calculate_area: float",
|
|
@@ -105,9 +73,7 @@ class TestJacLangServer(TestCase):
|
|
|
105
73
|
circle_impl_file = uris.from_fs_path(
|
|
106
74
|
self.fixture_abs_path("circle_pure.impl.jac")
|
|
107
75
|
)
|
|
108
|
-
lsp.quick_check(circle_impl_file)
|
|
109
76
|
lsp.deep_check(circle_impl_file)
|
|
110
|
-
lsp.type_check(circle_impl_file)
|
|
111
77
|
pos = lspt.Position(8, 11)
|
|
112
78
|
self.assertIn(
|
|
113
79
|
"ability) calculate_area: float",
|
|
@@ -122,9 +88,7 @@ class TestJacLangServer(TestCase):
|
|
|
122
88
|
workspace = Workspace(workspace_path, lsp)
|
|
123
89
|
lsp.lsp._workspace = workspace
|
|
124
90
|
target = uris.from_fs_path(self.examples_abs_path("guess_game/guess_game4.jac"))
|
|
125
|
-
lsp.quick_check(target)
|
|
126
91
|
lsp.deep_check(target)
|
|
127
|
-
lsp.type_check(target)
|
|
128
92
|
pos = lspt.Position(43, 18)
|
|
129
93
|
self.assertIn(
|
|
130
94
|
"attempts: int",
|
|
@@ -138,9 +102,7 @@ class TestJacLangServer(TestCase):
|
|
|
138
102
|
workspace = Workspace(workspace_path, lsp)
|
|
139
103
|
lsp.lsp._workspace = workspace
|
|
140
104
|
circle_file = uris.from_fs_path(self.fixture_abs_path("circle_pure.jac"))
|
|
141
|
-
lsp.quick_check(circle_file)
|
|
142
105
|
lsp.deep_check(circle_file)
|
|
143
|
-
lsp.type_check(circle_file)
|
|
144
106
|
self.assertEqual(8, len(lsp.get_document_symbols(circle_file)))
|
|
145
107
|
|
|
146
108
|
def test_go_to_definition(self) -> None:
|
|
@@ -150,9 +112,7 @@ class TestJacLangServer(TestCase):
|
|
|
150
112
|
workspace = Workspace(workspace_path, lsp)
|
|
151
113
|
lsp.lsp._workspace = workspace
|
|
152
114
|
circle_file = uris.from_fs_path(self.fixture_abs_path("circle_pure.jac"))
|
|
153
|
-
lsp.quick_check(circle_file)
|
|
154
115
|
lsp.deep_check(circle_file)
|
|
155
|
-
lsp.type_check(circle_file)
|
|
156
116
|
self.assertIn(
|
|
157
117
|
"fixtures/circle_pure.impl.jac:8:0-8:19",
|
|
158
118
|
str(lsp.get_definition(circle_file, lspt.Position(9, 16))),
|
|
@@ -171,14 +131,36 @@ class TestJacLangServer(TestCase):
|
|
|
171
131
|
guess_game_file = uris.from_fs_path(
|
|
172
132
|
self.examples_abs_path("guess_game/guess_game4.jac")
|
|
173
133
|
)
|
|
174
|
-
lsp.quick_check(guess_game_file)
|
|
175
134
|
lsp.deep_check(guess_game_file)
|
|
176
|
-
lsp.type_check(guess_game_file)
|
|
177
135
|
self.assertIn(
|
|
178
136
|
"guess_game4.jac:27:8-27:21",
|
|
179
137
|
str(lsp.get_definition(guess_game_file, lspt.Position(46, 45))),
|
|
180
138
|
)
|
|
181
139
|
|
|
140
|
+
def test_go_to_definition_method_manual_impl(self) -> None:
|
|
141
|
+
"""Test that the go to definition is correct."""
|
|
142
|
+
lsp = JacLangServer()
|
|
143
|
+
workspace_path = self.fixture_abs_path("")
|
|
144
|
+
workspace = Workspace(workspace_path, lsp)
|
|
145
|
+
lsp.lsp._workspace = workspace
|
|
146
|
+
decldef_file = uris.from_fs_path(
|
|
147
|
+
self.examples_abs_path("micro/decl_defs_impl.jac")
|
|
148
|
+
)
|
|
149
|
+
lsp.deep_check(decldef_file)
|
|
150
|
+
self.assertNotIn(
|
|
151
|
+
"decl_defs_main.jac:8:8-8:17",
|
|
152
|
+
str(lsp.get_definition(decldef_file, lspt.Position(2, 24))),
|
|
153
|
+
)
|
|
154
|
+
decldef_main_file = uris.from_fs_path(
|
|
155
|
+
self.examples_abs_path("micro/decl_defs_main.jac")
|
|
156
|
+
)
|
|
157
|
+
lsp.deep_check(decldef_main_file)
|
|
158
|
+
lsp.deep_check(decldef_file)
|
|
159
|
+
self.assertIn(
|
|
160
|
+
"decl_defs_main.jac:8:8-8:17",
|
|
161
|
+
str(lsp.get_definition(decldef_file, lspt.Position(2, 24))),
|
|
162
|
+
)
|
|
163
|
+
|
|
182
164
|
def test_test_annex(self) -> None:
|
|
183
165
|
"""Test that the server doesn't run if there is a syntax error."""
|
|
184
166
|
lsp = JacLangServer()
|
|
@@ -187,9 +169,7 @@ class TestJacLangServer(TestCase):
|
|
|
187
169
|
workspace = Workspace(workspace_path, lsp)
|
|
188
170
|
lsp.lsp._workspace = workspace
|
|
189
171
|
circle_file = uris.from_fs_path(self.fixture_abs_path("circle_pure.test.jac"))
|
|
190
|
-
lsp.quick_check(circle_file)
|
|
191
172
|
lsp.deep_check(circle_file)
|
|
192
|
-
lsp.type_check(circle_file)
|
|
193
173
|
pos = lspt.Position(13, 29)
|
|
194
174
|
self.assertIn(
|
|
195
175
|
"shape_type: circle_pure.ShapeType",
|
|
@@ -205,13 +185,11 @@ class TestJacLangServer(TestCase):
|
|
|
205
185
|
import_file = uris.from_fs_path(
|
|
206
186
|
self.fixture_abs_path("import_include_statements.jac")
|
|
207
187
|
)
|
|
208
|
-
lsp.quick_check(import_file)
|
|
209
188
|
lsp.deep_check(import_file)
|
|
210
|
-
lsp.type_check(import_file)
|
|
211
189
|
positions = [
|
|
212
190
|
(2, 16, "datetime.py:0:0-0:0"),
|
|
213
191
|
(3, 17, "base_module_structure.jac:0:0-0:0"),
|
|
214
|
-
(3,
|
|
192
|
+
(3, 87, "base_module_structure.jac:23:0-23:5"),
|
|
215
193
|
(5, 65, "py_import.py:12:0-20:5"),
|
|
216
194
|
(5, 35, "py_import.py:3:0-4:5"),
|
|
217
195
|
]
|
|
@@ -230,49 +208,84 @@ class TestJacLangServer(TestCase):
|
|
|
230
208
|
workspace = Workspace(workspace_path, lsp)
|
|
231
209
|
lsp.lsp._workspace = workspace
|
|
232
210
|
circle_file = uris.from_fs_path(self.fixture_abs_path("circle.jac"))
|
|
233
|
-
lsp.quick_check(circle_file)
|
|
234
211
|
lsp.deep_check(circle_file)
|
|
235
|
-
lsp.type_check(circle_file)
|
|
236
212
|
sem_list = lsp.get_semantic_tokens(circle_file).data
|
|
237
213
|
expected_counts = [
|
|
238
|
-
("<JacSemTokenType.VARIABLE: 8>, <JacSemTokenModifier.READONLY: 4>",
|
|
214
|
+
("<JacSemTokenType.VARIABLE: 8>, <JacSemTokenModifier.READONLY: 4>", 12),
|
|
239
215
|
(
|
|
240
216
|
"<JacSemTokenType.PROPERTY: 9>, <JacSemTokenModifier.DEFINITION: 2>,",
|
|
241
|
-
|
|
217
|
+
19,
|
|
242
218
|
),
|
|
243
219
|
(
|
|
244
220
|
"<JacSemTokenType.PARAMETER: 7>, <JacSemTokenModifier.DECLARATION: 1>,",
|
|
245
|
-
|
|
221
|
+
5,
|
|
246
222
|
),
|
|
247
223
|
(
|
|
248
224
|
"<JacSemTokenType.FUNCTION: 12>, <JacSemTokenModifier.DECLARATION: 1>,",
|
|
249
|
-
|
|
225
|
+
9,
|
|
250
226
|
),
|
|
251
|
-
("<JacSemTokenType.METHOD: 13>, <JacSemTokenModifier.DECLARATION: 1>",
|
|
252
|
-
("<JacSemTokenType.ENUM: 3>, <JacSemTokenModifier.DECLARATION: 1>,",
|
|
253
|
-
("<JacSemTokenType.CLASS: 2>, <JacSemTokenModifier.DECLARATION: ",
|
|
227
|
+
("<JacSemTokenType.METHOD: 13>, <JacSemTokenModifier.DECLARATION: 1>", 6),
|
|
228
|
+
("<JacSemTokenType.ENUM: 3>, <JacSemTokenModifier.DECLARATION: 1>,", 4),
|
|
229
|
+
("<JacSemTokenType.CLASS: 2>, <JacSemTokenModifier.DECLARATION: ", 12),
|
|
254
230
|
(
|
|
255
231
|
"<JacSemTokenType.NAMESPACE: 0>, <JacSemTokenModifier.DEFINITION: 2>,",
|
|
256
|
-
|
|
232
|
+
3,
|
|
257
233
|
),
|
|
258
|
-
("0, 0, 4,", 22),
|
|
259
|
-
("0, 0, 3,", 192),
|
|
260
|
-
("0, 0, 6, ", 65),
|
|
261
|
-
(" 0, 7, 3,", 3),
|
|
262
234
|
]
|
|
263
235
|
for token_type, expected_count in expected_counts:
|
|
264
236
|
self.assertEqual(str(sem_list).count(token_type), expected_count)
|
|
265
237
|
|
|
238
|
+
def test_completion(self) -> None:
|
|
239
|
+
"""Test that the completions are correct."""
|
|
240
|
+
lsp = JacLangServer()
|
|
241
|
+
workspace_path = self.fixture_abs_path("")
|
|
242
|
+
workspace = Workspace(workspace_path, lsp)
|
|
243
|
+
lsp.lsp._workspace = workspace
|
|
244
|
+
base_module_file = uris.from_fs_path(
|
|
245
|
+
self.fixture_abs_path("base_module_structure.jac")
|
|
246
|
+
)
|
|
247
|
+
lsp.deep_check(base_module_file)
|
|
248
|
+
test_cases = [
|
|
249
|
+
(lspt.Position(37, 16), ["get_color1", "color1", "point1"], 3),
|
|
250
|
+
(
|
|
251
|
+
lspt.Position(51, 12),
|
|
252
|
+
[
|
|
253
|
+
"get_color1",
|
|
254
|
+
"color1",
|
|
255
|
+
"point1",
|
|
256
|
+
"base_colorred",
|
|
257
|
+
"pointred",
|
|
258
|
+
"color2",
|
|
259
|
+
],
|
|
260
|
+
6,
|
|
261
|
+
),
|
|
262
|
+
(lspt.Position(52, 19), ["color22", "point22"], 2),
|
|
263
|
+
]
|
|
264
|
+
for position, expected_completions, expected_length in test_cases:
|
|
265
|
+
completions = lsp.get_completion(
|
|
266
|
+
base_module_file, position, completion_trigger="."
|
|
267
|
+
).items
|
|
268
|
+
for completion in expected_completions:
|
|
269
|
+
self.assertIn(completion, str(completions))
|
|
270
|
+
self.assertEqual(expected_length, len(completions))
|
|
271
|
+
|
|
272
|
+
if position == lspt.Position(47, 12):
|
|
273
|
+
self.assertEqual(
|
|
274
|
+
1, str(completions).count("kind=<CompletionItemKind.Function: 3>")
|
|
275
|
+
)
|
|
276
|
+
self.assertEqual(
|
|
277
|
+
4, str(completions).count("kind=<CompletionItemKind.Field: 5>")
|
|
278
|
+
)
|
|
279
|
+
|
|
266
280
|
def test_go_to_reference(self) -> None:
|
|
267
281
|
"""Test that the go to reference is correct."""
|
|
268
282
|
lsp = JacLangServer()
|
|
269
283
|
workspace_path = self.fixture_abs_path("")
|
|
270
284
|
workspace = Workspace(workspace_path, lsp)
|
|
271
285
|
lsp.lsp._workspace = workspace
|
|
286
|
+
|
|
272
287
|
circle_file = uris.from_fs_path(self.fixture_abs_path("circle.jac"))
|
|
273
|
-
lsp.quick_check(circle_file)
|
|
274
288
|
lsp.deep_check(circle_file)
|
|
275
|
-
lsp.type_check(circle_file)
|
|
276
289
|
test_cases = [
|
|
277
290
|
(47, 12, ["circle.jac:47:8-47:14", "69:8-69:14", "74:8-74:14"]),
|
|
278
291
|
(54, 66, ["54:62-54:76", "65:28-65:42"]),
|
jaclang/langserve/utils.py
CHANGED
|
@@ -4,14 +4,18 @@ import asyncio
|
|
|
4
4
|
import builtins
|
|
5
5
|
import importlib.util
|
|
6
6
|
import os
|
|
7
|
+
import re
|
|
7
8
|
import sys
|
|
8
9
|
from functools import wraps
|
|
9
10
|
from typing import Any, Awaitable, Callable, Coroutine, Optional, ParamSpec, TypeVar
|
|
10
11
|
|
|
11
12
|
import jaclang.compiler.absyntree as ast
|
|
12
13
|
from jaclang.compiler.codeloc import CodeLocInfo
|
|
14
|
+
from jaclang.compiler.constant import SymbolType
|
|
15
|
+
from jaclang.compiler.passes.transform import Alert
|
|
13
16
|
from jaclang.compiler.symtable import Symbol, SymbolTable
|
|
14
17
|
from jaclang.utils.helpers import import_target_to_relative_path
|
|
18
|
+
from jaclang.vendor.pygls import uris
|
|
15
19
|
|
|
16
20
|
import lsprotocol.types as lspt
|
|
17
21
|
|
|
@@ -19,6 +23,29 @@ T = TypeVar("T", bound=Callable[..., Coroutine[Any, Any, Any]])
|
|
|
19
23
|
P = ParamSpec("P")
|
|
20
24
|
|
|
21
25
|
|
|
26
|
+
def gen_diagnostics(
|
|
27
|
+
from_path: str, errors: list[Alert], warnings: list[Alert]
|
|
28
|
+
) -> list[lspt.Diagnostic]:
|
|
29
|
+
"""Return diagnostics."""
|
|
30
|
+
return [
|
|
31
|
+
lspt.Diagnostic(
|
|
32
|
+
range=create_range(error.loc),
|
|
33
|
+
message=error.msg,
|
|
34
|
+
severity=lspt.DiagnosticSeverity.Error,
|
|
35
|
+
)
|
|
36
|
+
for error in errors
|
|
37
|
+
if error.loc.mod_path == uris.to_fs_path(from_path)
|
|
38
|
+
] + [
|
|
39
|
+
lspt.Diagnostic(
|
|
40
|
+
range=create_range(warning.loc),
|
|
41
|
+
message=warning.msg,
|
|
42
|
+
severity=lspt.DiagnosticSeverity.Warning,
|
|
43
|
+
)
|
|
44
|
+
for warning in warnings
|
|
45
|
+
if warning.loc.mod_path == uris.to_fs_path(from_path)
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
|
|
22
49
|
def debounce(wait: float) -> Callable[[T], Callable[..., Awaitable[None]]]:
|
|
23
50
|
"""Debounce decorator for async functions."""
|
|
24
51
|
|
|
@@ -194,6 +221,49 @@ def kind_map(sub_tab: ast.AstNode) -> lspt.SymbolKind:
|
|
|
194
221
|
)
|
|
195
222
|
|
|
196
223
|
|
|
224
|
+
def label_map(sub_tab: SymbolType) -> lspt.CompletionItemKind:
|
|
225
|
+
"""Map the symbol node to an lspt.CompletionItemKind."""
|
|
226
|
+
return (
|
|
227
|
+
lspt.CompletionItemKind.Function
|
|
228
|
+
if sub_tab in [SymbolType.ABILITY, SymbolType.TEST]
|
|
229
|
+
else (
|
|
230
|
+
lspt.CompletionItemKind.Class
|
|
231
|
+
if sub_tab
|
|
232
|
+
in [
|
|
233
|
+
SymbolType.OBJECT_ARCH,
|
|
234
|
+
SymbolType.NODE_ARCH,
|
|
235
|
+
SymbolType.EDGE_ARCH,
|
|
236
|
+
SymbolType.WALKER_ARCH,
|
|
237
|
+
]
|
|
238
|
+
else (
|
|
239
|
+
lspt.CompletionItemKind.Module
|
|
240
|
+
if sub_tab == SymbolType.MODULE
|
|
241
|
+
else (
|
|
242
|
+
lspt.CompletionItemKind.Enum
|
|
243
|
+
if sub_tab == SymbolType.ENUM_ARCH
|
|
244
|
+
else (
|
|
245
|
+
lspt.CompletionItemKind.Field
|
|
246
|
+
if sub_tab == SymbolType.HAS_VAR
|
|
247
|
+
else (
|
|
248
|
+
lspt.CompletionItemKind.Method
|
|
249
|
+
if sub_tab == SymbolType.METHOD
|
|
250
|
+
else (
|
|
251
|
+
lspt.CompletionItemKind.EnumMember
|
|
252
|
+
if sub_tab == SymbolType.ENUM_MEMBER
|
|
253
|
+
else (
|
|
254
|
+
lspt.CompletionItemKind.Interface
|
|
255
|
+
if sub_tab == SymbolType.IMPL
|
|
256
|
+
else lspt.CompletionItemKind.Variable
|
|
257
|
+
)
|
|
258
|
+
)
|
|
259
|
+
)
|
|
260
|
+
)
|
|
261
|
+
)
|
|
262
|
+
)
|
|
263
|
+
)
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
|
|
197
267
|
def get_mod_path(mod_path: ast.ModulePath, name_node: ast.Name) -> str | None:
|
|
198
268
|
"""Get path for a module import name."""
|
|
199
269
|
ret_target = None
|
|
@@ -292,3 +362,199 @@ def get_definition_range(
|
|
|
292
362
|
return filename, (start_line - 1, end_line - 1)
|
|
293
363
|
|
|
294
364
|
return None
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def locate_affected_token(
|
|
368
|
+
tokens: list[int],
|
|
369
|
+
change_start_line: int,
|
|
370
|
+
change_start_char: int,
|
|
371
|
+
change_end_line: int,
|
|
372
|
+
change_end_char: int,
|
|
373
|
+
) -> Optional[int]:
|
|
374
|
+
"""Find in which token change is occurring."""
|
|
375
|
+
token_index = 0
|
|
376
|
+
current_line = 0
|
|
377
|
+
line_char_offset = 0
|
|
378
|
+
|
|
379
|
+
while token_index < len(tokens):
|
|
380
|
+
token_line_delta = tokens[token_index]
|
|
381
|
+
token_start_char = tokens[token_index + 1]
|
|
382
|
+
token_length = tokens[token_index + 2]
|
|
383
|
+
|
|
384
|
+
if token_line_delta > 0:
|
|
385
|
+
current_line += token_line_delta
|
|
386
|
+
line_char_offset = 0
|
|
387
|
+
token_abs_start_char = line_char_offset + token_start_char
|
|
388
|
+
token_abs_end_char = token_abs_start_char + token_length
|
|
389
|
+
if (
|
|
390
|
+
current_line == change_start_line == change_end_line
|
|
391
|
+
and token_abs_start_char <= change_start_char
|
|
392
|
+
and change_end_char <= token_abs_end_char
|
|
393
|
+
):
|
|
394
|
+
return token_index
|
|
395
|
+
if (
|
|
396
|
+
current_line == change_start_line
|
|
397
|
+
and token_abs_start_char <= change_start_char < token_abs_end_char
|
|
398
|
+
):
|
|
399
|
+
return token_index
|
|
400
|
+
if (
|
|
401
|
+
current_line == change_end_line
|
|
402
|
+
and token_abs_start_char < change_end_char <= token_abs_end_char
|
|
403
|
+
):
|
|
404
|
+
return token_index
|
|
405
|
+
|
|
406
|
+
line_char_offset += token_start_char
|
|
407
|
+
token_index += 5
|
|
408
|
+
return None
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def collect_all_symbols_in_scope(
|
|
412
|
+
sym_tab: SymbolTable, up_tree: bool = True
|
|
413
|
+
) -> list[lspt.CompletionItem]:
|
|
414
|
+
"""Return all symbols in scope."""
|
|
415
|
+
symbols = []
|
|
416
|
+
visited = set()
|
|
417
|
+
current_tab: Optional[SymbolTable] = sym_tab
|
|
418
|
+
|
|
419
|
+
while current_tab is not None and current_tab not in visited:
|
|
420
|
+
visited.add(current_tab)
|
|
421
|
+
for name, symbol in current_tab.tab.items():
|
|
422
|
+
if name not in dir(builtins):
|
|
423
|
+
symbols.append(
|
|
424
|
+
lspt.CompletionItem(label=name, kind=label_map(symbol.sym_type))
|
|
425
|
+
)
|
|
426
|
+
if not up_tree:
|
|
427
|
+
return symbols
|
|
428
|
+
current_tab = current_tab.parent if current_tab.parent != current_tab else None
|
|
429
|
+
return symbols
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def parse_symbol_path(text: str, dot_position: int) -> list[str]:
|
|
433
|
+
"""Parse text and return a list of symbols."""
|
|
434
|
+
text = text[:dot_position].strip()
|
|
435
|
+
pattern = re.compile(r"\b\w+\(\)?|\b\w+\b")
|
|
436
|
+
matches = pattern.findall(text)
|
|
437
|
+
if text.endswith("."):
|
|
438
|
+
matches.append("")
|
|
439
|
+
symbol_path = []
|
|
440
|
+
i = 0
|
|
441
|
+
while i < len(matches):
|
|
442
|
+
if matches[i].endswith("("):
|
|
443
|
+
i += 1
|
|
444
|
+
continue
|
|
445
|
+
elif "(" in matches[i]:
|
|
446
|
+
symbol_path.append(matches[i])
|
|
447
|
+
elif matches[i] == "":
|
|
448
|
+
pass
|
|
449
|
+
else:
|
|
450
|
+
symbol_path.append(matches[i])
|
|
451
|
+
i += 1
|
|
452
|
+
|
|
453
|
+
return symbol_path
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
def resolve_symbol_path(sym_name: str, node_tab: SymbolTable) -> str:
|
|
457
|
+
"""Resolve symbol path."""
|
|
458
|
+
visited = set()
|
|
459
|
+
current_tab: Optional[SymbolTable] = node_tab
|
|
460
|
+
|
|
461
|
+
while current_tab is not None and current_tab not in visited:
|
|
462
|
+
visited.add(current_tab)
|
|
463
|
+
for name, symbol in current_tab.tab.items():
|
|
464
|
+
if name not in dir(builtins) and name == sym_name:
|
|
465
|
+
path = symbol.defn[0]._sym_type
|
|
466
|
+
return path
|
|
467
|
+
current_tab = current_tab.parent if current_tab.parent != current_tab else None
|
|
468
|
+
return ""
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
def find_symbol_table(path: str, current_tab: Optional[SymbolTable]) -> SymbolTable:
|
|
472
|
+
"""Find symbol table."""
|
|
473
|
+
path = path.lstrip(".")
|
|
474
|
+
current_table = current_tab
|
|
475
|
+
if current_table:
|
|
476
|
+
for segment in path.split("."):
|
|
477
|
+
current_table = next(
|
|
478
|
+
(
|
|
479
|
+
child_table
|
|
480
|
+
for child_table in current_table.kid
|
|
481
|
+
if child_table.name == segment
|
|
482
|
+
),
|
|
483
|
+
current_table,
|
|
484
|
+
)
|
|
485
|
+
if current_table:
|
|
486
|
+
return current_table
|
|
487
|
+
raise ValueError(f"Symbol table not found for path {path}")
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def resolve_completion_symbol_table(
|
|
491
|
+
mod_tab: SymbolTable,
|
|
492
|
+
current_symbol_path: list[str],
|
|
493
|
+
current_tab: Optional[SymbolTable],
|
|
494
|
+
) -> list[lspt.CompletionItem]:
|
|
495
|
+
"""Resolve symbol table for completion items."""
|
|
496
|
+
current_symbol_table = mod_tab
|
|
497
|
+
for obj in current_symbol_path:
|
|
498
|
+
if obj == "self":
|
|
499
|
+
try:
|
|
500
|
+
try:
|
|
501
|
+
is_abilitydef = (
|
|
502
|
+
mod_tab.owner
|
|
503
|
+
if isinstance(mod_tab.owner, ast.AbilityDef)
|
|
504
|
+
else mod_tab.owner.parent_of_type(ast.AbilityDef)
|
|
505
|
+
)
|
|
506
|
+
archi_owner = (
|
|
507
|
+
(is_abilitydef.decl_link.parent_of_type(ast.Architype))
|
|
508
|
+
if is_abilitydef.decl_link
|
|
509
|
+
else None
|
|
510
|
+
)
|
|
511
|
+
current_symbol_table = (
|
|
512
|
+
archi_owner._sym_tab
|
|
513
|
+
if archi_owner and archi_owner._sym_tab
|
|
514
|
+
else mod_tab
|
|
515
|
+
)
|
|
516
|
+
continue
|
|
517
|
+
|
|
518
|
+
except ValueError:
|
|
519
|
+
pass
|
|
520
|
+
archi_owner = mod_tab.owner.parent_of_type(ast.Architype)
|
|
521
|
+
current_symbol_table = (
|
|
522
|
+
archi_owner._sym_tab
|
|
523
|
+
if archi_owner and archi_owner._sym_tab
|
|
524
|
+
else mod_tab
|
|
525
|
+
)
|
|
526
|
+
except ValueError:
|
|
527
|
+
pass
|
|
528
|
+
else:
|
|
529
|
+
path: str = resolve_symbol_path(obj, current_symbol_table)
|
|
530
|
+
if path:
|
|
531
|
+
current_symbol_table = find_symbol_table(path, current_tab)
|
|
532
|
+
else:
|
|
533
|
+
if (
|
|
534
|
+
isinstance(current_symbol_table.owner, ast.Architype)
|
|
535
|
+
and current_symbol_table.owner.base_classes
|
|
536
|
+
):
|
|
537
|
+
for base_name in current_symbol_table.owner.base_classes.items:
|
|
538
|
+
if isinstance(base_name, ast.Name) and base_name.sym:
|
|
539
|
+
path = base_name.sym.sym_dotted_name + "." + obj
|
|
540
|
+
current_symbol_table = find_symbol_table(path, current_tab)
|
|
541
|
+
if (
|
|
542
|
+
isinstance(current_symbol_table.owner, ast.Architype)
|
|
543
|
+
and current_symbol_table.owner.base_classes
|
|
544
|
+
):
|
|
545
|
+
base = []
|
|
546
|
+
for base_name in current_symbol_table.owner.base_classes.items:
|
|
547
|
+
if isinstance(base_name, ast.Name) and base_name.sym:
|
|
548
|
+
base.append(base_name.sym.sym_dotted_name)
|
|
549
|
+
for base_ in base:
|
|
550
|
+
completion_items = collect_all_symbols_in_scope(
|
|
551
|
+
find_symbol_table(base_, current_tab),
|
|
552
|
+
up_tree=False,
|
|
553
|
+
)
|
|
554
|
+
else:
|
|
555
|
+
completion_items = []
|
|
556
|
+
|
|
557
|
+
completion_items.extend(
|
|
558
|
+
collect_all_symbols_in_scope(current_symbol_table, up_tree=False)
|
|
559
|
+
)
|
|
560
|
+
return completion_items
|
jaclang/plugin/default.py
CHANGED
|
@@ -213,8 +213,8 @@ class JacFeatureDefaults:
|
|
|
213
213
|
override_name: Optional[str],
|
|
214
214
|
mod_bundle: Optional[Module | str],
|
|
215
215
|
lng: Optional[str],
|
|
216
|
-
items: Optional[dict[str, Union[str,
|
|
217
|
-
) ->
|
|
216
|
+
items: Optional[dict[str, Union[str, Optional[str]]]],
|
|
217
|
+
) -> tuple[types.ModuleType, ...]:
|
|
218
218
|
"""Core Import Process."""
|
|
219
219
|
result = jac_importer(
|
|
220
220
|
target=target,
|
|
@@ -260,6 +260,8 @@ class JacFeatureDefaults:
|
|
|
260
260
|
base, mod_name = os.path.split(filepath)
|
|
261
261
|
base = base if base else "./"
|
|
262
262
|
mod_name = mod_name[:-4]
|
|
263
|
+
if mod_name.endswith(".test"):
|
|
264
|
+
mod_name = mod_name[:-5]
|
|
263
265
|
JacTestCheck.reset()
|
|
264
266
|
Jac.jac_import(target=mod_name, base_path=base)
|
|
265
267
|
JacTestCheck.run_test(xit, maxfail, verbose)
|
jaclang/plugin/feature.py
CHANGED
|
@@ -104,8 +104,8 @@ class JacFeature:
|
|
|
104
104
|
override_name: Optional[str] = None,
|
|
105
105
|
mod_bundle: Optional[Module | str] = None,
|
|
106
106
|
lng: Optional[str] = "jac",
|
|
107
|
-
items: Optional[dict[str, Union[str,
|
|
108
|
-
) ->
|
|
107
|
+
items: Optional[dict[str, Union[str, Optional[str]]]] = None,
|
|
108
|
+
) -> tuple[types.ModuleType, ...]:
|
|
109
109
|
"""Core Import Process."""
|
|
110
110
|
return pm.hook.jac_import(
|
|
111
111
|
target=target,
|
jaclang/plugin/spec.py
CHANGED
|
@@ -101,8 +101,8 @@ class JacFeatureSpec:
|
|
|
101
101
|
override_name: Optional[str],
|
|
102
102
|
mod_bundle: Optional[Module | str],
|
|
103
103
|
lng: Optional[str],
|
|
104
|
-
items: Optional[dict[str, Union[str,
|
|
105
|
-
) ->
|
|
104
|
+
items: Optional[dict[str, Union[str, Optional[str]]]],
|
|
105
|
+
) -> tuple[types.ModuleType, ...]:
|
|
106
106
|
"""Core Import Process."""
|
|
107
107
|
raise NotImplementedError
|
|
108
108
|
|
jaclang/tests/test_cli.py
CHANGED
|
@@ -37,17 +37,16 @@ class JacCliTests(TestCase):
|
|
|
37
37
|
sys.stdout = captured_output
|
|
38
38
|
sys.stderr = captured_output
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
# print(f"Error: {e}")
|
|
40
|
+
try:
|
|
41
|
+
cli.enter(self.fixture_abs_path("err2.jac"), entrypoint="speak", args=[]) # type: ignore
|
|
42
|
+
except Exception as e:
|
|
43
|
+
print(f"Error: {e}")
|
|
45
44
|
|
|
46
45
|
sys.stdout = sys.__stdout__
|
|
47
46
|
sys.stderr = sys.__stderr__
|
|
48
47
|
stdout_value = captured_output.getvalue()
|
|
49
48
|
# print(stdout_value)
|
|
50
|
-
self.assertIn("
|
|
49
|
+
self.assertIn("Syntax Error", stdout_value)
|
|
51
50
|
|
|
52
51
|
def test_jac_ast_tool_pass_template(self) -> None:
|
|
53
52
|
"""Basic test for pass."""
|
|
@@ -126,6 +125,7 @@ class JacCliTests(TestCase):
|
|
|
126
125
|
cli.run(f"{self.fixture_abs_path('needs_import.jir')}")
|
|
127
126
|
sys.stdout = sys.__stdout__
|
|
128
127
|
stdout_value = captured_output.getvalue()
|
|
128
|
+
print(stdout_value)
|
|
129
129
|
self.assertIn("Errors: 0, Warnings: 0", stdout_value)
|
|
130
130
|
self.assertIn("<module 'pyfunc' from", stdout_value)
|
|
131
131
|
|
jaclang/tests/test_language.py
CHANGED
|
@@ -840,3 +840,12 @@ class JacLanguageTests(TestCase):
|
|
|
840
840
|
sys.stdout = sys.__stdout__
|
|
841
841
|
stdout_value = captured_output.getvalue()
|
|
842
842
|
self.assertIn("[x()]", stdout_value)
|
|
843
|
+
|
|
844
|
+
def test_blank_with_entry(self) -> None:
|
|
845
|
+
"""Test importing python."""
|
|
846
|
+
captured_output = io.StringIO()
|
|
847
|
+
sys.stdout = captured_output
|
|
848
|
+
jac_import("blankwithentry", base_path=self.fixture_abs_path("./"))
|
|
849
|
+
sys.stdout = sys.__stdout__
|
|
850
|
+
stdout_value = captured_output.getvalue()
|
|
851
|
+
self.assertIn("i work", stdout_value)
|