jaclang 0.7.11__py3-none-any.whl → 0.7.14__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of jaclang might be problematic. Click here for more details.

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