jaclang 0.7.1__py3-none-any.whl → 0.7.5__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.

Files changed (85) hide show
  1. jaclang/cli/cli.py +2 -2
  2. jaclang/compiler/absyntree.py +378 -277
  3. jaclang/compiler/codeloc.py +2 -2
  4. jaclang/compiler/constant.py +2 -0
  5. jaclang/compiler/jac.lark +25 -19
  6. jaclang/compiler/parser.py +115 -92
  7. jaclang/compiler/passes/main/access_modifier_pass.py +15 -9
  8. jaclang/compiler/passes/main/def_impl_match_pass.py +29 -11
  9. jaclang/compiler/passes/main/def_use_pass.py +48 -17
  10. jaclang/compiler/passes/main/fuse_typeinfo_pass.py +49 -30
  11. jaclang/compiler/passes/main/import_pass.py +12 -7
  12. jaclang/compiler/passes/main/pyast_gen_pass.py +110 -47
  13. jaclang/compiler/passes/main/pyast_load_pass.py +49 -13
  14. jaclang/compiler/passes/main/pyjac_ast_link_pass.py +25 -11
  15. jaclang/compiler/passes/main/pyout_pass.py +3 -1
  16. jaclang/compiler/passes/main/registry_pass.py +6 -6
  17. jaclang/compiler/passes/main/sym_tab_build_pass.py +30 -72
  18. jaclang/compiler/passes/main/tests/test_decl_def_match_pass.py +21 -4
  19. jaclang/compiler/passes/main/tests/test_def_use_pass.py +5 -10
  20. jaclang/compiler/passes/main/tests/test_import_pass.py +8 -0
  21. jaclang/compiler/passes/main/tests/test_type_check_pass.py +1 -1
  22. jaclang/compiler/passes/main/type_check_pass.py +2 -1
  23. jaclang/compiler/passes/tool/jac_formatter_pass.py +44 -11
  24. jaclang/compiler/passes/tool/tests/fixtures/corelib.jac +16 -0
  25. jaclang/compiler/passes/tool/tests/fixtures/corelib_fmt.jac +16 -0
  26. jaclang/compiler/passes/tool/tests/fixtures/doc_string.jac +15 -0
  27. jaclang/compiler/passes/tool/tests/test_jac_format_pass.py +7 -5
  28. jaclang/compiler/passes/tool/tests/test_unparse_validate.py +1 -2
  29. jaclang/compiler/passes/transform.py +2 -4
  30. jaclang/{core/registry.py → compiler/semtable.py} +1 -3
  31. jaclang/compiler/symtable.py +39 -31
  32. jaclang/compiler/tests/test_parser.py +2 -2
  33. jaclang/core/aott.py +112 -16
  34. jaclang/core/{construct.py → architype.py} +44 -93
  35. jaclang/core/constructs.py +44 -0
  36. jaclang/core/context.py +157 -0
  37. jaclang/core/importer.py +18 -9
  38. jaclang/core/llms/anthropic.py +31 -2
  39. jaclang/core/llms/base.py +3 -3
  40. jaclang/core/llms/groq.py +4 -1
  41. jaclang/core/llms/huggingface.py +4 -1
  42. jaclang/core/llms/ollama.py +4 -1
  43. jaclang/core/llms/openai.py +6 -2
  44. jaclang/core/llms/togetherai.py +4 -1
  45. jaclang/core/memory.py +53 -2
  46. jaclang/core/test.py +90 -0
  47. jaclang/core/utils.py +2 -2
  48. jaclang/langserve/engine.py +119 -122
  49. jaclang/langserve/server.py +27 -5
  50. jaclang/langserve/tests/fixtures/circle.jac +16 -12
  51. jaclang/langserve/tests/fixtures/circle_err.jac +3 -3
  52. jaclang/langserve/tests/fixtures/circle_pure.impl.jac +8 -4
  53. jaclang/langserve/tests/fixtures/circle_pure.jac +2 -2
  54. jaclang/langserve/tests/test_server.py +114 -0
  55. jaclang/langserve/utils.py +104 -10
  56. jaclang/plugin/builtin.py +1 -1
  57. jaclang/plugin/default.py +46 -90
  58. jaclang/plugin/feature.py +32 -16
  59. jaclang/plugin/spec.py +17 -19
  60. jaclang/plugin/tests/test_features.py +0 -33
  61. jaclang/settings.py +4 -0
  62. jaclang/tests/fixtures/abc.jac +16 -12
  63. jaclang/tests/fixtures/byllmissue.jac +12 -0
  64. jaclang/tests/fixtures/edgetypetest.jac +16 -0
  65. jaclang/tests/fixtures/hash_init_check.jac +17 -0
  66. jaclang/tests/fixtures/impl_match_confused.impl.jac +1 -0
  67. jaclang/tests/fixtures/impl_match_confused.jac +5 -0
  68. jaclang/tests/fixtures/math_question.jpg +0 -0
  69. jaclang/tests/fixtures/maxfail_run_test.jac +17 -5
  70. jaclang/tests/fixtures/nosigself.jac +19 -0
  71. jaclang/tests/fixtures/run_test.jac +17 -5
  72. jaclang/tests/fixtures/walker_override.jac +21 -0
  73. jaclang/tests/fixtures/with_llm_vision.jac +25 -0
  74. jaclang/tests/test_bugs.py +19 -0
  75. jaclang/tests/test_cli.py +1 -1
  76. jaclang/tests/test_language.py +116 -11
  77. jaclang/tests/test_reference.py +1 -1
  78. jaclang/utils/lang_tools.py +5 -4
  79. jaclang/utils/test.py +2 -1
  80. jaclang/utils/treeprinter.py +35 -4
  81. {jaclang-0.7.1.dist-info → jaclang-0.7.5.dist-info}/METADATA +3 -2
  82. {jaclang-0.7.1.dist-info → jaclang-0.7.5.dist-info}/RECORD +84 -71
  83. jaclang/core/shelve_storage.py +0 -55
  84. {jaclang-0.7.1.dist-info → jaclang-0.7.5.dist-info}/WHEEL +0 -0
  85. {jaclang-0.7.1.dist-info → jaclang-0.7.5.dist-info}/entry_points.txt +0 -0
@@ -2,9 +2,10 @@
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
- from typing import Optional, Sequence
8
+ from typing import Optional
8
9
 
9
10
 
10
11
  import jaclang.compiler.absyntree as ast
@@ -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 find_deepest_node_at_pos
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
 
@@ -35,8 +40,8 @@ class ModuleInfo:
35
40
  def __init__(
36
41
  self,
37
42
  ir: ast.Module,
38
- errors: Sequence[Alert],
39
- warnings: Sequence[Alert],
43
+ errors: list[Alert],
44
+ warnings: list[Alert],
40
45
  alev: ALev,
41
46
  parent: Optional[ModuleInfo] = None,
42
47
  ) -> None:
@@ -58,35 +63,26 @@ class ModuleInfo:
58
63
  """Return if there are syntax errors."""
59
64
  return len(self.errors) > 0 and self.alev == ALev.QUICK
60
65
 
66
+ def update_with(self, new_info: ModuleInfo) -> None:
67
+ """Update module info."""
68
+ self.ir = new_info.ir
69
+ self.errors += [i for i in new_info.errors if i not in self.errors]
70
+ self.warnings += [i for i in new_info.warnings if i not in self.warnings]
71
+ self.alev = new_info.alev
72
+ self.diagnostics = self.gen_diagnostics()
73
+
61
74
  def gen_diagnostics(self) -> list[lspt.Diagnostic]:
62
75
  """Return diagnostics."""
63
76
  return [
64
77
  lspt.Diagnostic(
65
- range=lspt.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
- ),
78
+ range=create_range(error.loc),
74
79
  message=error.msg,
75
80
  severity=lspt.DiagnosticSeverity.Error,
76
81
  )
77
82
  for error in self.errors
78
83
  ] + [
79
84
  lspt.Diagnostic(
80
- range=lspt.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
- ),
85
+ range=create_range(warning.loc),
90
86
  message=warning.msg,
91
87
  severity=lspt.DiagnosticSeverity.Warning,
92
88
  )
@@ -104,7 +100,7 @@ class JacLangServer(LanguageServer):
104
100
 
105
101
  def module_not_diff(self, uri: str, alev: ALev) -> bool:
106
102
  """Check if module was changed."""
107
- doc = self.workspace.get_document(uri)
103
+ doc = self.workspace.get_text_document(uri)
108
104
  return (
109
105
  doc.uri in self.modules
110
106
  and self.modules[doc.uri].ir.source.hash
@@ -125,17 +121,25 @@ class JacLangServer(LanguageServer):
125
121
 
126
122
  def unwind_to_parent(self, file_path: str) -> str:
127
123
  """Unwind to parent."""
124
+ orig_file_path = file_path
128
125
  if file_path in self.modules:
129
126
  while cur := self.modules[file_path].parent:
130
127
  file_path = cur.uri
128
+ if file_path == orig_file_path and (
129
+ discover := self.modules[file_path].ir.annexable_by
130
+ ):
131
+ file_path = uris.from_fs_path(discover)
132
+ self.quick_check(file_path)
131
133
  return file_path
132
134
 
133
- def update_modules(self, file_path: str, build: Pass, alev: ALev) -> None:
135
+ def update_modules(
136
+ self, file_path: str, build: Pass, alev: ALev, refresh: bool = False
137
+ ) -> None:
134
138
  """Update modules."""
135
139
  if not isinstance(build.ir, ast.Module):
136
140
  self.log_error("Error with module build.")
137
141
  return
138
- self.modules[file_path] = ModuleInfo(
142
+ new_mod = ModuleInfo(
139
143
  ir=build.ir,
140
144
  errors=[
141
145
  i
@@ -149,33 +153,44 @@ class JacLangServer(LanguageServer):
149
153
  ],
150
154
  alev=alev,
151
155
  )
156
+ if not refresh and file_path in self.modules:
157
+ self.modules[file_path].update_with(new_mod)
158
+ else:
159
+ self.modules[file_path] = new_mod
152
160
  for p in build.ir.mod_deps.keys():
153
161
  uri = uris.from_fs_path(p)
154
- self.modules[uri] = ModuleInfo(
162
+ new_mod = ModuleInfo(
155
163
  ir=build.ir.mod_deps[p],
156
164
  errors=[i for i in build.errors_had if i.loc.mod_path == p],
157
165
  warnings=[i for i in build.warnings_had if i.loc.mod_path == p],
158
166
  alev=alev,
159
167
  )
168
+ if not refresh and uri in self.modules:
169
+ self.modules[uri].update_with(new_mod)
170
+ else:
171
+ self.modules[uri] = new_mod
172
+ self.modules[uri].parent = (
173
+ self.modules[file_path] if file_path != uri else None
174
+ )
160
175
 
161
- def quick_check(self, file_path: str) -> None:
176
+ def quick_check(self, file_path: str, force: bool = False) -> None:
162
177
  """Rebuild a file."""
163
- if self.module_not_diff(file_path, ALev.QUICK):
178
+ if not force and self.module_not_diff(file_path, ALev.QUICK):
164
179
  return
165
180
  try:
166
- document = self.workspace.get_document(file_path)
181
+ document = self.workspace.get_text_document(file_path)
167
182
  build = jac_str_to_pass(
168
183
  jac_str=document.source, file_path=document.path, schedule=[]
169
184
  )
170
185
  except Exception as e:
171
186
  self.log_error(f"Error during syntax check: {e}")
172
- self.update_modules(file_path, build, ALev.QUICK)
187
+ self.update_modules(file_path, build, ALev.QUICK, refresh=True)
173
188
 
174
- def deep_check(self, file_path: str) -> None:
189
+ def deep_check(self, file_path: str, force: bool = False) -> None:
175
190
  """Rebuild a file and its dependencies."""
176
191
  if file_path in self.modules:
177
- self.quick_check(file_path)
178
- if self.module_not_diff(file_path, ALev.DEEP):
192
+ self.quick_check(file_path, force=force)
193
+ if not force and self.module_not_diff(file_path, ALev.DEEP):
179
194
  return
180
195
  try:
181
196
  file_path = self.unwind_to_parent(file_path)
@@ -184,11 +199,11 @@ class JacLangServer(LanguageServer):
184
199
  self.log_error(f"Error during syntax check: {e}")
185
200
  self.update_modules(file_path, build, ALev.DEEP)
186
201
 
187
- def type_check(self, file_path: str) -> None:
202
+ def type_check(self, file_path: str, force: bool = False) -> None:
188
203
  """Rebuild a file and its dependencies."""
189
204
  if file_path not in self.modules:
190
- self.deep_check(file_path)
191
- if self.module_not_diff(file_path, ALev.TYPE):
205
+ self.deep_check(file_path, force=force)
206
+ if not force and self.module_not_diff(file_path, ALev.TYPE):
192
207
  return
193
208
  try:
194
209
  file_path = self.unwind_to_parent(file_path)
@@ -204,7 +219,7 @@ class JacLangServer(LanguageServer):
204
219
  ) -> lspt.CompletionList:
205
220
  """Return completion for a file."""
206
221
  items = []
207
- document = self.workspace.get_document(file_path)
222
+ document = self.workspace.get_text_document(file_path)
208
223
  current_line = document.lines[position.line].strip()
209
224
  if current_line.endswith("hello."):
210
225
 
@@ -228,7 +243,7 @@ class JacLangServer(LanguageServer):
228
243
  def formatted_jac(self, file_path: str) -> list[lspt.TextEdit]:
229
244
  """Return formatted jac."""
230
245
  try:
231
- document = self.workspace.get_document(file_path)
246
+ document = self.workspace.get_text_document(file_path)
232
247
  format = jac_str_to_pass(
233
248
  jac_str=document.source,
234
249
  file_path=document.path,
@@ -259,7 +274,7 @@ class JacLangServer(LanguageServer):
259
274
  self, file_path: str, position: lspt.Position
260
275
  ) -> Optional[lspt.Hover]:
261
276
  """Return hover information for a file."""
262
- node_selected = find_deepest_node_at_pos(
277
+ node_selected = find_deepest_symbol_node_at_pos(
263
278
  self.modules[file_path].ir, position.line, position.character
264
279
  )
265
280
  value = self.get_node_info(node_selected) if node_selected else None
@@ -271,94 +286,72 @@ class JacLangServer(LanguageServer):
271
286
  )
272
287
  return None
273
288
 
274
- def get_node_info(self, node: ast.AstNode) -> Optional[str]:
289
+ def get_node_info(self, node: ast.AstSymbolNode) -> Optional[str]:
275
290
  """Extract meaningful information from the AST node."""
276
291
  try:
277
- if isinstance(node, ast.Token):
278
- if isinstance(node, ast.AstSymbolNode):
279
- if isinstance(node, ast.String):
280
- return None
281
- if node.sym_link and node.sym_link.decl:
282
- decl_node = node.sym_link.decl
283
- if isinstance(decl_node, ast.Architype):
284
- if decl_node.doc:
285
- node_info = f"({decl_node.arch_type.value}) {node.value} \n{decl_node.doc.lit_value}"
286
- else:
287
- node_info = (
288
- f"({decl_node.arch_type.value}) {node.value}"
289
- )
290
- if decl_node.semstr:
291
- node_info += f"\n{decl_node.semstr.lit_value}"
292
- elif isinstance(decl_node, ast.Ability):
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
292
+ if isinstance(node, ast.NameAtom):
293
+ node = node.name_of
294
+ access = node.sym.access.value + " " if node.sym else None
295
+ node_info = (
296
+ f"({access if access else ''}{node.sym_category.value}) {node.sym_name}"
297
+ )
298
+ if node.name_spec.clean_type:
299
+ node_info += f": {node.name_spec.clean_type}"
300
+ if isinstance(node, ast.AstSemStrNode) and node.semstr:
301
+ node_info += f"\n{node.semstr.value}"
302
+ if isinstance(node, ast.AstDocNode) and node.doc:
303
+ node_info += f"\n{node.doc.value}"
304
+ if isinstance(node, ast.Ability) and node.signature:
305
+ node_info += f"\n{node.signature.unparse()}"
306
+ self.log_py(f"mypy_node: {node.gen.mypy_ast}")
358
307
  except AttributeError as e:
359
308
  self.log_warning(f"Attribute error when accessing node attributes: {e}")
360
309
  return node_info.strip()
361
310
 
311
+ def get_document_symbols(self, file_path: str) -> list[lspt.DocumentSymbol]:
312
+ """Return document symbols for a file."""
313
+ root_node = self.modules[file_path].ir.sym_tab
314
+ if root_node:
315
+ return collect_symbols(root_node)
316
+ return []
317
+
318
+ def get_definition(
319
+ self, file_path: str, position: lspt.Position
320
+ ) -> Optional[lspt.Location]:
321
+ """Return definition location for a file."""
322
+ node_selected: Optional[ast.AstSymbolNode] = find_deepest_symbol_node_at_pos(
323
+ self.modules[file_path].ir, position.line, position.character
324
+ )
325
+ if node_selected:
326
+ if isinstance(node_selected, (ast.ElementStmt, ast.BuiltinType)):
327
+ return None
328
+ decl_node = (
329
+ node_selected.parent.body.target
330
+ if node_selected.parent
331
+ and isinstance(node_selected.parent, ast.AstImplNeedingNode)
332
+ and isinstance(node_selected.parent.body, ast.AstImplOnlyNode)
333
+ else (
334
+ node_selected.sym.decl
335
+ if (node_selected.sym and node_selected.sym.decl)
336
+ else node_selected
337
+ )
338
+ )
339
+ self.log_py(f"{node_selected}, {decl_node}")
340
+ decl_uri = uris.from_fs_path(decl_node.loc.mod_path)
341
+ try:
342
+ decl_range = create_range(decl_node.loc)
343
+ except ValueError: # 'print' name has decl in 0,0,0,0
344
+ return None
345
+ decl_location = lspt.Location(
346
+ uri=decl_uri,
347
+ range=decl_range,
348
+ )
349
+
350
+ return decl_location
351
+ else:
352
+ self.log_info("No declaration found for the selected node.")
353
+ return None
354
+
362
355
  def log_error(self, message: str) -> None:
363
356
  """Log an error message."""
364
357
  self.show_message_log(message, lspt.MessageType.Error)
@@ -373,3 +366,7 @@ class JacLangServer(LanguageServer):
373
366
  """Log an info message."""
374
367
  self.show_message_log(message, lspt.MessageType.Info)
375
368
  self.show_message(message, lspt.MessageType.Info)
369
+
370
+ def log_py(self, message: str) -> None:
371
+ """Log a message."""
372
+ logging.info(message)
@@ -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
- ls.deep_check(uri)
26
- ls.push_diagnostics(uri)
27
- ls.type_check(uri)
28
- 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)
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()
@@ -18,9 +18,9 @@ Below we have the demonstration of a class to calculate the area of a circle.
18
18
  *#
19
19
 
20
20
  """Enum for shape types"""
21
- enum ShapeType {
22
- CIRCLE="Circle",
23
- UNKNOWN="Unknown"
21
+ enum ShapeType {
22
+ CIRCLE = "Circle",
23
+ UNKNOWN = "Unknown"
24
24
  }
25
25
 
26
26
  """Base class for a shape."""
@@ -50,24 +50,28 @@ with entry {
50
50
  # Global also works here
51
51
 
52
52
  with entry:__main__ {
53
- # To run the program functionality
54
- print(f"Area of a circle with radius {RAD} using function: {calculate_area(RAD)}");
55
- print(f"Area of a {c.shape_type.value} with radius {RAD} using class: {c.area()}");
53
+ # To run the program functionality
54
+ print(
55
+ f"Area of a circle with radius {RAD} using function: {calculate_area(RAD)}"
56
+ );
57
+ print(
58
+ f"Area of a {c.shape_type.value} with radius {RAD} using class: {c.area()}"
59
+ );
56
60
  }
57
61
  # Unit Tests!
58
62
 
59
63
  glob expected_area = 78.53981633974483;
60
64
 
61
- test calc_area {
62
- check.assertAlmostEqual(calculate_area(RAD), expected_area);
65
+ test calc_area {
66
+ check assertAlmostEqual(calculate_area(RAD), expected_area);
63
67
  }
64
68
 
65
- test circle_area {
69
+ test circle_area {
66
70
  c = Circle(RAD);
67
- check.assertAlmostEqual(c.area(), expected_area);
71
+ check assertAlmostEqual(c.area(), expected_area);
68
72
  }
69
73
 
70
- test circle_type {
74
+ test circle_type {
71
75
  c = Circle(RAD);
72
- check.assertEqual(c.shape_type, ShapeType.CIRCLE);
76
+ check assertEqual(c.shape_type, ShapeType.CIRCLE);
73
77
  }
@@ -59,15 +59,15 @@ print(f"Area of a {c.shape_type.value} with radius {RAD} using class: {c.area()}
59
59
  glob expected_area = 78.53981633974483;
60
60
 
61
61
  test calc_area {
62
- check.assertAlmostEqual(calculate_area(RAD), expected_area);
62
+ check assertAlmostEqual(calculate_area(RAD), expected_area);
63
63
  }
64
64
 
65
65
  test circle_area {
66
66
  c = Circle(RAD);
67
- check.assertAlmostEqual(c.area(), expected_area);
67
+ check assertAlmostEqual(c.area(), expected_area);
68
68
  }
69
69
 
70
70
  test circle_type {
71
71
  c = Circle(RAD);
72
- check.assertEqual(c.shape_type, ShapeType.CIRCLE);
72
+ check assertEqual(c.shape_type, ShapeType.CIRCLE);
73
73
  }
@@ -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(f"Area of a circle with radius {RAD} using function: {calculate_area(RAD)}");
27
- print(f"Area of a {c.shape_type.value} with radius {RAD} using class: {c.area()}");
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,115 @@ 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
+ self.assertEqual(8, len(lsp.get_document_symbols(circle_file)))
147
+
148
+ def test_go_to_definition(self) -> None:
149
+ """Test that the go to definition is correct."""
150
+ lsp = JacLangServer()
151
+ workspace_path = self.fixture_abs_path("")
152
+ workspace = Workspace(workspace_path, lsp)
153
+ lsp.lsp._workspace = workspace
154
+ circle_file = uris.from_fs_path(self.fixture_abs_path("circle_pure.jac"))
155
+ lsp.quick_check(circle_file)
156
+ lsp.deep_check(circle_file)
157
+ lsp.type_check(circle_file)
158
+ self.assertIn(
159
+ "fixtures/circle_pure.impl.jac:8:0-8:19",
160
+ str(lsp.get_definition(circle_file, lspt.Position(9, 16))),
161
+ )
162
+ self.assertIn(
163
+ "fixtures/circle_pure.jac:13:11-13:16",
164
+ str(lsp.get_definition(circle_file, lspt.Position(20, 17))),
165
+ )
166
+
167
+ def test_go_to_definition_method(self) -> None:
168
+ """Test that the go to definition is correct."""
169
+ lsp = JacLangServer()
170
+ workspace_path = self.fixture_abs_path("")
171
+ workspace = Workspace(workspace_path, lsp)
172
+ lsp.lsp._workspace = workspace
173
+ guess_game_file = uris.from_fs_path(
174
+ self.fixture_abs_path("../../../../examples/guess_game/guess_game4.jac")
175
+ )
176
+ lsp.quick_check(guess_game_file)
177
+ lsp.deep_check(guess_game_file)
178
+ lsp.type_check(guess_game_file)
179
+ self.assertIn(
180
+ "guess_game4.jac:27:8-27:21",
181
+ str(lsp.get_definition(guess_game_file, lspt.Position(46, 45))),
182
+ )