jaclang 0.7.5__py3-none-any.whl → 0.7.6__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/compiler/absyntree.py +167 -26
- jaclang/compiler/constant.py +98 -2
- jaclang/compiler/jac.lark +2 -0
- jaclang/compiler/parser.py +4 -0
- jaclang/compiler/passes/main/access_modifier_pass.py +5 -3
- jaclang/compiler/passes/main/def_impl_match_pass.py +3 -1
- jaclang/compiler/passes/main/def_use_pass.py +27 -39
- jaclang/compiler/passes/main/fuse_typeinfo_pass.py +34 -12
- jaclang/compiler/passes/main/sub_node_tab_pass.py +0 -5
- jaclang/compiler/passes/main/sym_tab_build_pass.py +31 -181
- jaclang/compiler/passes/tool/tests/fixtures/genai/essay_review.jac +1 -1
- jaclang/compiler/passes/tool/tests/fixtures/genai/expert_answer.jac +1 -1
- jaclang/compiler/passes/tool/tests/fixtures/genai/joke_gen.jac +1 -1
- jaclang/compiler/passes/tool/tests/fixtures/genai/odd_word_out.jac +1 -1
- jaclang/compiler/passes/tool/tests/fixtures/genai/personality_finder.jac +1 -1
- jaclang/compiler/passes/tool/tests/fixtures/genai/text_to_type.jac +1 -1
- jaclang/compiler/passes/tool/tests/fixtures/genai/translator.jac +1 -1
- jaclang/compiler/passes/tool/tests/fixtures/genai/wikipedia.jac +1 -1
- jaclang/compiler/symtable.py +118 -65
- jaclang/core/aott.py +7 -3
- jaclang/core/importer.py +1 -1
- jaclang/langserve/engine.py +100 -36
- jaclang/langserve/server.py +34 -61
- jaclang/langserve/tests/fixtures/base_module_structure.jac +28 -0
- jaclang/langserve/tests/fixtures/circle_pure.test.jac +15 -0
- jaclang/langserve/tests/fixtures/import_include_statements.jac +6 -0
- jaclang/langserve/tests/fixtures/py_import.py +26 -0
- jaclang/langserve/tests/test_server.py +90 -6
- jaclang/langserve/utils.py +114 -4
- jaclang/plugin/default.py +2 -2
- jaclang/plugin/feature.py +1 -1
- jaclang/plugin/spec.py +1 -1
- jaclang/tests/fixtures/aott_raise.jac +1 -1
- jaclang/tests/fixtures/edgetypeissue.jac +10 -0
- jaclang/tests/fixtures/hello.jac +1 -1
- jaclang/tests/fixtures/with_llm_function.jac +1 -1
- jaclang/tests/fixtures/with_llm_lower.jac +1 -1
- jaclang/tests/fixtures/with_llm_method.jac +1 -1
- jaclang/tests/fixtures/with_llm_type.jac +1 -1
- jaclang/tests/fixtures/with_llm_vision.jac +1 -1
- jaclang/tests/test_language.py +106 -96
- {jaclang-0.7.5.dist-info → jaclang-0.7.6.dist-info}/METADATA +1 -1
- {jaclang-0.7.5.dist-info → jaclang-0.7.6.dist-info}/RECORD +45 -50
- jaclang/core/llms/__init__.py +0 -20
- jaclang/core/llms/anthropic.py +0 -90
- jaclang/core/llms/base.py +0 -206
- jaclang/core/llms/groq.py +0 -70
- jaclang/core/llms/huggingface.py +0 -76
- jaclang/core/llms/ollama.py +0 -81
- jaclang/core/llms/openai.py +0 -65
- jaclang/core/llms/togetherai.py +0 -63
- jaclang/core/llms/utils.py +0 -9
- jaclang/tests/fixtures/edgetypetest.jac +0 -16
- {jaclang-0.7.5.dist-info → jaclang-0.7.6.dist-info}/WHEEL +0 -0
- {jaclang-0.7.5.dist-info → jaclang-0.7.6.dist-info}/entry_points.txt +0 -0
jaclang/langserve/server.py
CHANGED
|
@@ -2,68 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import threading
|
|
6
5
|
from typing import Optional
|
|
7
6
|
|
|
7
|
+
from jaclang.compiler.constant import (
|
|
8
|
+
JacSemTokenModifier as SemTokMod,
|
|
9
|
+
JacSemTokenType as SemTokType,
|
|
10
|
+
)
|
|
8
11
|
from jaclang.langserve.engine import JacLangServer
|
|
9
12
|
from jaclang.langserve.utils import debounce
|
|
10
13
|
|
|
11
14
|
import lsprotocol.types as lspt
|
|
12
15
|
|
|
13
16
|
server = JacLangServer()
|
|
14
|
-
analysis_thread: Optional[threading.Thread] = None
|
|
15
|
-
analysis_stop_event = threading.Event()
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def analyze_and_publish(ls: JacLangServer, uri: str, level: int = 2) -> None:
|
|
19
|
-
"""Analyze and publish diagnostics."""
|
|
20
|
-
global analysis_thread, analysis_stop_event
|
|
21
|
-
|
|
22
|
-
def run_analysis() -> None:
|
|
23
|
-
ls.quick_check(uri)
|
|
24
|
-
ls.push_diagnostics(uri)
|
|
25
|
-
if not analysis_stop_event.is_set() and level > 0:
|
|
26
|
-
ls.deep_check(uri)
|
|
27
|
-
ls.push_diagnostics(uri)
|
|
28
|
-
if not analysis_stop_event.is_set() and level > 1:
|
|
29
|
-
ls.type_check(uri)
|
|
30
|
-
ls.push_diagnostics(uri)
|
|
31
|
-
|
|
32
|
-
analysis_thread = threading.Thread(target=run_analysis)
|
|
33
|
-
analysis_thread.start()
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def stop_analysis() -> None:
|
|
37
|
-
"""Stop analysis."""
|
|
38
|
-
global analysis_thread, analysis_stop_event
|
|
39
|
-
if analysis_thread is not None:
|
|
40
|
-
analysis_stop_event.set()
|
|
41
|
-
analysis_thread.join()
|
|
42
|
-
analysis_stop_event.clear()
|
|
43
17
|
|
|
44
18
|
|
|
45
19
|
@server.feature(lspt.TEXT_DOCUMENT_DID_OPEN)
|
|
46
|
-
|
|
20
|
+
@server.feature(lspt.TEXT_DOCUMENT_DID_SAVE)
|
|
21
|
+
def did_open(ls: JacLangServer, params: lspt.DidOpenTextDocumentParams) -> None:
|
|
47
22
|
"""Check syntax on change."""
|
|
48
|
-
|
|
49
|
-
|
|
23
|
+
ls.analyze_and_publish(params.text_document.uri)
|
|
24
|
+
# token_params = lspt.SemanticTokensParams(
|
|
25
|
+
# text_document=lspt.TextDocumentIdentifier(uri=params.text_document.uri)
|
|
26
|
+
# )
|
|
27
|
+
# tokens = semantic_tokens_full(ls, token_params)
|
|
28
|
+
# ls.send_notification("textDocument/publishSemanticTokens", tokens)
|
|
50
29
|
|
|
51
30
|
|
|
52
31
|
@server.feature(lspt.TEXT_DOCUMENT_DID_CHANGE)
|
|
53
32
|
@debounce(0.1)
|
|
54
|
-
async def did_change(
|
|
55
|
-
ls: JacLangServer, params: lspt.DidChangeTextDocumentParams
|
|
56
|
-
) -> None:
|
|
33
|
+
async def did_change(ls: JacLangServer, params: lspt.DidOpenTextDocumentParams) -> None:
|
|
57
34
|
"""Check syntax on change."""
|
|
58
|
-
|
|
59
|
-
analyze_and_publish(ls, params.text_document.uri)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
@server.feature(lspt.TEXT_DOCUMENT_DID_SAVE)
|
|
63
|
-
async def did_save(ls: JacLangServer, params: lspt.DidSaveTextDocumentParams) -> None:
|
|
64
|
-
"""Check syntax on save."""
|
|
65
|
-
stop_analysis()
|
|
66
|
-
analyze_and_publish(ls, params.text_document.uri)
|
|
35
|
+
ls.analyze_and_publish(params.text_document.uri, level=0)
|
|
67
36
|
|
|
68
37
|
|
|
69
38
|
@server.feature(
|
|
@@ -74,11 +43,8 @@ async def did_save(ls: JacLangServer, params: lspt.DidSaveTextDocumentParams) ->
|
|
|
74
43
|
]
|
|
75
44
|
),
|
|
76
45
|
)
|
|
77
|
-
|
|
46
|
+
def did_create_files(ls: JacLangServer, params: lspt.CreateFilesParams) -> None:
|
|
78
47
|
"""Check syntax on file creation."""
|
|
79
|
-
for file in params.files:
|
|
80
|
-
ls.quick_check(file.uri)
|
|
81
|
-
ls.push_diagnostics(file.uri)
|
|
82
48
|
|
|
83
49
|
|
|
84
50
|
@server.feature(
|
|
@@ -89,13 +55,12 @@ async def did_create_files(ls: JacLangServer, params: lspt.CreateFilesParams) ->
|
|
|
89
55
|
]
|
|
90
56
|
),
|
|
91
57
|
)
|
|
92
|
-
|
|
58
|
+
def did_rename_files(ls: JacLangServer, params: lspt.RenameFilesParams) -> None:
|
|
93
59
|
"""Check syntax on file rename."""
|
|
94
60
|
new_uris = [file.new_uri for file in params.files]
|
|
95
61
|
old_uris = [file.old_uri for file in params.files]
|
|
96
62
|
for i in range(len(new_uris)):
|
|
97
63
|
ls.rename_module(old_uris[i], new_uris[i])
|
|
98
|
-
ls.quick_check(new_uris[i])
|
|
99
64
|
|
|
100
65
|
|
|
101
66
|
@server.feature(
|
|
@@ -106,7 +71,7 @@ async def did_rename_files(ls: JacLangServer, params: lspt.RenameFilesParams) ->
|
|
|
106
71
|
]
|
|
107
72
|
),
|
|
108
73
|
)
|
|
109
|
-
|
|
74
|
+
def did_delete_files(ls: JacLangServer, params: lspt.DeleteFilesParams) -> None:
|
|
110
75
|
"""Check syntax on file delete."""
|
|
111
76
|
for file in params.files:
|
|
112
77
|
ls.delete_module(file.uri)
|
|
@@ -116,9 +81,7 @@ async def did_delete_files(ls: JacLangServer, params: lspt.DeleteFilesParams) ->
|
|
|
116
81
|
lspt.TEXT_DOCUMENT_COMPLETION,
|
|
117
82
|
lspt.CompletionOptions(trigger_characters=[".", ":", ""]),
|
|
118
83
|
)
|
|
119
|
-
|
|
120
|
-
ls: JacLangServer, params: lspt.CompletionParams
|
|
121
|
-
) -> lspt.CompletionList:
|
|
84
|
+
def completion(ls: JacLangServer, params: lspt.CompletionParams) -> lspt.CompletionList:
|
|
122
85
|
"""Provide completion."""
|
|
123
86
|
return ls.get_completion(params.text_document.uri, params.position)
|
|
124
87
|
|
|
@@ -140,25 +103,35 @@ def hover(
|
|
|
140
103
|
|
|
141
104
|
|
|
142
105
|
@server.feature(lspt.TEXT_DOCUMENT_DOCUMENT_SYMBOL)
|
|
143
|
-
|
|
106
|
+
def document_symbol(
|
|
144
107
|
ls: JacLangServer, params: lspt.DocumentSymbolParams
|
|
145
108
|
) -> list[lspt.DocumentSymbol]:
|
|
146
109
|
"""Provide document symbols."""
|
|
147
|
-
stop_analysis()
|
|
148
|
-
analyze_and_publish(ls, params.text_document.uri)
|
|
149
110
|
return ls.get_document_symbols(params.text_document.uri)
|
|
150
111
|
|
|
151
112
|
|
|
152
113
|
@server.feature(lspt.TEXT_DOCUMENT_DEFINITION)
|
|
153
|
-
|
|
114
|
+
def definition(
|
|
154
115
|
ls: JacLangServer, params: lspt.TextDocumentPositionParams
|
|
155
116
|
) -> Optional[lspt.Location]:
|
|
156
117
|
"""Provide definition."""
|
|
157
|
-
stop_analysis()
|
|
158
|
-
analyze_and_publish(ls, params.text_document.uri, level=1)
|
|
159
118
|
return ls.get_definition(params.text_document.uri, params.position)
|
|
160
119
|
|
|
161
120
|
|
|
121
|
+
@server.feature(
|
|
122
|
+
lspt.TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL,
|
|
123
|
+
lspt.SemanticTokensLegend(
|
|
124
|
+
token_types=SemTokType.as_str_list(),
|
|
125
|
+
token_modifiers=SemTokMod.as_str_list(),
|
|
126
|
+
),
|
|
127
|
+
)
|
|
128
|
+
def semantic_tokens_full(
|
|
129
|
+
ls: JacLangServer, params: lspt.SemanticTokensParams
|
|
130
|
+
) -> lspt.SemanticTokens:
|
|
131
|
+
"""Provide semantic tokens."""
|
|
132
|
+
return ls.get_semantic_tokens(params.text_document.uri)
|
|
133
|
+
|
|
134
|
+
|
|
162
135
|
def run_lang_server() -> None:
|
|
163
136
|
"""Run the language server."""
|
|
164
137
|
server.start_io()
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""A Docstring can be added the head of any module.
|
|
2
|
+
|
|
3
|
+
Any element in the module can also have a docstring.
|
|
4
|
+
If there is only one docstring before the first element,
|
|
5
|
+
it is assumed to be a module docstring.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
"""A docstring for add function"""
|
|
9
|
+
can add(a: int, b: int) -> int {
|
|
10
|
+
return a + b;
|
|
11
|
+
}
|
|
12
|
+
# No docstring for subtract function
|
|
13
|
+
|
|
14
|
+
can subtract(a: int, b: int) -> int {
|
|
15
|
+
return a - b;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
with entry:__main__ {
|
|
19
|
+
print(add(1, subtract(3, 1)));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
glob x: int = 10;
|
|
23
|
+
|
|
24
|
+
enum Color {
|
|
25
|
+
RED,
|
|
26
|
+
GREEN,
|
|
27
|
+
BLUE
|
|
28
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
glob expected_area = 78.53981633974483;
|
|
2
|
+
|
|
3
|
+
test a1 {
|
|
4
|
+
check assertAlmostEqual(calculate_area(RAD), expected_area);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
test a2 {
|
|
8
|
+
c = Circle(RAD);
|
|
9
|
+
check assertAlmostEqual(c.area(), expected_area);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
test a3 {
|
|
13
|
+
c = Circle(RAD);
|
|
14
|
+
check assertEqual(c.shape_type, ShapeType.CIRCLE);
|
|
15
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import:py os;
|
|
2
|
+
import:py from math, sqrt as square_root;
|
|
3
|
+
import:py datetime as dt;
|
|
4
|
+
import:jac from base_module_structure, add as adsd, subtract,x,Color as clr;
|
|
5
|
+
import:jac base_module_structure as base_module_structure;
|
|
6
|
+
import:py from py_import,add1 as ss, sub1 as subtract1,apple,Orange1;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""This is a helper file."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def add1():
|
|
5
|
+
return 1 + 1
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def sub1():
|
|
9
|
+
return 1 - 1
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# classes
|
|
13
|
+
class Orange1:
|
|
14
|
+
def __init__(self):
|
|
15
|
+
self.orange = 1
|
|
16
|
+
|
|
17
|
+
def get_orange(self):
|
|
18
|
+
return self.orange
|
|
19
|
+
|
|
20
|
+
def set_orange(self, orange):
|
|
21
|
+
self.orange = orange
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# variables
|
|
25
|
+
orange2 = 1
|
|
26
|
+
apple = 1
|
|
@@ -63,10 +63,10 @@ class TestJacLangServer(TestCase):
|
|
|
63
63
|
self.assertEqual(len(lsp.modules), 1)
|
|
64
64
|
self.assertEqual(lsp.modules[circle_file].diagnostics[0].range.start.line, 22)
|
|
65
65
|
lsp.deep_check(circle_file)
|
|
66
|
-
self.assertEqual(len(lsp.modules), 1)
|
|
66
|
+
# self.assertEqual(len(lsp.modules), 1)
|
|
67
67
|
self.assertEqual(lsp.modules[circle_file].diagnostics[0].range.start.line, 22)
|
|
68
68
|
lsp.type_check(circle_file)
|
|
69
|
-
self.assertEqual(len(lsp.modules), 1)
|
|
69
|
+
# self.assertEqual(len(lsp.modules), 1)
|
|
70
70
|
self.assertEqual(lsp.modules[circle_file].diagnostics[0].range.start.line, 22)
|
|
71
71
|
|
|
72
72
|
def test_impl_stay_connected(self) -> None:
|
|
@@ -88,7 +88,7 @@ class TestJacLangServer(TestCase):
|
|
|
88
88
|
"Circle class inherits from Shape.",
|
|
89
89
|
lsp.get_hover_info(circle_file, pos).contents.value,
|
|
90
90
|
)
|
|
91
|
-
lsp.type_check(circle_impl_file
|
|
91
|
+
lsp.type_check(circle_impl_file)
|
|
92
92
|
pos = lspt.Position(8, 11)
|
|
93
93
|
self.assertIn(
|
|
94
94
|
"ability) calculate_area: float",
|
|
@@ -105,9 +105,9 @@ class TestJacLangServer(TestCase):
|
|
|
105
105
|
circle_impl_file = uris.from_fs_path(
|
|
106
106
|
self.fixture_abs_path("circle_pure.impl.jac")
|
|
107
107
|
)
|
|
108
|
-
lsp.quick_check(circle_impl_file
|
|
109
|
-
lsp.deep_check(circle_impl_file
|
|
110
|
-
lsp.type_check(circle_impl_file
|
|
108
|
+
lsp.quick_check(circle_impl_file)
|
|
109
|
+
lsp.deep_check(circle_impl_file)
|
|
110
|
+
lsp.type_check(circle_impl_file)
|
|
111
111
|
pos = lspt.Position(8, 11)
|
|
112
112
|
self.assertIn(
|
|
113
113
|
"ability) calculate_area: float",
|
|
@@ -180,3 +180,87 @@ class TestJacLangServer(TestCase):
|
|
|
180
180
|
"guess_game4.jac:27:8-27:21",
|
|
181
181
|
str(lsp.get_definition(guess_game_file, lspt.Position(46, 45))),
|
|
182
182
|
)
|
|
183
|
+
|
|
184
|
+
def test_test_annex(self) -> None:
|
|
185
|
+
"""Test that the server doesn't run if there is a syntax error."""
|
|
186
|
+
lsp = JacLangServer()
|
|
187
|
+
# Set up the workspace path to "fixtures/"
|
|
188
|
+
workspace_path = self.fixture_abs_path("")
|
|
189
|
+
workspace = Workspace(workspace_path, lsp)
|
|
190
|
+
lsp.lsp._workspace = workspace
|
|
191
|
+
circle_file = uris.from_fs_path(self.fixture_abs_path("circle_pure.test.jac"))
|
|
192
|
+
lsp.quick_check(circle_file)
|
|
193
|
+
lsp.deep_check(circle_file)
|
|
194
|
+
lsp.type_check(circle_file)
|
|
195
|
+
pos = lspt.Position(13, 29)
|
|
196
|
+
self.assertIn(
|
|
197
|
+
"shape_type: circle_pure.ShapeType",
|
|
198
|
+
lsp.get_hover_info(circle_file, pos).contents.value,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
def test_go_to_defintion_import(self) -> None:
|
|
202
|
+
"""Test that the go to definition is correct."""
|
|
203
|
+
lsp = JacLangServer()
|
|
204
|
+
workspace_path = self.fixture_abs_path("")
|
|
205
|
+
workspace = Workspace(workspace_path, lsp)
|
|
206
|
+
lsp.lsp._workspace = workspace
|
|
207
|
+
import_file = uris.from_fs_path(
|
|
208
|
+
self.fixture_abs_path("import_include_statements.jac")
|
|
209
|
+
)
|
|
210
|
+
lsp.quick_check(import_file)
|
|
211
|
+
lsp.deep_check(import_file)
|
|
212
|
+
lsp.type_check(import_file)
|
|
213
|
+
positions = [
|
|
214
|
+
(2, 16, "datetime.py:0:0-0:0"),
|
|
215
|
+
(3, 17, "base_module_structure.jac:0:0-0:0"),
|
|
216
|
+
(3, 74, "base_module_structure.jac:23:0-23:5"),
|
|
217
|
+
(5, 65, "py_import.py:12:0-20:5"),
|
|
218
|
+
(5, 35, "py_import.py:3:0-4:5"),
|
|
219
|
+
]
|
|
220
|
+
|
|
221
|
+
for line, char, expected in positions:
|
|
222
|
+
with self.subTest(line=line, char=char):
|
|
223
|
+
self.assertIn(
|
|
224
|
+
expected,
|
|
225
|
+
str(lsp.get_definition(import_file, lspt.Position(line, char))),
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
def test_sem_tokens(self) -> None:
|
|
229
|
+
"""Test that the Semantic Tokens are generated correctly."""
|
|
230
|
+
lsp = JacLangServer()
|
|
231
|
+
workspace_path = self.fixture_abs_path("")
|
|
232
|
+
workspace = Workspace(workspace_path, lsp)
|
|
233
|
+
lsp.lsp._workspace = workspace
|
|
234
|
+
circle_file = uris.from_fs_path(self.fixture_abs_path("circle.jac"))
|
|
235
|
+
lsp.quick_check(circle_file)
|
|
236
|
+
lsp.deep_check(circle_file)
|
|
237
|
+
lsp.type_check(circle_file)
|
|
238
|
+
sem_list = lsp.get_semantic_tokens(circle_file).data
|
|
239
|
+
expected_counts = [
|
|
240
|
+
("<JacSemTokenType.VARIABLE: 8>, <JacSemTokenModifier.READONLY: 4>", 206),
|
|
241
|
+
(
|
|
242
|
+
"<JacSemTokenType.PROPERTY: 9>, <JacSemTokenModifier.DEFINITION: 2>,",
|
|
243
|
+
112,
|
|
244
|
+
),
|
|
245
|
+
(
|
|
246
|
+
"<JacSemTokenType.PARAMETER: 7>, <JacSemTokenModifier.DECLARATION: 1>,",
|
|
247
|
+
56,
|
|
248
|
+
),
|
|
249
|
+
(
|
|
250
|
+
"<JacSemTokenType.FUNCTION: 12>, <JacSemTokenModifier.DECLARATION: 1>,",
|
|
251
|
+
25,
|
|
252
|
+
),
|
|
253
|
+
("<JacSemTokenType.METHOD: 13>, <JacSemTokenModifier.DECLARATION: 1>", 12),
|
|
254
|
+
("<JacSemTokenType.ENUM: 3>, <JacSemTokenModifier.DECLARATION: 1>,", 37),
|
|
255
|
+
("<JacSemTokenType.CLASS: 2>, <JacSemTokenModifier.DECLARATION: ", 162),
|
|
256
|
+
(
|
|
257
|
+
"<JacSemTokenType.NAMESPACE: 0>, <JacSemTokenModifier.DEFINITION: 2>,",
|
|
258
|
+
10,
|
|
259
|
+
),
|
|
260
|
+
("0, 0, 4,", 22),
|
|
261
|
+
("0, 0, 3,", 192),
|
|
262
|
+
("0, 0, 6, ", 65),
|
|
263
|
+
(" 0, 7, 3,", 3),
|
|
264
|
+
]
|
|
265
|
+
for token_type, expected_count in expected_counts:
|
|
266
|
+
self.assertEqual(str(sem_list).count(token_type), expected_count)
|
jaclang/langserve/utils.py
CHANGED
|
@@ -2,12 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import builtins
|
|
5
|
+
import importlib.util
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
5
8
|
from functools import wraps
|
|
6
9
|
from typing import Any, Awaitable, Callable, Coroutine, Optional, ParamSpec, TypeVar
|
|
7
10
|
|
|
8
11
|
import jaclang.compiler.absyntree as ast
|
|
9
12
|
from jaclang.compiler.codeloc import CodeLocInfo
|
|
10
13
|
from jaclang.compiler.symtable import Symbol, SymbolTable
|
|
14
|
+
from jaclang.utils.helpers import import_target_to_relative_path
|
|
11
15
|
|
|
12
16
|
import lsprotocol.types as lspt
|
|
13
17
|
|
|
@@ -68,7 +72,7 @@ def find_deepest_symbol_node_at_pos(
|
|
|
68
72
|
if isinstance(node, ast.AstSymbolNode):
|
|
69
73
|
last_symbol_node = node
|
|
70
74
|
|
|
71
|
-
for child in node.kid:
|
|
75
|
+
for child in [i for i in node.kid if i.loc.mod_path == node.loc.mod_path]:
|
|
72
76
|
if position_within_node(child, line, character):
|
|
73
77
|
deeper_node = find_deepest_symbol_node_at_pos(child, line, character)
|
|
74
78
|
if deeper_node is not None:
|
|
@@ -150,7 +154,7 @@ def collect_symbols(node: SymbolTable) -> list[lspt.DocumentSymbol]:
|
|
|
150
154
|
|
|
151
155
|
def owner_sym(table: SymbolTable) -> Optional[Symbol]:
|
|
152
156
|
"""Get owner sym."""
|
|
153
|
-
if table.
|
|
157
|
+
if table.parent and isinstance(table.owner, ast.AstSymbolNode):
|
|
154
158
|
return table.parent.lookup(table.owner.sym_name)
|
|
155
159
|
return None
|
|
156
160
|
|
|
@@ -158,8 +162,14 @@ def owner_sym(table: SymbolTable) -> Optional[Symbol]:
|
|
|
158
162
|
def create_range(loc: CodeLocInfo) -> lspt.Range:
|
|
159
163
|
"""Create an lspt.Range from a location object."""
|
|
160
164
|
return lspt.Range(
|
|
161
|
-
start=lspt.Position(
|
|
162
|
-
|
|
165
|
+
start=lspt.Position(
|
|
166
|
+
line=loc.first_line - 1 if loc.first_line > 0 else 0,
|
|
167
|
+
character=loc.col_start - 1 if loc.col_start > 0 else 0,
|
|
168
|
+
),
|
|
169
|
+
end=lspt.Position(
|
|
170
|
+
line=loc.last_line - 1 if loc.last_line > 0 else 0,
|
|
171
|
+
character=loc.col_end - 1 if loc.col_end > 0 else 0,
|
|
172
|
+
),
|
|
163
173
|
)
|
|
164
174
|
|
|
165
175
|
|
|
@@ -182,3 +192,103 @@ def kind_map(sub_tab: ast.AstNode) -> lspt.SymbolKind:
|
|
|
182
192
|
)
|
|
183
193
|
)
|
|
184
194
|
)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def get_mod_path(mod_path: ast.ModulePath, name_node: ast.Name) -> str | None:
|
|
198
|
+
"""Get path for a module import name."""
|
|
199
|
+
ret_target = None
|
|
200
|
+
module_location_path = mod_path.loc.mod_path
|
|
201
|
+
if mod_path.parent and (
|
|
202
|
+
(
|
|
203
|
+
isinstance(mod_path.parent.parent, ast.Import)
|
|
204
|
+
and mod_path.parent.parent.hint.tag.value == "py"
|
|
205
|
+
)
|
|
206
|
+
or (
|
|
207
|
+
isinstance(mod_path.parent, ast.Import)
|
|
208
|
+
and mod_path.parent.from_loc
|
|
209
|
+
and mod_path.parent.hint.tag.value == "py"
|
|
210
|
+
)
|
|
211
|
+
):
|
|
212
|
+
if mod_path.path and name_node in mod_path.path:
|
|
213
|
+
temporary_path_str = ("." * mod_path.level) + ".".join(
|
|
214
|
+
[p.value for p in mod_path.path[: mod_path.path.index(name_node) + 1]]
|
|
215
|
+
if mod_path.path
|
|
216
|
+
else ""
|
|
217
|
+
)
|
|
218
|
+
else:
|
|
219
|
+
temporary_path_str = mod_path.path_str
|
|
220
|
+
sys.path.append(os.path.dirname(module_location_path))
|
|
221
|
+
spec = importlib.util.find_spec(temporary_path_str)
|
|
222
|
+
sys.path.remove(os.path.dirname(module_location_path))
|
|
223
|
+
if spec and spec.origin and spec.origin.endswith(".py"):
|
|
224
|
+
ret_target = spec.origin
|
|
225
|
+
elif mod_path.parent and (
|
|
226
|
+
(
|
|
227
|
+
isinstance(mod_path.parent.parent, ast.Import)
|
|
228
|
+
and mod_path.parent.parent.hint.tag.value == "jac"
|
|
229
|
+
)
|
|
230
|
+
or (
|
|
231
|
+
isinstance(mod_path.parent, ast.Import)
|
|
232
|
+
and mod_path.parent.from_loc
|
|
233
|
+
and mod_path.parent.hint.tag.value == "jac"
|
|
234
|
+
)
|
|
235
|
+
):
|
|
236
|
+
ret_target = import_target_to_relative_path(
|
|
237
|
+
level=mod_path.level,
|
|
238
|
+
target=mod_path.path_str,
|
|
239
|
+
base_path=os.path.dirname(module_location_path),
|
|
240
|
+
)
|
|
241
|
+
return ret_target
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def get_item_path(mod_item: ast.ModuleItem) -> tuple[str, tuple[int, int]] | None:
|
|
245
|
+
"""Get path."""
|
|
246
|
+
item_name = mod_item.name.value
|
|
247
|
+
if mod_item.from_parent.hint.tag.value == "py" and mod_item.from_parent.from_loc:
|
|
248
|
+
path = get_mod_path(mod_item.from_parent.from_loc, mod_item.name)
|
|
249
|
+
if path:
|
|
250
|
+
return get_definition_range(path, item_name)
|
|
251
|
+
elif mod_item.from_parent.hint.tag.value == "jac":
|
|
252
|
+
mod_node = mod_item.from_mod_path
|
|
253
|
+
if mod_node.sub_module and mod_node.sub_module._sym_tab:
|
|
254
|
+
for symbol_name, symbol in mod_node.sub_module._sym_tab.tab.items():
|
|
255
|
+
if symbol_name == item_name:
|
|
256
|
+
return symbol.decl.loc.mod_path, (
|
|
257
|
+
symbol.decl.loc.first_line - 1,
|
|
258
|
+
symbol.decl.loc.last_line - 1,
|
|
259
|
+
)
|
|
260
|
+
return None
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def get_definition_range(
|
|
264
|
+
filename: str, name: str
|
|
265
|
+
) -> tuple[str, tuple[int, int]] | None:
|
|
266
|
+
"""Get the start and end line of a function or class definition in a file."""
|
|
267
|
+
import ast
|
|
268
|
+
|
|
269
|
+
with open(filename, "r") as file:
|
|
270
|
+
source = file.read()
|
|
271
|
+
|
|
272
|
+
tree = ast.parse(source)
|
|
273
|
+
|
|
274
|
+
for node in ast.walk(tree):
|
|
275
|
+
if isinstance(node, (ast.FunctionDef, ast.ClassDef)) and node.name == name:
|
|
276
|
+
start_line = node.lineno
|
|
277
|
+
end_line = (
|
|
278
|
+
node.body[-1].end_lineno
|
|
279
|
+
if hasattr(node.body[-1], "end_lineno")
|
|
280
|
+
else node.body[-1].lineno
|
|
281
|
+
)
|
|
282
|
+
if start_line and end_line:
|
|
283
|
+
return filename, (start_line - 1, end_line - 1)
|
|
284
|
+
elif isinstance(node, ast.Assign):
|
|
285
|
+
for target in node.targets:
|
|
286
|
+
if isinstance(target, ast.Name) and target.id == name:
|
|
287
|
+
start_line = node.lineno
|
|
288
|
+
end_line = (
|
|
289
|
+
node.end_lineno if hasattr(node, "end_lineno") else node.lineno
|
|
290
|
+
)
|
|
291
|
+
if start_line and end_line:
|
|
292
|
+
return filename, (start_line - 1, end_line - 1)
|
|
293
|
+
|
|
294
|
+
return None
|
jaclang/plugin/default.py
CHANGED
|
@@ -495,14 +495,14 @@ class JacFeatureDefaults:
|
|
|
495
495
|
@hookimpl
|
|
496
496
|
def build_edge(
|
|
497
497
|
is_undirected: bool,
|
|
498
|
-
conn_type: Optional[Type[EdgeArchitype]],
|
|
498
|
+
conn_type: Optional[Type[EdgeArchitype] | EdgeArchitype],
|
|
499
499
|
conn_assign: Optional[tuple[tuple, tuple]],
|
|
500
500
|
) -> Callable[[], EdgeArchitype]:
|
|
501
501
|
"""Jac's root getter."""
|
|
502
502
|
conn_type = conn_type if conn_type else GenericEdge
|
|
503
503
|
|
|
504
504
|
def builder() -> EdgeArchitype:
|
|
505
|
-
edge = conn_type()
|
|
505
|
+
edge = conn_type() if isinstance(conn_type, type) else conn_type
|
|
506
506
|
edge._jac_.is_undirected = is_undirected
|
|
507
507
|
if conn_assign:
|
|
508
508
|
for fld, val in zip(conn_assign[0], conn_assign[1]):
|
jaclang/plugin/feature.py
CHANGED
|
@@ -263,7 +263,7 @@ class JacFeature:
|
|
|
263
263
|
@staticmethod
|
|
264
264
|
def build_edge(
|
|
265
265
|
is_undirected: bool,
|
|
266
|
-
conn_type: Optional[Type[EdgeArchitype]],
|
|
266
|
+
conn_type: Optional[Type[EdgeArchitype] | EdgeArchitype],
|
|
267
267
|
conn_assign: Optional[tuple[tuple, tuple]],
|
|
268
268
|
) -> Callable[[], EdgeArchitype]:
|
|
269
269
|
"""Jac's root getter."""
|
jaclang/plugin/spec.py
CHANGED
|
@@ -246,7 +246,7 @@ class JacFeatureSpec:
|
|
|
246
246
|
@hookspec(firstresult=True)
|
|
247
247
|
def build_edge(
|
|
248
248
|
is_undirected: bool,
|
|
249
|
-
conn_type: Optional[Type[EdgeArchitype]],
|
|
249
|
+
conn_type: Optional[Type[EdgeArchitype] | EdgeArchitype],
|
|
250
250
|
conn_assign: Optional[tuple[tuple, tuple]],
|
|
251
251
|
) -> Callable[[], EdgeArchitype]:
|
|
252
252
|
"""Jac's root getter."""
|
jaclang/tests/fixtures/hello.jac
CHANGED