jaclang 0.7.1__py3-none-any.whl → 0.7.2__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 +51 -14
- jaclang/compiler/passes/main/def_impl_match_pass.py +9 -3
- jaclang/compiler/passes/main/fuse_typeinfo_pass.py +20 -1
- jaclang/compiler/passes/main/import_pass.py +4 -1
- jaclang/compiler/passes/main/pyast_gen_pass.py +14 -6
- jaclang/compiler/passes/main/pyast_load_pass.py +2 -1
- jaclang/compiler/passes/main/pyjac_ast_link_pass.py +6 -1
- jaclang/compiler/passes/main/pyout_pass.py +3 -1
- jaclang/compiler/passes/main/tests/test_import_pass.py +8 -0
- jaclang/compiler/passes/main/tests/test_type_check_pass.py +1 -1
- jaclang/compiler/passes/tool/jac_formatter_pass.py +14 -2
- jaclang/compiler/passes/tool/tests/fixtures/doc_string.jac +15 -0
- jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +7 -5
- jaclang/compiler/passes/tool/tests/test_unparse_validate.py +1 -2
- jaclang/compiler/symtable.py +21 -1
- jaclang/core/aott.py +107 -11
- jaclang/core/construct.py +171 -5
- jaclang/core/llms/anthropic.py +31 -2
- jaclang/core/llms/base.py +3 -3
- jaclang/core/llms/groq.py +4 -1
- jaclang/core/llms/huggingface.py +4 -1
- jaclang/core/llms/ollama.py +4 -1
- jaclang/core/llms/openai.py +6 -2
- jaclang/core/llms/togetherai.py +4 -1
- jaclang/langserve/engine.py +99 -115
- jaclang/langserve/server.py +27 -5
- jaclang/langserve/tests/fixtures/circle_pure.impl.jac +8 -4
- jaclang/langserve/tests/fixtures/circle_pure.jac +2 -2
- jaclang/langserve/tests/test_server.py +123 -0
- jaclang/langserve/utils.py +100 -10
- jaclang/plugin/default.py +25 -83
- jaclang/plugin/feature.py +10 -12
- jaclang/plugin/tests/test_features.py +0 -33
- jaclang/settings.py +1 -0
- jaclang/tests/fixtures/byllmissue.jac +3 -0
- jaclang/tests/fixtures/hash_init_check.jac +17 -0
- jaclang/tests/fixtures/math_question.jpg +0 -0
- jaclang/tests/fixtures/nosigself.jac +19 -0
- jaclang/tests/fixtures/walker_override.jac +21 -0
- jaclang/tests/fixtures/with_llm_vision.jac +25 -0
- jaclang/tests/test_language.py +61 -11
- jaclang/utils/treeprinter.py +19 -2
- {jaclang-0.7.1.dist-info → jaclang-0.7.2.dist-info}/METADATA +3 -2
- {jaclang-0.7.1.dist-info → jaclang-0.7.2.dist-info}/RECORD +46 -41
- jaclang/core/memory.py +0 -48
- jaclang/core/shelve_storage.py +0 -55
- {jaclang-0.7.1.dist-info → jaclang-0.7.2.dist-info}/WHEEL +0 -0
- {jaclang-0.7.1.dist-info → jaclang-0.7.2.dist-info}/entry_points.txt +0 -0
jaclang/langserve/engine.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import logging
|
|
5
6
|
from enum import IntEnum
|
|
6
7
|
from hashlib import md5
|
|
7
8
|
from typing import Optional, Sequence
|
|
@@ -14,7 +15,11 @@ from jaclang.compiler.passes import Pass
|
|
|
14
15
|
from jaclang.compiler.passes.main.schedules import type_checker_sched
|
|
15
16
|
from jaclang.compiler.passes.tool import FuseCommentsPass, JacFormatPass
|
|
16
17
|
from jaclang.compiler.passes.transform import Alert
|
|
17
|
-
from jaclang.langserve.utils import
|
|
18
|
+
from jaclang.langserve.utils import (
|
|
19
|
+
collect_symbols,
|
|
20
|
+
create_range,
|
|
21
|
+
find_deepest_symbol_node_at_pos,
|
|
22
|
+
)
|
|
18
23
|
from jaclang.vendor.pygls import uris
|
|
19
24
|
from jaclang.vendor.pygls.server import LanguageServer
|
|
20
25
|
|
|
@@ -62,31 +67,14 @@ class ModuleInfo:
|
|
|
62
67
|
"""Return diagnostics."""
|
|
63
68
|
return [
|
|
64
69
|
lspt.Diagnostic(
|
|
65
|
-
range=
|
|
66
|
-
start=lspt.Position(
|
|
67
|
-
line=error.loc.first_line - 1, character=error.loc.col_start - 1
|
|
68
|
-
),
|
|
69
|
-
end=lspt.Position(
|
|
70
|
-
line=error.loc.last_line - 1,
|
|
71
|
-
character=error.loc.col_end - 1,
|
|
72
|
-
),
|
|
73
|
-
),
|
|
70
|
+
range=create_range(error.loc),
|
|
74
71
|
message=error.msg,
|
|
75
72
|
severity=lspt.DiagnosticSeverity.Error,
|
|
76
73
|
)
|
|
77
74
|
for error in self.errors
|
|
78
75
|
] + [
|
|
79
76
|
lspt.Diagnostic(
|
|
80
|
-
range=
|
|
81
|
-
start=lspt.Position(
|
|
82
|
-
line=warning.loc.first_line - 1,
|
|
83
|
-
character=warning.loc.col_start - 1,
|
|
84
|
-
),
|
|
85
|
-
end=lspt.Position(
|
|
86
|
-
line=warning.loc.last_line - 1,
|
|
87
|
-
character=warning.loc.col_end - 1,
|
|
88
|
-
),
|
|
89
|
-
),
|
|
77
|
+
range=create_range(warning.loc),
|
|
90
78
|
message=warning.msg,
|
|
91
79
|
severity=lspt.DiagnosticSeverity.Warning,
|
|
92
80
|
)
|
|
@@ -104,7 +92,7 @@ class JacLangServer(LanguageServer):
|
|
|
104
92
|
|
|
105
93
|
def module_not_diff(self, uri: str, alev: ALev) -> bool:
|
|
106
94
|
"""Check if module was changed."""
|
|
107
|
-
doc = self.workspace.
|
|
95
|
+
doc = self.workspace.get_text_document(uri)
|
|
108
96
|
return (
|
|
109
97
|
doc.uri in self.modules
|
|
110
98
|
and self.modules[doc.uri].ir.source.hash
|
|
@@ -125,9 +113,15 @@ class JacLangServer(LanguageServer):
|
|
|
125
113
|
|
|
126
114
|
def unwind_to_parent(self, file_path: str) -> str:
|
|
127
115
|
"""Unwind to parent."""
|
|
116
|
+
orig_file_path = file_path
|
|
128
117
|
if file_path in self.modules:
|
|
129
118
|
while cur := self.modules[file_path].parent:
|
|
130
119
|
file_path = cur.uri
|
|
120
|
+
if file_path == orig_file_path and (
|
|
121
|
+
discover := self.modules[file_path].ir.annexable_by
|
|
122
|
+
):
|
|
123
|
+
file_path = uris.from_fs_path(discover)
|
|
124
|
+
self.quick_check(file_path)
|
|
131
125
|
return file_path
|
|
132
126
|
|
|
133
127
|
def update_modules(self, file_path: str, build: Pass, alev: ALev) -> None:
|
|
@@ -135,6 +129,9 @@ class JacLangServer(LanguageServer):
|
|
|
135
129
|
if not isinstance(build.ir, ast.Module):
|
|
136
130
|
self.log_error("Error with module build.")
|
|
137
131
|
return
|
|
132
|
+
save_parent = (
|
|
133
|
+
self.modules[file_path].parent if file_path in self.modules else None
|
|
134
|
+
)
|
|
138
135
|
self.modules[file_path] = ModuleInfo(
|
|
139
136
|
ir=build.ir,
|
|
140
137
|
errors=[
|
|
@@ -149,6 +146,7 @@ class JacLangServer(LanguageServer):
|
|
|
149
146
|
],
|
|
150
147
|
alev=alev,
|
|
151
148
|
)
|
|
149
|
+
self.modules[file_path].parent = save_parent
|
|
152
150
|
for p in build.ir.mod_deps.keys():
|
|
153
151
|
uri = uris.from_fs_path(p)
|
|
154
152
|
self.modules[uri] = ModuleInfo(
|
|
@@ -157,13 +155,16 @@ class JacLangServer(LanguageServer):
|
|
|
157
155
|
warnings=[i for i in build.warnings_had if i.loc.mod_path == p],
|
|
158
156
|
alev=alev,
|
|
159
157
|
)
|
|
158
|
+
self.modules[uri].parent = (
|
|
159
|
+
self.modules[file_path] if file_path != uri else None
|
|
160
|
+
)
|
|
160
161
|
|
|
161
|
-
def quick_check(self, file_path: str) -> None:
|
|
162
|
+
def quick_check(self, file_path: str, force: bool = False) -> None:
|
|
162
163
|
"""Rebuild a file."""
|
|
163
|
-
if self.module_not_diff(file_path, ALev.QUICK):
|
|
164
|
+
if not force and self.module_not_diff(file_path, ALev.QUICK):
|
|
164
165
|
return
|
|
165
166
|
try:
|
|
166
|
-
document = self.workspace.
|
|
167
|
+
document = self.workspace.get_text_document(file_path)
|
|
167
168
|
build = jac_str_to_pass(
|
|
168
169
|
jac_str=document.source, file_path=document.path, schedule=[]
|
|
169
170
|
)
|
|
@@ -171,11 +172,11 @@ class JacLangServer(LanguageServer):
|
|
|
171
172
|
self.log_error(f"Error during syntax check: {e}")
|
|
172
173
|
self.update_modules(file_path, build, ALev.QUICK)
|
|
173
174
|
|
|
174
|
-
def deep_check(self, file_path: str) -> None:
|
|
175
|
+
def deep_check(self, file_path: str, force: bool = False) -> None:
|
|
175
176
|
"""Rebuild a file and its dependencies."""
|
|
176
177
|
if file_path in self.modules:
|
|
177
|
-
self.quick_check(file_path)
|
|
178
|
-
if self.module_not_diff(file_path, ALev.DEEP):
|
|
178
|
+
self.quick_check(file_path, force=force)
|
|
179
|
+
if not force and self.module_not_diff(file_path, ALev.DEEP):
|
|
179
180
|
return
|
|
180
181
|
try:
|
|
181
182
|
file_path = self.unwind_to_parent(file_path)
|
|
@@ -184,11 +185,11 @@ class JacLangServer(LanguageServer):
|
|
|
184
185
|
self.log_error(f"Error during syntax check: {e}")
|
|
185
186
|
self.update_modules(file_path, build, ALev.DEEP)
|
|
186
187
|
|
|
187
|
-
def type_check(self, file_path: str) -> None:
|
|
188
|
+
def type_check(self, file_path: str, force: bool = False) -> None:
|
|
188
189
|
"""Rebuild a file and its dependencies."""
|
|
189
190
|
if file_path not in self.modules:
|
|
190
|
-
self.deep_check(file_path)
|
|
191
|
-
if self.module_not_diff(file_path, ALev.TYPE):
|
|
191
|
+
self.deep_check(file_path, force=force)
|
|
192
|
+
if not force and self.module_not_diff(file_path, ALev.TYPE):
|
|
192
193
|
return
|
|
193
194
|
try:
|
|
194
195
|
file_path = self.unwind_to_parent(file_path)
|
|
@@ -204,7 +205,7 @@ class JacLangServer(LanguageServer):
|
|
|
204
205
|
) -> lspt.CompletionList:
|
|
205
206
|
"""Return completion for a file."""
|
|
206
207
|
items = []
|
|
207
|
-
document = self.workspace.
|
|
208
|
+
document = self.workspace.get_text_document(file_path)
|
|
208
209
|
current_line = document.lines[position.line].strip()
|
|
209
210
|
if current_line.endswith("hello."):
|
|
210
211
|
|
|
@@ -228,7 +229,7 @@ class JacLangServer(LanguageServer):
|
|
|
228
229
|
def formatted_jac(self, file_path: str) -> list[lspt.TextEdit]:
|
|
229
230
|
"""Return formatted jac."""
|
|
230
231
|
try:
|
|
231
|
-
document = self.workspace.
|
|
232
|
+
document = self.workspace.get_text_document(file_path)
|
|
232
233
|
format = jac_str_to_pass(
|
|
233
234
|
jac_str=document.source,
|
|
234
235
|
file_path=document.path,
|
|
@@ -259,7 +260,7 @@ class JacLangServer(LanguageServer):
|
|
|
259
260
|
self, file_path: str, position: lspt.Position
|
|
260
261
|
) -> Optional[lspt.Hover]:
|
|
261
262
|
"""Return hover information for a file."""
|
|
262
|
-
node_selected =
|
|
263
|
+
node_selected = find_deepest_symbol_node_at_pos(
|
|
263
264
|
self.modules[file_path].ir, position.line, position.character
|
|
264
265
|
)
|
|
265
266
|
value = self.get_node_info(node_selected) if node_selected else None
|
|
@@ -271,94 +272,73 @@ class JacLangServer(LanguageServer):
|
|
|
271
272
|
)
|
|
272
273
|
return None
|
|
273
274
|
|
|
274
|
-
def get_node_info(self, node: ast.
|
|
275
|
+
def get_node_info(self, node: ast.AstSymbolNode) -> Optional[str]:
|
|
275
276
|
"""Extract meaningful information from the AST node."""
|
|
276
277
|
try:
|
|
277
|
-
if isinstance(node, ast.
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
node_info = f"(ability) can {node.value}"
|
|
294
|
-
if decl_node.signature:
|
|
295
|
-
node_info += f" {decl_node.signature.unparse()}"
|
|
296
|
-
if decl_node.doc:
|
|
297
|
-
node_info += f"\n{decl_node.doc.lit_value}"
|
|
298
|
-
if decl_node.semstr:
|
|
299
|
-
node_info += f"\n{decl_node.semstr.lit_value}"
|
|
300
|
-
elif isinstance(decl_node, ast.Name):
|
|
301
|
-
if (
|
|
302
|
-
decl_node.parent
|
|
303
|
-
and isinstance(decl_node.parent, ast.SubNodeList)
|
|
304
|
-
and decl_node.parent.parent
|
|
305
|
-
and isinstance(decl_node.parent.parent, ast.Assignment)
|
|
306
|
-
and decl_node.parent.parent.type_tag
|
|
307
|
-
):
|
|
308
|
-
node_info = (
|
|
309
|
-
f"(variable) {decl_node.value}: "
|
|
310
|
-
f"{decl_node.parent.parent.type_tag.unparse()}"
|
|
311
|
-
)
|
|
312
|
-
if decl_node.parent.parent.semstr:
|
|
313
|
-
node_info += (
|
|
314
|
-
f"\n{decl_node.parent.parent.semstr.lit_value}"
|
|
315
|
-
)
|
|
316
|
-
else:
|
|
317
|
-
if decl_node.value in [
|
|
318
|
-
"str",
|
|
319
|
-
"int",
|
|
320
|
-
"float",
|
|
321
|
-
"bool",
|
|
322
|
-
"bytes",
|
|
323
|
-
"list",
|
|
324
|
-
"tuple",
|
|
325
|
-
"set",
|
|
326
|
-
"dict",
|
|
327
|
-
"type",
|
|
328
|
-
]:
|
|
329
|
-
node_info = f"({decl_node.value}) Built-in type"
|
|
330
|
-
else:
|
|
331
|
-
node_info = f"(variable) {decl_node.value}: None"
|
|
332
|
-
elif isinstance(decl_node, ast.HasVar):
|
|
333
|
-
if decl_node.type_tag:
|
|
334
|
-
node_info = f"(variable) {decl_node.name.value} {decl_node.type_tag.unparse()}"
|
|
335
|
-
else:
|
|
336
|
-
node_info = f"(variable) {decl_node.name.value}"
|
|
337
|
-
if decl_node.semstr:
|
|
338
|
-
node_info += f"\n{decl_node.semstr.lit_value}"
|
|
339
|
-
elif isinstance(decl_node, ast.ParamVar):
|
|
340
|
-
if decl_node.type_tag:
|
|
341
|
-
node_info = f"(parameter) {decl_node.name.value} {decl_node.type_tag.unparse()}"
|
|
342
|
-
else:
|
|
343
|
-
node_info = f"(parameter) {decl_node.name.value}"
|
|
344
|
-
if decl_node.semstr:
|
|
345
|
-
node_info += f"\n{decl_node.semstr.lit_value}"
|
|
346
|
-
elif isinstance(decl_node, ast.ModuleItem):
|
|
347
|
-
node_info = (
|
|
348
|
-
f"(ModuleItem) {node.value}" # TODO: Add more info
|
|
349
|
-
)
|
|
350
|
-
else:
|
|
351
|
-
node_info = f"{node.value}"
|
|
352
|
-
else:
|
|
353
|
-
node_info = f"{node.value}" # non symbol node
|
|
354
|
-
else:
|
|
355
|
-
return None
|
|
356
|
-
else:
|
|
357
|
-
return None
|
|
278
|
+
if isinstance(node, ast.NameSpec):
|
|
279
|
+
node = node.name_of
|
|
280
|
+
access = node.sym_link.access.value + " " if node.sym_link else None
|
|
281
|
+
node_info = (
|
|
282
|
+
f"({access if access else ''}{node.sym_type.value}) {node.sym_name}"
|
|
283
|
+
)
|
|
284
|
+
if node.sym_info.clean_type:
|
|
285
|
+
node_info += f": {node.sym_info.clean_type}"
|
|
286
|
+
if isinstance(node, ast.AstSemStrNode) and node.semstr:
|
|
287
|
+
node_info += f"\n{node.semstr.value}"
|
|
288
|
+
if isinstance(node, ast.AstDocNode) and node.doc:
|
|
289
|
+
node_info += f"\n{node.doc.value}"
|
|
290
|
+
if isinstance(node, ast.Ability) and node.signature:
|
|
291
|
+
node_info += f"\n{node.signature.unparse()}"
|
|
292
|
+
self.log_py(node.pp())
|
|
293
|
+
self.log_py(f"mypy_node: {node.gen.mypy_ast}")
|
|
358
294
|
except AttributeError as e:
|
|
359
295
|
self.log_warning(f"Attribute error when accessing node attributes: {e}")
|
|
360
296
|
return node_info.strip()
|
|
361
297
|
|
|
298
|
+
def get_document_symbols(self, file_path: str) -> list[lspt.DocumentSymbol]:
|
|
299
|
+
"""Return document symbols for a file."""
|
|
300
|
+
root_node = self.modules[file_path].ir.sym_tab
|
|
301
|
+
if root_node:
|
|
302
|
+
return collect_symbols(root_node)
|
|
303
|
+
return []
|
|
304
|
+
|
|
305
|
+
def get_definition(
|
|
306
|
+
self, file_path: str, position: lspt.Position
|
|
307
|
+
) -> Optional[lspt.Location]:
|
|
308
|
+
"""Return definition location for a file."""
|
|
309
|
+
node_selected: Optional[ast.AstSymbolNode] = find_deepest_symbol_node_at_pos(
|
|
310
|
+
self.modules[file_path].ir, position.line, position.character
|
|
311
|
+
)
|
|
312
|
+
if node_selected:
|
|
313
|
+
if isinstance(node_selected, (ast.ElementStmt, ast.BuiltinType)):
|
|
314
|
+
return None
|
|
315
|
+
decl_node = (
|
|
316
|
+
node_selected.parent.body.target
|
|
317
|
+
if node_selected.parent
|
|
318
|
+
and isinstance(node_selected.parent, ast.AstImplNeedingNode)
|
|
319
|
+
and isinstance(node_selected.parent.body, ast.AstImplOnlyNode)
|
|
320
|
+
else (
|
|
321
|
+
node_selected.sym_link.decl
|
|
322
|
+
if (node_selected.sym_link and node_selected.sym_link.decl)
|
|
323
|
+
else node_selected
|
|
324
|
+
)
|
|
325
|
+
)
|
|
326
|
+
self.log_py(f"{node_selected}, {decl_node}")
|
|
327
|
+
decl_uri = uris.from_fs_path(decl_node.loc.mod_path)
|
|
328
|
+
try:
|
|
329
|
+
decl_range = create_range(decl_node.loc)
|
|
330
|
+
except ValueError: # 'print' name has decl in 0,0,0,0
|
|
331
|
+
return None
|
|
332
|
+
decl_location = lspt.Location(
|
|
333
|
+
uri=decl_uri,
|
|
334
|
+
range=decl_range,
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
return decl_location
|
|
338
|
+
else:
|
|
339
|
+
self.log_info("No declaration found for the selected node.")
|
|
340
|
+
return None
|
|
341
|
+
|
|
362
342
|
def log_error(self, message: str) -> None:
|
|
363
343
|
"""Log an error message."""
|
|
364
344
|
self.show_message_log(message, lspt.MessageType.Error)
|
|
@@ -373,3 +353,7 @@ class JacLangServer(LanguageServer):
|
|
|
373
353
|
"""Log an info message."""
|
|
374
354
|
self.show_message_log(message, lspt.MessageType.Info)
|
|
375
355
|
self.show_message(message, lspt.MessageType.Info)
|
|
356
|
+
|
|
357
|
+
def log_py(self, message: str) -> None:
|
|
358
|
+
"""Log a message."""
|
|
359
|
+
logging.info(message)
|
jaclang/langserve/server.py
CHANGED
|
@@ -15,17 +15,19 @@ analysis_thread: Optional[threading.Thread] = None
|
|
|
15
15
|
analysis_stop_event = threading.Event()
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
def analyze_and_publish(ls: JacLangServer, uri: str) -> None:
|
|
18
|
+
def analyze_and_publish(ls: JacLangServer, uri: str, level: int = 2) -> None:
|
|
19
19
|
"""Analyze and publish diagnostics."""
|
|
20
20
|
global analysis_thread, analysis_stop_event
|
|
21
21
|
|
|
22
22
|
def run_analysis() -> None:
|
|
23
23
|
ls.quick_check(uri)
|
|
24
24
|
ls.push_diagnostics(uri)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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)
|
|
29
31
|
|
|
30
32
|
analysis_thread = threading.Thread(target=run_analysis)
|
|
31
33
|
analysis_thread.start()
|
|
@@ -137,6 +139,26 @@ def hover(
|
|
|
137
139
|
return ls.get_hover_info(params.text_document.uri, params.position)
|
|
138
140
|
|
|
139
141
|
|
|
142
|
+
@server.feature(lspt.TEXT_DOCUMENT_DOCUMENT_SYMBOL)
|
|
143
|
+
async def document_symbol(
|
|
144
|
+
ls: JacLangServer, params: lspt.DocumentSymbolParams
|
|
145
|
+
) -> list[lspt.DocumentSymbol]:
|
|
146
|
+
"""Provide document symbols."""
|
|
147
|
+
stop_analysis()
|
|
148
|
+
analyze_and_publish(ls, params.text_document.uri)
|
|
149
|
+
return ls.get_document_symbols(params.text_document.uri)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@server.feature(lspt.TEXT_DOCUMENT_DEFINITION)
|
|
153
|
+
async def definition(
|
|
154
|
+
ls: JacLangServer, params: lspt.TextDocumentPositionParams
|
|
155
|
+
) -> Optional[lspt.Location]:
|
|
156
|
+
"""Provide definition."""
|
|
157
|
+
stop_analysis()
|
|
158
|
+
analyze_and_publish(ls, params.text_document.uri, level=1)
|
|
159
|
+
return ls.get_definition(params.text_document.uri, params.position)
|
|
160
|
+
|
|
161
|
+
|
|
140
162
|
def run_lang_server() -> None:
|
|
141
163
|
"""Run the language server."""
|
|
142
164
|
server.start_io()
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""Enum for shape types"""
|
|
2
2
|
|
|
3
3
|
:enum:ShapeType {
|
|
4
|
-
CIRCLE="Circle",
|
|
5
|
-
UNKNOWN="Unknown"
|
|
4
|
+
CIRCLE = "Circle",
|
|
5
|
+
UNKNOWN = "Unknown"
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
"""Function to calculate the area of a circle."""
|
|
@@ -23,6 +23,10 @@
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
:can:main_run {
|
|
26
|
-
print(
|
|
27
|
-
|
|
26
|
+
print(
|
|
27
|
+
f"Area of a circle with radius {RAD} using function: {calculate_area(RAD)}"
|
|
28
|
+
);
|
|
29
|
+
print(
|
|
30
|
+
f"Area of a {c.shape_type.value} with radius {RAD} using class: {c.area()}"
|
|
31
|
+
);
|
|
28
32
|
}
|
|
@@ -11,7 +11,7 @@ can calculate_area(radius: float) -> float;
|
|
|
11
11
|
can main_run;
|
|
12
12
|
|
|
13
13
|
"""Base class for a shape."""
|
|
14
|
-
obj Shape {
|
|
14
|
+
obj : priv Shape {
|
|
15
15
|
has shape_type: ShapeType;
|
|
16
16
|
|
|
17
17
|
can area -> float abs;
|
|
@@ -22,7 +22,7 @@ obj Circle :Shape: {
|
|
|
22
22
|
has radius: float;
|
|
23
23
|
|
|
24
24
|
can init(radius: float);
|
|
25
|
-
can area -> float;
|
|
25
|
+
override can area -> float;
|
|
26
26
|
}
|
|
27
27
|
# Radius of the demo circle
|
|
28
28
|
|
|
@@ -4,6 +4,8 @@ from jaclang.vendor.pygls.workspace import Workspace
|
|
|
4
4
|
from jaclang.langserve.engine import JacLangServer
|
|
5
5
|
from .session import LspSession
|
|
6
6
|
|
|
7
|
+
import lsprotocol.types as lspt
|
|
8
|
+
|
|
7
9
|
|
|
8
10
|
class TestJacLangServer(TestCase):
|
|
9
11
|
|
|
@@ -66,3 +68,124 @@ class TestJacLangServer(TestCase):
|
|
|
66
68
|
lsp.type_check(circle_file)
|
|
67
69
|
self.assertEqual(len(lsp.modules), 1)
|
|
68
70
|
self.assertEqual(lsp.modules[circle_file].diagnostics[0].range.start.line, 22)
|
|
71
|
+
|
|
72
|
+
def test_impl_stay_connected(self) -> None:
|
|
73
|
+
"""Test that the server doesn't run if there is a syntax error."""
|
|
74
|
+
lsp = JacLangServer()
|
|
75
|
+
# Set up the workspace path to "fixtures/"
|
|
76
|
+
workspace_path = self.fixture_abs_path("")
|
|
77
|
+
workspace = Workspace(workspace_path, lsp)
|
|
78
|
+
lsp.lsp._workspace = workspace
|
|
79
|
+
circle_file = uris.from_fs_path(self.fixture_abs_path("circle_pure.jac"))
|
|
80
|
+
circle_impl_file = uris.from_fs_path(
|
|
81
|
+
self.fixture_abs_path("circle_pure.impl.jac")
|
|
82
|
+
)
|
|
83
|
+
lsp.quick_check(circle_file)
|
|
84
|
+
lsp.deep_check(circle_file)
|
|
85
|
+
lsp.type_check(circle_file)
|
|
86
|
+
pos = lspt.Position(20, 8)
|
|
87
|
+
self.assertIn(
|
|
88
|
+
"Circle class inherits from Shape.",
|
|
89
|
+
lsp.get_hover_info(circle_file, pos).contents.value,
|
|
90
|
+
)
|
|
91
|
+
lsp.type_check(circle_impl_file, force=True)
|
|
92
|
+
pos = lspt.Position(8, 11)
|
|
93
|
+
self.assertIn(
|
|
94
|
+
"ability) calculate_area: float",
|
|
95
|
+
lsp.get_hover_info(circle_impl_file, pos).contents.value,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
def test_impl_auto_discover(self) -> None:
|
|
99
|
+
"""Test that the server doesn't run if there is a syntax error."""
|
|
100
|
+
lsp = JacLangServer()
|
|
101
|
+
# Set up the workspace path to "fixtures/"
|
|
102
|
+
workspace_path = self.fixture_abs_path("")
|
|
103
|
+
workspace = Workspace(workspace_path, lsp)
|
|
104
|
+
lsp.lsp._workspace = workspace
|
|
105
|
+
circle_impl_file = uris.from_fs_path(
|
|
106
|
+
self.fixture_abs_path("circle_pure.impl.jac")
|
|
107
|
+
)
|
|
108
|
+
lsp.quick_check(circle_impl_file, force=True)
|
|
109
|
+
lsp.deep_check(circle_impl_file, force=True)
|
|
110
|
+
lsp.type_check(circle_impl_file, force=True)
|
|
111
|
+
pos = lspt.Position(8, 11)
|
|
112
|
+
self.assertIn(
|
|
113
|
+
"ability) calculate_area: float",
|
|
114
|
+
lsp.get_hover_info(circle_impl_file, pos).contents.value,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
def test_show_type_impl(self) -> None:
|
|
118
|
+
"""Test that the server doesn't run if there is a syntax error."""
|
|
119
|
+
lsp = JacLangServer()
|
|
120
|
+
# Set up the workspace path to "fixtures/"
|
|
121
|
+
workspace_path = self.fixture_abs_path("")
|
|
122
|
+
workspace = Workspace(workspace_path, lsp)
|
|
123
|
+
lsp.lsp._workspace = workspace
|
|
124
|
+
target = uris.from_fs_path(
|
|
125
|
+
self.fixture_abs_path("../../../../examples/guess_game/guess_game4.jac")
|
|
126
|
+
)
|
|
127
|
+
lsp.quick_check(target)
|
|
128
|
+
lsp.deep_check(target)
|
|
129
|
+
lsp.type_check(target)
|
|
130
|
+
pos = lspt.Position(43, 18)
|
|
131
|
+
self.assertIn(
|
|
132
|
+
"attempts: int",
|
|
133
|
+
lsp.get_hover_info(target, pos).contents.value,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
def test_outline_symbols(self) -> None:
|
|
137
|
+
"""Test that the outline symbols are correct."""
|
|
138
|
+
lsp = JacLangServer()
|
|
139
|
+
workspace_path = self.fixture_abs_path("")
|
|
140
|
+
workspace = Workspace(workspace_path, lsp)
|
|
141
|
+
lsp.lsp._workspace = workspace
|
|
142
|
+
circle_file = uris.from_fs_path(self.fixture_abs_path("circle_pure.jac"))
|
|
143
|
+
lsp.quick_check(circle_file)
|
|
144
|
+
lsp.deep_check(circle_file)
|
|
145
|
+
lsp.type_check(circle_file)
|
|
146
|
+
expected_string = (
|
|
147
|
+
"DocumentSymbol(name='calculate_area', kind=<SymbolKind.Function: 12>, range=9:0-9:43, "
|
|
148
|
+
"selection_range=9:0-9:43, detail=None, tags=None, deprecated=None, children=["
|
|
149
|
+
"DocumentSymbol(name='radius', kind=<SymbolKind.Variable: 13>, range=9:1-9:14, "
|
|
150
|
+
"selection_range=9:1-9:14, detail=None, tags=None, deprecated=None, children=[])])"
|
|
151
|
+
)
|
|
152
|
+
self.assertEqual(
|
|
153
|
+
expected_string, str((lsp.get_document_symbols(circle_file))[6])
|
|
154
|
+
)
|
|
155
|
+
self.assertEqual(10, len(lsp.get_document_symbols(circle_file)))
|
|
156
|
+
|
|
157
|
+
def test_go_to_definition(self) -> None:
|
|
158
|
+
"""Test that the go to definition is correct."""
|
|
159
|
+
lsp = JacLangServer()
|
|
160
|
+
workspace_path = self.fixture_abs_path("")
|
|
161
|
+
workspace = Workspace(workspace_path, lsp)
|
|
162
|
+
lsp.lsp._workspace = workspace
|
|
163
|
+
circle_file = uris.from_fs_path(self.fixture_abs_path("circle_pure.jac"))
|
|
164
|
+
lsp.quick_check(circle_file)
|
|
165
|
+
lsp.deep_check(circle_file)
|
|
166
|
+
lsp.type_check(circle_file)
|
|
167
|
+
self.assertIn(
|
|
168
|
+
"fixtures/circle_pure.impl.jac:8:0-8:19",
|
|
169
|
+
str(lsp.get_definition(circle_file, lspt.Position(9, 16))),
|
|
170
|
+
)
|
|
171
|
+
self.assertIn(
|
|
172
|
+
"fixtures/circle_pure.jac:12:0-17:1",
|
|
173
|
+
str(lsp.get_definition(circle_file, lspt.Position(20, 17))),
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
def test_go_to_definition_method(self) -> None:
|
|
177
|
+
"""Test that the go to definition is correct."""
|
|
178
|
+
lsp = JacLangServer()
|
|
179
|
+
workspace_path = self.fixture_abs_path("")
|
|
180
|
+
workspace = Workspace(workspace_path, lsp)
|
|
181
|
+
lsp.lsp._workspace = workspace
|
|
182
|
+
guess_game_file = uris.from_fs_path(
|
|
183
|
+
self.fixture_abs_path("../../../../examples/guess_game/guess_game4.jac")
|
|
184
|
+
)
|
|
185
|
+
lsp.quick_check(guess_game_file)
|
|
186
|
+
lsp.deep_check(guess_game_file)
|
|
187
|
+
lsp.type_check(guess_game_file)
|
|
188
|
+
self.assertIn(
|
|
189
|
+
"guess_game4.jac:27:4-27:34",
|
|
190
|
+
str(lsp.get_definition(guess_game_file, lspt.Position(46, 45))),
|
|
191
|
+
)
|