jaclang 0.7.14__py3-none-any.whl → 0.7.16__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 +15 -10
- jaclang/cli/cmdreg.py +9 -12
- jaclang/compiler/__init__.py +19 -53
- jaclang/compiler/absyntree.py +86 -13
- jaclang/compiler/jac.lark +4 -3
- jaclang/compiler/parser.py +31 -23
- jaclang/compiler/passes/ir_pass.py +4 -13
- jaclang/compiler/passes/main/access_modifier_pass.py +1 -1
- jaclang/compiler/passes/main/fuse_typeinfo_pass.py +4 -5
- jaclang/compiler/passes/main/import_pass.py +18 -23
- jaclang/compiler/passes/main/pyast_gen_pass.py +307 -559
- jaclang/compiler/passes/main/pyast_load_pass.py +32 -6
- jaclang/compiler/passes/main/registry_pass.py +37 -3
- jaclang/compiler/passes/main/sym_tab_build_pass.py +1 -1
- jaclang/compiler/passes/main/tests/__init__.py +1 -1
- jaclang/compiler/passes/main/type_check_pass.py +7 -0
- jaclang/compiler/passes/tool/jac_formatter_pass.py +124 -86
- jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +0 -1
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/architype_test.jac +13 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/comment_alignment.jac +11 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/comments.jac +13 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/decorator_stack.jac +37 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/esc_keywords.jac +5 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/long_names.jac +19 -0
- jaclang/compiler/passes/tool/tests/fixtures/general_format_checks/triple_quoted_string.jac +6 -0
- jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +11 -0
- jaclang/compiler/passes/tool/tests/test_unparse_validate.py +33 -39
- jaclang/compiler/passes/transform.py +4 -0
- jaclang/compiler/semtable.py +31 -7
- jaclang/compiler/tests/test_importer.py +12 -5
- jaclang/langserve/engine.py +65 -118
- jaclang/langserve/sem_manager.py +379 -0
- jaclang/langserve/server.py +8 -10
- jaclang/langserve/tests/fixtures/base_module_structure.jac +27 -6
- jaclang/langserve/tests/fixtures/circle.jac +3 -3
- jaclang/langserve/tests/fixtures/circle_err.jac +3 -3
- jaclang/langserve/tests/fixtures/circle_pure.test.jac +3 -3
- jaclang/langserve/tests/fixtures/import_include_statements.jac +1 -1
- jaclang/langserve/tests/test_sem_tokens.py +277 -0
- jaclang/langserve/tests/test_server.py +72 -16
- jaclang/langserve/utils.py +163 -96
- jaclang/plugin/builtin.py +1 -1
- jaclang/plugin/default.py +212 -24
- jaclang/plugin/feature.py +59 -11
- jaclang/plugin/spec.py +58 -6
- jaclang/{core → runtimelib}/architype.py +1 -1
- jaclang/{core → runtimelib}/context.py +8 -1
- jaclang/runtimelib/importer.py +361 -0
- jaclang/runtimelib/machine.py +94 -0
- jaclang/{core → runtimelib}/utils.py +13 -5
- jaclang/settings.py +4 -1
- jaclang/tests/fixtures/abc.jac +3 -3
- jaclang/tests/fixtures/byllmissue.jac +1 -5
- jaclang/tests/fixtures/chandra_bugs2.jac +11 -10
- jaclang/tests/fixtures/cls_method.jac +41 -0
- jaclang/tests/fixtures/dblhello.jac +6 -0
- jaclang/tests/fixtures/deep/one_lev.jac +3 -3
- jaclang/tests/fixtures/deep/one_lev_dup.jac +2 -3
- jaclang/tests/fixtures/deep_import_mods.jac +13 -0
- jaclang/tests/fixtures/err.impl.jac +3 -0
- jaclang/tests/fixtures/err.jac +4 -2
- jaclang/tests/fixtures/err.test.jac +3 -0
- jaclang/tests/fixtures/err_runtime.jac +15 -0
- jaclang/tests/fixtures/game1.jac +1 -1
- jaclang/tests/fixtures/hello.jac +4 -0
- jaclang/tests/fixtures/impl_grab.impl.jac +2 -1
- jaclang/tests/fixtures/impl_grab.jac +4 -1
- jaclang/tests/fixtures/jp_importer_auto.jac +14 -0
- jaclang/tests/fixtures/maxfail_run_test.jac +4 -4
- jaclang/tests/fixtures/needs_import.jac +2 -2
- jaclang/tests/fixtures/pyfunc_2.py +3 -0
- jaclang/tests/fixtures/registry.jac +9 -0
- jaclang/tests/fixtures/run_test.jac +4 -4
- jaclang/tests/fixtures/semstr.jac +1 -4
- jaclang/tests/fixtures/simple_archs.jac +1 -1
- jaclang/tests/test_cli.py +65 -2
- jaclang/tests/test_language.py +74 -7
- jaclang/tests/test_reference.py +6 -0
- jaclang/utils/helpers.py +45 -21
- jaclang/utils/test.py +9 -0
- jaclang/utils/treeprinter.py +0 -4
- {jaclang-0.7.14.dist-info → jaclang-0.7.16.dist-info}/METADATA +3 -2
- {jaclang-0.7.14.dist-info → jaclang-0.7.16.dist-info}/RECORD +89 -74
- jaclang/core/importer.py +0 -344
- jaclang/tests/fixtures/aott_raise.jac +0 -25
- jaclang/tests/fixtures/package_import.jac +0 -6
- /jaclang/{core → runtimelib}/__init__.py +0 -0
- /jaclang/{core → runtimelib}/constructs.py +0 -0
- /jaclang/{core → runtimelib}/memory.py +0 -0
- /jaclang/{core → runtimelib}/test.py +0 -0
- {jaclang-0.7.14.dist-info → jaclang-0.7.16.dist-info}/WHEEL +0 -0
- {jaclang-0.7.14.dist-info → jaclang-0.7.16.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
with entry {
|
|
2
|
+
long_variable_name_1 = another_long_variable_name_2
|
|
3
|
+
= yet_another_long_variable_name_3
|
|
4
|
+
= still_another_long_variable_name_4
|
|
5
|
+
= yet_still_another_long_variable_name_5
|
|
6
|
+
= one_more_long_variable_name_6
|
|
7
|
+
= and_another_long_variable_name_7
|
|
8
|
+
= final_long_variable_name_8
|
|
9
|
+
=1.5;
|
|
10
|
+
result = long_variable_name_1
|
|
11
|
+
+ another_long_variable_name_2
|
|
12
|
+
* yet_another_long_variable_name_3
|
|
13
|
+
- still_another_long_variable_name_4
|
|
14
|
+
/ yet_still_another_long_variable_name_5
|
|
15
|
+
+ one_more_long_variable_name_6
|
|
16
|
+
* and_another_long_variable_name_7
|
|
17
|
+
- final_long_variable_name_8;
|
|
18
|
+
print("The result of the ugly calculation is:", result);
|
|
19
|
+
}
|
|
@@ -102,6 +102,17 @@ class JacFormatPassTests(TestCaseMicroSuite, AstSyncTestMixin):
|
|
|
102
102
|
file_path = os.path.join(fixtures_dir, file_name)
|
|
103
103
|
self.compare_files(file_path)
|
|
104
104
|
|
|
105
|
+
def test_general_format_fixtures(self) -> None:
|
|
106
|
+
"""Tests if files in the general fixtures directory do not change after being formatted."""
|
|
107
|
+
fixtures_dir = os.path.join(self.fixture_abs_path(""), "general_format_checks")
|
|
108
|
+
fixture_files = os.listdir(fixtures_dir)
|
|
109
|
+
for file_name in fixture_files:
|
|
110
|
+
if file_name == "__jac_gen__":
|
|
111
|
+
continue
|
|
112
|
+
with self.subTest(file=file_name):
|
|
113
|
+
file_path = os.path.join(fixtures_dir, file_name)
|
|
114
|
+
self.compare_files(file_path)
|
|
115
|
+
|
|
105
116
|
def micro_suite_test(self, filename: str) -> None:
|
|
106
117
|
"""Parse micro jac file."""
|
|
107
118
|
code_gen_pure = jac_file_to_pass(
|
|
@@ -33,47 +33,41 @@ class JacUnparseTests(TestCaseMicroSuite, AstSyncTestMixin):
|
|
|
33
33
|
|
|
34
34
|
def micro_suite_test(self, filename: str) -> None:
|
|
35
35
|
"""Parse micro jac file."""
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
code_gen_pure = jac_file_to_pass(
|
|
37
|
+
self.fixture_abs_path(filename),
|
|
38
|
+
target=PyastGenPass,
|
|
39
|
+
schedule=without_format,
|
|
40
|
+
)
|
|
41
|
+
before = ast3.dump(code_gen_pure.ir.gen.py_ast[0], indent=2)
|
|
42
|
+
x = code_gen_pure.ir.unparse()
|
|
43
|
+
# print(x)
|
|
44
|
+
# print(f"Testing {code_gen_pure.ir.name}")
|
|
45
|
+
# print(code_gen_pure.ir.pp())
|
|
46
|
+
code_gen_jac = jac_str_to_pass(
|
|
47
|
+
jac_str=x,
|
|
48
|
+
file_path=filename,
|
|
49
|
+
target=PyastGenPass,
|
|
50
|
+
schedule=without_format,
|
|
51
|
+
)
|
|
52
|
+
after = ast3.dump(code_gen_jac.ir.gen.py_ast[0], indent=2)
|
|
53
|
+
if "circle_clean_tests.jac" in filename:
|
|
54
|
+
self.assertEqual(
|
|
55
|
+
len(
|
|
56
|
+
[
|
|
57
|
+
i
|
|
58
|
+
for i in unified_diff(
|
|
59
|
+
before.splitlines(), after.splitlines(), n=0
|
|
60
|
+
)
|
|
61
|
+
if "test" not in i
|
|
62
|
+
]
|
|
63
|
+
),
|
|
64
|
+
5,
|
|
41
65
|
)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
# print(code_gen_pure.ir.pp())
|
|
47
|
-
code_gen_jac = jac_str_to_pass(
|
|
48
|
-
jac_str=x,
|
|
49
|
-
file_path=filename,
|
|
50
|
-
target=PyastGenPass,
|
|
51
|
-
schedule=without_format,
|
|
66
|
+
else:
|
|
67
|
+
self.assertEqual(
|
|
68
|
+
len("\n".join(unified_diff(before.splitlines(), after.splitlines()))),
|
|
69
|
+
0,
|
|
52
70
|
)
|
|
53
|
-
after = ast3.dump(code_gen_jac.ir.gen.py_ast[0], indent=2)
|
|
54
|
-
if "circle_clean_tests.jac" in filename:
|
|
55
|
-
self.assertEqual(
|
|
56
|
-
len(
|
|
57
|
-
[
|
|
58
|
-
i
|
|
59
|
-
for i in unified_diff(
|
|
60
|
-
before.splitlines(), after.splitlines(), n=0
|
|
61
|
-
)
|
|
62
|
-
if "test" not in i
|
|
63
|
-
]
|
|
64
|
-
),
|
|
65
|
-
5,
|
|
66
|
-
)
|
|
67
|
-
else:
|
|
68
|
-
self.assertEqual(
|
|
69
|
-
len(
|
|
70
|
-
"\n".join(unified_diff(before.splitlines(), after.splitlines()))
|
|
71
|
-
),
|
|
72
|
-
0,
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
except Exception as e:
|
|
76
|
-
raise e
|
|
77
71
|
|
|
78
72
|
|
|
79
73
|
JacUnparseTests.self_attach_micro_tests()
|
jaclang/compiler/semtable.py
CHANGED
|
@@ -6,14 +6,24 @@ semantic information.
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
-
from typing import Optional
|
|
9
|
+
from typing import Optional, TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
import jaclang.compiler.absyntree as ast
|
|
10
13
|
|
|
11
14
|
|
|
12
15
|
class SemInfo:
|
|
13
16
|
"""Semantic information class."""
|
|
14
17
|
|
|
15
|
-
def __init__(
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
node: ast.AstNode,
|
|
21
|
+
name: str,
|
|
22
|
+
type: Optional[str] = None,
|
|
23
|
+
semstr: str = "",
|
|
24
|
+
) -> None:
|
|
16
25
|
"""Initialize the class."""
|
|
26
|
+
self.node = node
|
|
17
27
|
self.name = name
|
|
18
28
|
self.type = type
|
|
19
29
|
self.semstr = semstr
|
|
@@ -22,6 +32,17 @@ class SemInfo:
|
|
|
22
32
|
"""Return the string representation of the class."""
|
|
23
33
|
return f"{self.semstr} ({self.type}) ({self.name})"
|
|
24
34
|
|
|
35
|
+
def get_children(
|
|
36
|
+
self, sem_registry: SemRegistry, filter: Optional[type[ast.AstNode]] = None
|
|
37
|
+
) -> list[SemInfo]:
|
|
38
|
+
"""Get the children of the SemInfo."""
|
|
39
|
+
scope, _ = sem_registry.lookup(name=self.name)
|
|
40
|
+
self_scope = str(scope) + f".{self.name}({self.type})"
|
|
41
|
+
_, children = sem_registry.lookup(scope=SemScope.get_scope_from_str(self_scope))
|
|
42
|
+
if filter and children and isinstance(children, list):
|
|
43
|
+
return [i for i in children if isinstance(i.node, filter)]
|
|
44
|
+
return children if children and isinstance(children, list) else []
|
|
45
|
+
|
|
25
46
|
|
|
26
47
|
class SemScope:
|
|
27
48
|
"""Scope class."""
|
|
@@ -38,7 +59,8 @@ class SemScope:
|
|
|
38
59
|
"""Return the string representation of the class."""
|
|
39
60
|
if self.parent:
|
|
40
61
|
return f"{self.parent}.{self.scope}({self.type})"
|
|
41
|
-
|
|
62
|
+
else:
|
|
63
|
+
return f"{self.scope}({self.type})"
|
|
42
64
|
|
|
43
65
|
def __repr__(self) -> str:
|
|
44
66
|
"""Return the string representation of the class."""
|
|
@@ -57,7 +79,7 @@ class SemScope:
|
|
|
57
79
|
|
|
58
80
|
@property
|
|
59
81
|
def as_type_str(self) -> Optional[str]:
|
|
60
|
-
"""Return the type string representation of the
|
|
82
|
+
"""Return the type string representation of the SemScope."""
|
|
61
83
|
if self.type not in ["class", "node", "obj"]:
|
|
62
84
|
return None
|
|
63
85
|
type_str = self.scope
|
|
@@ -127,9 +149,11 @@ class SemRegistry:
|
|
|
127
149
|
break
|
|
128
150
|
return i
|
|
129
151
|
|
|
130
|
-
def pp(self) ->
|
|
152
|
+
def pp(self) -> str:
|
|
131
153
|
"""Pretty print the registry."""
|
|
154
|
+
ret_str = ""
|
|
132
155
|
for k, v in self.registry.items():
|
|
133
|
-
|
|
156
|
+
ret_str += f"{k}\n"
|
|
134
157
|
for i in v:
|
|
135
|
-
|
|
158
|
+
ret_str += f" {i.name} {i.type} {i.semstr}\n"
|
|
159
|
+
return ret_str
|
|
@@ -5,6 +5,7 @@ import sys
|
|
|
5
5
|
|
|
6
6
|
from jaclang import jac_import
|
|
7
7
|
from jaclang.cli import cli
|
|
8
|
+
from jaclang.plugin.feature import JacFeature as Jac
|
|
8
9
|
from jaclang.utils.test import TestCase
|
|
9
10
|
|
|
10
11
|
|
|
@@ -17,13 +18,15 @@ class TestLoader(TestCase):
|
|
|
17
18
|
|
|
18
19
|
def test_import_basic_python(self) -> None:
|
|
19
20
|
"""Test basic self loading."""
|
|
21
|
+
Jac.context().init_memory(base_path=self.fixture_abs_path(__file__))
|
|
20
22
|
(h,) = jac_import("fixtures.hello_world", base_path=__file__)
|
|
21
23
|
self.assertEqual(h.hello(), "Hello World!") # type: ignore
|
|
22
24
|
|
|
23
25
|
def test_modules_correct(self) -> None:
|
|
24
26
|
"""Test basic self loading."""
|
|
27
|
+
Jac.context().init_memory(base_path=self.fixture_abs_path(__file__))
|
|
25
28
|
jac_import("fixtures.hello_world", base_path=__file__)
|
|
26
|
-
self.assertIn("module 'hello_world'", str(sys.modules))
|
|
29
|
+
self.assertIn("module 'fixtures.hello_world'", str(sys.modules))
|
|
27
30
|
self.assertIn("/tests/fixtures/hello_world.jac", str(sys.modules))
|
|
28
31
|
|
|
29
32
|
def test_jac_py_import(self) -> None:
|
|
@@ -39,11 +42,15 @@ class TestLoader(TestCase):
|
|
|
39
42
|
stdout_value,
|
|
40
43
|
)
|
|
41
44
|
|
|
42
|
-
def
|
|
43
|
-
"""
|
|
45
|
+
def test_jac_py_import_auto(self) -> None:
|
|
46
|
+
"""Basic test for pass."""
|
|
44
47
|
captured_output = io.StringIO()
|
|
45
48
|
sys.stdout = captured_output
|
|
46
|
-
cli.run(self.fixture_abs_path("../../../tests/fixtures/
|
|
49
|
+
cli.run(self.fixture_abs_path("../../../tests/fixtures/jp_importer_auto.jac"))
|
|
47
50
|
sys.stdout = sys.__stdout__
|
|
48
51
|
stdout_value = captured_output.getvalue()
|
|
49
|
-
self.
|
|
52
|
+
self.assertIn("Hello World!", stdout_value)
|
|
53
|
+
self.assertIn(
|
|
54
|
+
"{SomeObj(a=10): 'check'} [MyObj(apple=5, banana=7), MyObj(apple=5, banana=7)]",
|
|
55
|
+
stdout_value,
|
|
56
|
+
)
|
jaclang/langserve/engine.py
CHANGED
|
@@ -4,25 +4,26 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import logging
|
|
7
|
+
import time
|
|
7
8
|
from concurrent.futures import ThreadPoolExecutor
|
|
8
9
|
from typing import Callable, Optional
|
|
9
10
|
|
|
10
|
-
|
|
11
11
|
import jaclang.compiler.absyntree as ast
|
|
12
12
|
from jaclang.compiler.compile import jac_str_to_pass
|
|
13
13
|
from jaclang.compiler.parser import JacParser
|
|
14
14
|
from jaclang.compiler.passes import Pass
|
|
15
15
|
from jaclang.compiler.passes.main.schedules import py_code_gen_typed
|
|
16
16
|
from jaclang.compiler.passes.tool import FuseCommentsPass, JacFormatPass
|
|
17
|
+
from jaclang.langserve.sem_manager import SemTokManager
|
|
17
18
|
from jaclang.langserve.utils import (
|
|
18
19
|
collect_all_symbols_in_scope,
|
|
19
|
-
collect_symbols,
|
|
20
20
|
create_range,
|
|
21
21
|
find_deepest_symbol_node_at_pos,
|
|
22
|
+
find_index,
|
|
22
23
|
gen_diagnostics,
|
|
23
24
|
get_item_path,
|
|
24
25
|
get_mod_path,
|
|
25
|
-
|
|
26
|
+
get_symbols_for_outline,
|
|
26
27
|
parse_symbol_path,
|
|
27
28
|
resolve_completion_symbol_table,
|
|
28
29
|
)
|
|
@@ -43,104 +44,13 @@ class ModuleInfo:
|
|
|
43
44
|
"""Initialize module info."""
|
|
44
45
|
self.ir = ir
|
|
45
46
|
self.impl_parent: Optional[ModuleInfo] = impl_parent
|
|
46
|
-
self.
|
|
47
|
+
self.sem_manager = SemTokManager(ir=ir)
|
|
47
48
|
|
|
48
49
|
@property
|
|
49
50
|
def uri(self) -> str:
|
|
50
51
|
"""Return uri."""
|
|
51
52
|
return uris.from_fs_path(self.ir.loc.mod_path)
|
|
52
53
|
|
|
53
|
-
def gen_sem_tokens(self) -> list[int]:
|
|
54
|
-
"""Return semantic tokens."""
|
|
55
|
-
tokens = []
|
|
56
|
-
prev_line, prev_col = 0, 0
|
|
57
|
-
for node in self.ir._in_mod_nodes:
|
|
58
|
-
if isinstance(node, ast.NameAtom) and node.sem_token:
|
|
59
|
-
line, col_start, col_end = (
|
|
60
|
-
node.loc.first_line - 1,
|
|
61
|
-
node.loc.col_start - 1,
|
|
62
|
-
node.loc.col_end - 1,
|
|
63
|
-
)
|
|
64
|
-
length = col_end - col_start
|
|
65
|
-
tokens += [
|
|
66
|
-
line - prev_line,
|
|
67
|
-
col_start if line != prev_line else col_start - prev_col,
|
|
68
|
-
length,
|
|
69
|
-
*node.sem_token,
|
|
70
|
-
]
|
|
71
|
-
prev_line, prev_col = line, col_start
|
|
72
|
-
return tokens
|
|
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
|
-
|
|
144
54
|
|
|
145
55
|
class JacLangServer(LanguageServer):
|
|
146
56
|
"""Class for managing workspace."""
|
|
@@ -190,6 +100,7 @@ class JacLangServer(LanguageServer):
|
|
|
190
100
|
def deep_check(self, file_path: str, annex_view: Optional[str] = None) -> bool:
|
|
191
101
|
"""Rebuild a file and its dependencies."""
|
|
192
102
|
try:
|
|
103
|
+
start_time = time.time()
|
|
193
104
|
document = self.workspace.get_text_document(file_path)
|
|
194
105
|
if file_path in self.modules and (
|
|
195
106
|
parent := self.modules[file_path].impl_parent
|
|
@@ -209,13 +120,14 @@ class JacLangServer(LanguageServer):
|
|
|
209
120
|
)
|
|
210
121
|
|
|
211
122
|
self.publish_diagnostics(
|
|
212
|
-
file_path,
|
|
123
|
+
annex_view if annex_view else file_path,
|
|
213
124
|
gen_diagnostics(
|
|
214
125
|
annex_view if annex_view else file_path,
|
|
215
126
|
build.errors_had,
|
|
216
127
|
build.warnings_had,
|
|
217
128
|
),
|
|
218
129
|
)
|
|
130
|
+
self.log_py(f"PROFILE: Deep check took {time.time() - start_time} seconds.")
|
|
219
131
|
return len(build.errors_had) == 0
|
|
220
132
|
except Exception as e:
|
|
221
133
|
self.log_error(f"Error during deep check: {e}")
|
|
@@ -256,12 +168,12 @@ class JacLangServer(LanguageServer):
|
|
|
256
168
|
current_line = document.lines[position.line]
|
|
257
169
|
current_pos = position.character
|
|
258
170
|
current_symbol_path = parse_symbol_path(current_line, current_pos)
|
|
171
|
+
|
|
259
172
|
node_selected = find_deepest_symbol_node_at_pos(
|
|
260
173
|
self.modules[file_path].ir,
|
|
261
174
|
position.line,
|
|
262
175
|
position.character - 2,
|
|
263
176
|
)
|
|
264
|
-
|
|
265
177
|
mod_tab = (
|
|
266
178
|
self.modules[file_path].ir.sym_tab
|
|
267
179
|
if not node_selected
|
|
@@ -269,15 +181,30 @@ class JacLangServer(LanguageServer):
|
|
|
269
181
|
)
|
|
270
182
|
current_tab = self.modules[file_path].ir._sym_tab
|
|
271
183
|
current_symbol_table = mod_tab
|
|
184
|
+
|
|
272
185
|
if completion_trigger == ".":
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
186
|
+
if current_symbol_path:
|
|
187
|
+
completion_items = resolve_completion_symbol_table(
|
|
188
|
+
mod_tab, current_symbol_path, current_tab
|
|
189
|
+
)
|
|
190
|
+
else:
|
|
191
|
+
completion_items = []
|
|
276
192
|
else:
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
193
|
+
if node_selected and (
|
|
194
|
+
node_selected.find_parent_of_type(ast.Architype)
|
|
195
|
+
or node_selected.find_parent_of_type(ast.AbilityDef)
|
|
196
|
+
):
|
|
197
|
+
self_symbol = [
|
|
198
|
+
lspt.CompletionItem(
|
|
199
|
+
label="self", kind=lspt.CompletionItemKind.Variable
|
|
200
|
+
)
|
|
201
|
+
]
|
|
202
|
+
else:
|
|
203
|
+
self_symbol = []
|
|
204
|
+
|
|
205
|
+
completion_items = (
|
|
206
|
+
collect_all_symbols_in_scope(current_symbol_table) + self_symbol
|
|
207
|
+
)
|
|
281
208
|
return lspt.CompletionList(is_incomplete=False, items=completion_items)
|
|
282
209
|
|
|
283
210
|
def rename_module(self, old_path: str, new_path: str) -> None:
|
|
@@ -327,9 +254,16 @@ class JacLangServer(LanguageServer):
|
|
|
327
254
|
"""Return hover information for a file."""
|
|
328
255
|
if file_path not in self.modules:
|
|
329
256
|
return None
|
|
330
|
-
|
|
331
|
-
self.modules[file_path].
|
|
257
|
+
token_index = find_index(
|
|
258
|
+
self.modules[file_path].sem_manager.sem_tokens,
|
|
259
|
+
position.line,
|
|
260
|
+
position.character,
|
|
332
261
|
)
|
|
262
|
+
if token_index is None:
|
|
263
|
+
return None
|
|
264
|
+
node_selected = self.modules[file_path].sem_manager.static_sem_tokens[
|
|
265
|
+
token_index
|
|
266
|
+
][3]
|
|
333
267
|
value = self.get_node_info(node_selected) if node_selected else None
|
|
334
268
|
if value:
|
|
335
269
|
return lspt.Hover(
|
|
@@ -361,12 +295,12 @@ class JacLangServer(LanguageServer):
|
|
|
361
295
|
self.log_warning(f"Attribute error when accessing node attributes: {e}")
|
|
362
296
|
return node_info.strip()
|
|
363
297
|
|
|
364
|
-
def
|
|
298
|
+
def get_outline(self, file_path: str) -> list[lspt.DocumentSymbol]:
|
|
365
299
|
"""Return document symbols for a file."""
|
|
366
300
|
if file_path in self.modules and (
|
|
367
301
|
root_node := self.modules[file_path].ir._sym_tab
|
|
368
302
|
):
|
|
369
|
-
return
|
|
303
|
+
return get_symbols_for_outline(root_node)
|
|
370
304
|
return []
|
|
371
305
|
|
|
372
306
|
def get_definition(
|
|
@@ -375,9 +309,16 @@ class JacLangServer(LanguageServer):
|
|
|
375
309
|
"""Return definition location for a file."""
|
|
376
310
|
if file_path not in self.modules:
|
|
377
311
|
return None
|
|
378
|
-
|
|
379
|
-
self.modules[file_path].
|
|
312
|
+
token_index = find_index(
|
|
313
|
+
self.modules[file_path].sem_manager.sem_tokens,
|
|
314
|
+
position.line,
|
|
315
|
+
position.character,
|
|
380
316
|
)
|
|
317
|
+
if token_index is None:
|
|
318
|
+
return None
|
|
319
|
+
node_selected = self.modules[file_path].sem_manager.static_sem_tokens[
|
|
320
|
+
token_index
|
|
321
|
+
][3]
|
|
381
322
|
if node_selected:
|
|
382
323
|
if (
|
|
383
324
|
isinstance(node_selected, ast.Name)
|
|
@@ -400,13 +341,13 @@ class JacLangServer(LanguageServer):
|
|
|
400
341
|
):
|
|
401
342
|
path_range = get_item_path(node_selected.parent)
|
|
402
343
|
if path_range:
|
|
403
|
-
path,
|
|
404
|
-
if path and
|
|
344
|
+
path, loc_range = path_range
|
|
345
|
+
if path and loc_range:
|
|
405
346
|
return lspt.Location(
|
|
406
347
|
uri=uris.from_fs_path(path),
|
|
407
348
|
range=lspt.Range(
|
|
408
|
-
start=lspt.Position(line=
|
|
409
|
-
end=lspt.Position(line=
|
|
349
|
+
start=lspt.Position(line=loc_range[0], character=0),
|
|
350
|
+
end=lspt.Position(line=loc_range[1], character=5),
|
|
410
351
|
),
|
|
411
352
|
)
|
|
412
353
|
else:
|
|
@@ -424,7 +365,6 @@ class JacLangServer(LanguageServer):
|
|
|
424
365
|
else node_selected
|
|
425
366
|
)
|
|
426
367
|
)
|
|
427
|
-
self.log_py(f"{node_selected}, {decl_node}")
|
|
428
368
|
decl_uri = uris.from_fs_path(decl_node.loc.mod_path)
|
|
429
369
|
try:
|
|
430
370
|
decl_range = create_range(decl_node.loc)
|
|
@@ -443,9 +383,16 @@ class JacLangServer(LanguageServer):
|
|
|
443
383
|
self, file_path: str, position: lspt.Position
|
|
444
384
|
) -> list[lspt.Location]:
|
|
445
385
|
"""Return references for a file."""
|
|
446
|
-
|
|
447
|
-
|
|
386
|
+
if file_path not in self.modules:
|
|
387
|
+
return []
|
|
388
|
+
index1 = find_index(
|
|
389
|
+
self.modules[file_path].sem_manager.sem_tokens,
|
|
390
|
+
position.line,
|
|
391
|
+
position.character,
|
|
448
392
|
)
|
|
393
|
+
if index1 is None:
|
|
394
|
+
return []
|
|
395
|
+
node_selected = self.modules[file_path].sem_manager.static_sem_tokens[index1][3]
|
|
449
396
|
if node_selected and node_selected.sym:
|
|
450
397
|
list_of_references: list[lspt.Location] = [
|
|
451
398
|
lspt.Location(
|
|
@@ -461,7 +408,7 @@ class JacLangServer(LanguageServer):
|
|
|
461
408
|
"""Return semantic tokens for a file."""
|
|
462
409
|
if file_path not in self.modules:
|
|
463
410
|
return lspt.SemanticTokens(data=[])
|
|
464
|
-
return lspt.SemanticTokens(data=self.modules[file_path].sem_tokens)
|
|
411
|
+
return lspt.SemanticTokens(data=self.modules[file_path].sem_manager.sem_tokens)
|
|
465
412
|
|
|
466
413
|
def log_error(self, message: str) -> None:
|
|
467
414
|
"""Log an error message."""
|