jaclang 0.8.5__py3-none-any.whl → 0.8.7__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 (70) hide show
  1. jaclang/cli/cli.md +4 -3
  2. jaclang/cli/cli.py +63 -29
  3. jaclang/cli/cmdreg.py +1 -140
  4. jaclang/compiler/passes/main/__init__.py +2 -0
  5. jaclang/compiler/passes/main/cfg_build_pass.py +21 -1
  6. jaclang/compiler/passes/main/inheritance_pass.py +8 -1
  7. jaclang/compiler/passes/main/pyast_gen_pass.py +70 -11
  8. jaclang/compiler/passes/main/pyast_load_pass.py +14 -20
  9. jaclang/compiler/passes/main/sym_tab_build_pass.py +4 -0
  10. jaclang/compiler/passes/main/tests/fixtures/cfg_has_var.jac +12 -0
  11. jaclang/compiler/passes/main/tests/fixtures/cfg_if_no_else.jac +11 -0
  12. jaclang/compiler/passes/main/tests/fixtures/cfg_return.jac +9 -0
  13. jaclang/compiler/passes/main/tests/fixtures/checker_binary_op.jac +21 -0
  14. jaclang/compiler/passes/main/tests/fixtures/checker_call_expr_class.jac +12 -0
  15. jaclang/compiler/passes/main/tests/fixtures/checker_cyclic_symbol.jac +4 -0
  16. jaclang/compiler/passes/main/tests/fixtures/checker_expr_call.jac +9 -0
  17. jaclang/compiler/passes/main/tests/fixtures/checker_import_missing_module.jac +13 -0
  18. jaclang/compiler/passes/main/tests/fixtures/checker_imported.jac +2 -0
  19. jaclang/compiler/passes/main/tests/fixtures/checker_importer.jac +6 -0
  20. jaclang/compiler/passes/main/tests/fixtures/checker_magic_call.jac +17 -0
  21. jaclang/compiler/passes/main/tests/fixtures/checker_mod_path.jac +8 -0
  22. jaclang/compiler/passes/main/tests/fixtures/data_spatial_types.jac +1 -1
  23. jaclang/compiler/passes/main/tests/fixtures/import_symbol_type_infer.jac +11 -0
  24. jaclang/compiler/passes/main/tests/fixtures/infer_type_assignment.jac +5 -0
  25. jaclang/compiler/passes/main/tests/fixtures/member_access_type_inferred.jac +13 -0
  26. jaclang/compiler/passes/main/tests/fixtures/member_access_type_resolve.jac +11 -0
  27. jaclang/compiler/passes/main/tests/fixtures/type_annotation_assignment.jac +8 -0
  28. jaclang/compiler/passes/main/tests/test_cfg_build_pass.py +62 -24
  29. jaclang/compiler/passes/main/tests/test_checker_pass.py +161 -0
  30. jaclang/compiler/passes/main/type_checker_pass.py +147 -0
  31. jaclang/compiler/passes/tool/tests/fixtures/simple_walk_fmt.jac +1 -4
  32. jaclang/compiler/program.py +17 -3
  33. jaclang/compiler/type_system/__init__.py +1 -0
  34. jaclang/compiler/type_system/operations.py +104 -0
  35. jaclang/compiler/type_system/type_evaluator.py +560 -0
  36. jaclang/compiler/type_system/type_utils.py +41 -0
  37. jaclang/compiler/type_system/types.py +240 -0
  38. jaclang/compiler/unitree.py +15 -9
  39. jaclang/langserve/dev_engine.jac +645 -0
  40. jaclang/langserve/dev_server.jac +201 -0
  41. jaclang/langserve/engine.jac +135 -91
  42. jaclang/langserve/server.jac +21 -14
  43. jaclang/langserve/tests/server_test/test_lang_serve.py +2 -5
  44. jaclang/langserve/tests/test_dev_server.py +80 -0
  45. jaclang/langserve/tests/test_server.py +9 -2
  46. jaclang/langserve/utils.jac +44 -48
  47. jaclang/runtimelib/builtin.py +28 -39
  48. jaclang/runtimelib/importer.py +1 -1
  49. jaclang/runtimelib/machine.py +48 -64
  50. jaclang/runtimelib/memory.py +23 -5
  51. jaclang/runtimelib/tests/fixtures/savable_object.jac +10 -2
  52. jaclang/runtimelib/utils.py +13 -6
  53. jaclang/tests/fixtures/edge_node_walk.jac +1 -1
  54. jaclang/tests/fixtures/edges_walk.jac +1 -1
  55. jaclang/tests/fixtures/gendot_bubble_sort.jac +1 -1
  56. jaclang/tests/fixtures/jac_run_py_bugs.py +18 -0
  57. jaclang/tests/fixtures/jac_run_py_import.py +13 -0
  58. jaclang/tests/fixtures/lambda_arg_annotation.jac +15 -0
  59. jaclang/tests/fixtures/lambda_self.jac +18 -0
  60. jaclang/tests/fixtures/py_run.jac +8 -0
  61. jaclang/tests/fixtures/py_run.py +23 -0
  62. jaclang/tests/fixtures/pyfunc.py +2 -0
  63. jaclang/tests/test_cli.py +103 -14
  64. jaclang/tests/test_language.py +10 -4
  65. jaclang/utils/lang_tools.py +3 -0
  66. jaclang/utils/module_resolver.py +1 -1
  67. {jaclang-0.8.5.dist-info → jaclang-0.8.7.dist-info}/METADATA +4 -2
  68. {jaclang-0.8.5.dist-info → jaclang-0.8.7.dist-info}/RECORD +70 -37
  69. {jaclang-0.8.5.dist-info → jaclang-0.8.7.dist-info}/WHEEL +1 -1
  70. {jaclang-0.8.5.dist-info → jaclang-0.8.7.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,201 @@
1
+ """Jaclang Language Server."""
2
+
3
+ import from typing { Optional }
4
+
5
+ import from jaclang.compiler.constant {
6
+ JacSemTokenModifier as SemTokMod,
7
+ JacSemTokenType as SemTokType
8
+ }
9
+
10
+ import from dev_engine { JacLangServer }
11
+ import from jaclang.settings { settings }
12
+
13
+ import lsprotocol.types as lspt;
14
+
15
+
16
+ with entry {
17
+ server = JacLangServer();
18
+ }
19
+
20
+
21
+ """Check syntax on change."""
22
+ @server.feature(lspt.TEXT_DOCUMENT_DID_OPEN)
23
+ async def did_open(ls: JacLangServerV2, params: lspt.DidOpenTextDocumentParams) -> None {
24
+ await ls.launch_deep_check(params.text_document.uri);
25
+ ls.lsp.send_request(lspt.WORKSPACE_SEMANTIC_TOKENS_REFRESH);
26
+ }
27
+
28
+
29
+ """Check syntax on change."""
30
+ @server.feature(lspt.TEXT_DOCUMENT_DID_SAVE)
31
+ async def did_save(ls: JacLangServerV2, params: lspt.DidOpenTextDocumentParams) -> None {
32
+ file_path = params.text_document.uri;
33
+ quick_check_passed = await ls.launch_quick_check(file_path);
34
+ if not quick_check_passed {
35
+ return;
36
+ }
37
+ await ls.launch_deep_check(file_path);
38
+ ls.lsp.send_request(lspt.WORKSPACE_SEMANTIC_TOKENS_REFRESH);
39
+ }
40
+
41
+
42
+ """Check syntax on change."""
43
+ @server.feature(lspt.TEXT_DOCUMENT_DID_CHANGE)
44
+ async def did_change(
45
+ ls: JacLangServerV2,
46
+ params: lspt.DidChangeTextDocumentParams
47
+ ) -> None {
48
+ file_path = params.text_document.uri;
49
+ quick_check_passed = await ls.launch_quick_check(file_path);
50
+
51
+ if quick_check_passed {
52
+ document = ls.workspace.get_text_document(file_path);
53
+ lines = document.source.splitlines();
54
+ sem_manager = ls.sem_managers[file_path.removeprefix('file://')];
55
+ sem_manager.update_sem_tokens(
56
+ params,sem_manager.sem_tokens,lines
57
+ );
58
+ ls.lsp.send_request(lspt.WORKSPACE_SEMANTIC_TOKENS_REFRESH);
59
+ await ls.launch_deep_check(file_path);
60
+ ls.lsp.send_request(lspt.WORKSPACE_SEMANTIC_TOKENS_REFRESH);
61
+ }
62
+ }
63
+
64
+
65
+ """Format the given document."""
66
+ @server.feature(lspt.TEXT_DOCUMENT_FORMATTING)
67
+ def formatting(
68
+ ls: JacLangServerV2,
69
+ params: lspt.DocumentFormattingParams
70
+ ) -> list[lspt.TextEdit] {
71
+ return ls.formatted_jac(params.text_document.uri);
72
+ }
73
+
74
+
75
+ """Check syntax on file creation."""
76
+ @server.feature(
77
+ lspt.WORKSPACE_DID_CREATE_FILES,
78
+ lspt.FileOperationRegistrationOptions(
79
+ filters=[lspt.FileOperationFilter(pattern=lspt.FileOperationPattern('**/*.jac'))]
80
+ )
81
+ )
82
+ def did_create_files(ls: JacLangServerV2, params: lspt.CreateFilesParams) -> None {}
83
+
84
+
85
+ """Check syntax on file rename."""
86
+ @server.feature(
87
+ lspt.WORKSPACE_DID_RENAME_FILES,
88
+ lspt.FileOperationRegistrationOptions(
89
+ filters=[lspt.FileOperationFilter(pattern=lspt.FileOperationPattern('**/*.jac'))]
90
+ )
91
+ )
92
+ def did_rename_files(ls: JacLangServerV2, params: lspt.RenameFilesParams) -> None {
93
+ new_uris = [ file.new_uri for file in params.files ];
94
+ old_uris = [ file.old_uri for file in params.files ];
95
+ for i in range(len(new_uris)) {
96
+ ls.rename_module(old_uris[i], new_uris[i]);
97
+ }
98
+ }
99
+
100
+
101
+ """Check syntax on file delete."""
102
+ @server.feature(
103
+ lspt.WORKSPACE_DID_DELETE_FILES,
104
+ lspt.FileOperationRegistrationOptions(
105
+ filters=[lspt.FileOperationFilter(pattern=lspt.FileOperationPattern('**/*.jac'))]
106
+ )
107
+ )
108
+ def did_delete_files(ls: JacLangServerV2, params: lspt.DeleteFilesParams) -> None {
109
+ for file in params.files {
110
+ ls.delete_module(file.uri);
111
+ }
112
+ }
113
+
114
+
115
+ """Provide completion."""
116
+ @server.feature(
117
+ lspt.TEXT_DOCUMENT_COMPLETION,
118
+ lspt.CompletionOptions(trigger_characters=['.', ':', 'a-zA-Z0-9'])
119
+ )
120
+ def completion(ls: JacLangServerV2, params: lspt.CompletionParams) -> lspt.CompletionList {
121
+ return ls.get_completion(
122
+ params.text_document.uri,
123
+ params.position,
124
+ params.context.trigger_character if params.context else None
125
+ );
126
+ }
127
+
128
+
129
+ """Provide hover information for the given hover request."""
130
+ @server.feature(lspt.TEXT_DOCUMENT_HOVER, lspt.HoverOptions(work_done_progress=True))
131
+ def hover(
132
+ ls: JacLangServerV2,
133
+ params: lspt.TextDocumentPositionParams
134
+ ) -> Optional[lspt.Hover] {
135
+ return ls.get_hover_info(params.text_document.uri, params.position);
136
+ }
137
+
138
+
139
+ """Provide document symbols."""
140
+ @server.feature(lspt.TEXT_DOCUMENT_DOCUMENT_SYMBOL)
141
+ def document_symbol(
142
+ ls: JacLangServerV2,
143
+ params: lspt.DocumentSymbolParams
144
+ ) -> list[lspt.DocumentSymbol] {
145
+ return ls.get_outline(params.text_document.uri);
146
+ }
147
+
148
+
149
+ """Provide definition."""
150
+ @server.feature(lspt.TEXT_DOCUMENT_DEFINITION)
151
+ def definition(
152
+ ls: JacLangServerV2,
153
+ params: lspt.TextDocumentPositionParams
154
+ ) -> Optional[lspt.Location] {
155
+ return ls.get_definition(params.text_document.uri, params.position);
156
+ }
157
+
158
+
159
+ """Provide references."""
160
+ @server.feature(lspt.TEXT_DOCUMENT_REFERENCES)
161
+ def references(ls: JacLangServerV2, params: lspt.ReferenceParams) -> list[lspt.Location] {
162
+ return ls.get_references(params.text_document.uri, params.position);
163
+ }
164
+
165
+
166
+ """Rename symbol."""
167
+ @server.feature(lspt.TEXT_DOCUMENT_RENAME)
168
+ def rename(ls: JacLangServerV2, params: lspt.RenameParams) -> Optional[lspt.WorkspaceEdit] {
169
+ ls.log_warning('Auto Rename is Experimental, Please use with caution.');
170
+ return ls.rename_symbol(params.text_document.uri, params.position, params.new_name);
171
+ }
172
+
173
+
174
+ """Provide semantic tokens."""
175
+ @server.feature(
176
+ lspt.TEXT_DOCUMENT_SEMANTIC_TOKENS_FULL,
177
+ lspt.SemanticTokensLegend(
178
+ token_types=SemTokType.as_str_list(),
179
+ token_modifiers=SemTokMod.as_str_list()
180
+ )
181
+ )
182
+ def semantic_tokens_full(
183
+ ls: JacLangServerV2,
184
+ params: lspt.SemanticTokensParams
185
+ ) -> lspt.SemanticTokens {
186
+ return ls.get_semantic_tokens(params.text_document.uri);
187
+ }
188
+
189
+
190
+ """Run the language server."""
191
+ def run_lang_server() -> None {
192
+ settings.pass_timer = True;
193
+ server.start_io();
194
+ }
195
+
196
+
197
+ with entry {
198
+ if __name__ == '__main__' {
199
+ run_lang_server();
200
+ }
201
+ }
@@ -10,6 +10,7 @@ import jaclang.compiler.unitree as uni;
10
10
  import from jaclang { JacMachineInterface as Jac }
11
11
  import from jaclang.compiler.constant {SymbolType}
12
12
  import from jaclang.compiler.program { JacProgram }
13
+ import from jaclang.compiler.type_system.types{ ClassType}
13
14
  import from jaclang.compiler.unitree { UniScopeNode }
14
15
  import from sem_manager { SemTokManager }
15
16
  import from jaclang.vendor.pygls { uris }
@@ -22,7 +23,7 @@ import utils;
22
23
  """Handles Jac module, semantic manager, and alert management."""
23
24
  class ModuleManager {
24
25
  """Initialize ModuleManager."""
25
- def init(self: ModuleManager, program: JacProgram, sem_managers: <>dict) -> None {
26
+ def init(self: ModuleManager, program: JacProgram, sem_managers: dict) -> None {
26
27
  self.program = program;
27
28
  self.sem_managers = sem_managers;
28
29
  }
@@ -34,7 +35,6 @@ class ModuleManager {
34
35
  build: uni.Module,
35
36
  update_annexed: bool = True
36
37
  ) -> None {
37
- file_path = file_path.removeprefix('file://');
38
38
  self.program.mod.hub[file_path] = build;
39
39
  if update_annexed {
40
40
  self.sem_managers[file_path] = SemTokManager(ir=build);
@@ -63,31 +63,32 @@ class JacLangServer(JacProgram , LanguageServer) {
63
63
  LanguageServer.init(self, 'jac-lsp', 'v0.1');
64
64
  JacProgram.init(self);
65
65
  self.executor = ThreadPoolExecutor();
66
- self.tasks: <>dict[(str, asyncio.Task)] = {};
67
- self.sem_managers: <>dict[(str, SemTokManager)] = {};
66
+ self.tasks: dict[(str, asyncio.Task)] = {};
67
+ self.sem_managers: dict[(str, SemTokManager)] = {};
68
68
  self.module_manager = ModuleManager(self, self.sem_managers);
69
+ self._quick_tasks: dict[str, asyncio.Task] = {};
70
+ self._deep_tasks: dict[str, asyncio.Task] = {};
69
71
  }
70
72
 
71
73
  """Return diagnostics for all files as a dict {uri: diagnostics}."""
72
74
  @ property
73
- def diagnostics(self: JacLangServer, ) -> <>dict[str, <>list] {
75
+ def diagnostics(self: JacLangServer, ) -> dict[str, list] {
74
76
  result = {};
75
77
  for file_path in self.mod.hub {
76
78
  uri = uris.from_fs_path(file_path);
77
79
  result[uri] =
78
- utils.gen_diagnostics(uri, self.errors_had, self.warnings_had);
80
+ utils.gen_diagnostics(file_path, self.errors_had, self.warnings_had);
79
81
  }
80
82
  return result;
81
83
  }
82
84
 
83
85
  """Remove errors and warnings for a specific file from the lists."""
84
- def _clear_alerts_for_file(self: JacLangServer, file_path_fs: str) -> None {
85
- self.module_manager.clear_alerts_for_file(file_path_fs);
86
+ def _clear_alerts_for_file(self: JacLangServer, file_path: str) -> None {
87
+ self.module_manager.clear_alerts_for_file(file_path);
86
88
  }
87
89
 
88
90
  """Get IR for a file path."""
89
91
  def get_ir(self: JacLangServer, file_path: str) -> Optional[uni.Module] {
90
- file_path = file_path.removeprefix('file://');
91
92
  return self.mod.hub.get(file_path);
92
93
  }
93
94
 
@@ -105,17 +106,19 @@ class JacLangServer(JacProgram , LanguageServer) {
105
106
  """Rebuild a file (syntax only)."""
106
107
  def quick_check(self: JacLangServer, file_path: str) -> bool {
107
108
  try {
108
- file_path_fs = file_path.removeprefix('file://');
109
109
  document = self.workspace.get_text_document(file_path);
110
- self._clear_alerts_for_file(file_path_fs);
111
- build = self.compile(use_str=document.source, file_path=document.path);
112
- self.update_modules(file_path_fs, build, need=False);
110
+ fs_path = document.path;
111
+
112
+ self._clear_alerts_for_file(fs_path);
113
+ build = self.compile(use_str=document.source, file_path=fs_path);
114
+ self.update_modules(fs_path, build, need=False);
115
+ # to display diagnostics, it needs URI starts with "file://"
113
116
  self.publish_diagnostics(
114
117
  file_path,
115
- utils.gen_diagnostics(file_path, self.errors_had, self.warnings_had)
118
+ utils.gen_diagnostics(fs_path, self.errors_had, self.warnings_had)
116
119
  );
117
120
  build_errors =
118
- [ e for e in self.errors_had if e.loc.mod_path == file_path_fs ];
121
+ [ e for e in self.errors_had if e.loc.mod_path == fs_path];
119
122
  return len(build_errors) == 0;
120
123
  } except Exception as e {
121
124
  self.log_error(f"'Error during syntax check: '{e}");
@@ -131,29 +134,31 @@ class JacLangServer(JacProgram , LanguageServer) {
131
134
  ) -> bool {
132
135
  try {
133
136
  start_time = time.time();
134
- file_path_fs = file_path.removeprefix('file://');
135
137
  document = self.workspace.get_text_document(file_path);
136
- self._clear_alerts_for_file(file_path_fs);
137
- build = self.build(use_str=document.source, file_path=document.path);
138
- self.update_modules(file_path_fs, build);
138
+ fs_path = document.path;
139
+
140
+ self._clear_alerts_for_file(fs_path);
141
+ build = self.compile(use_str=document.source, file_path=document.path,type_check=True);
142
+ self.update_modules(fs_path, build);
139
143
  if build.annexable_by {
140
144
  return self.deep_check(
141
145
  uris.from_fs_path(build.annexable_by),
142
- annex_view=file_path
146
+ annex_view=fs_path
143
147
  );
144
148
  }
145
149
  self.publish_diagnostics(
146
- annex_view if annex_view else file_path,
150
+ # to display diagnostic , it need URI starts with "file://"
151
+ uris.from_fs_path(annex_view) if annex_view else uris.from_fs_path(fs_path) ,
147
152
  utils.gen_diagnostics(
148
- annex_view if annex_view else file_path,
153
+ annex_view if annex_view else fs_path,
149
154
  self.errors_had,
150
155
  self.warnings_had
151
156
  )
152
157
  );
153
158
  if annex_view {
154
159
  self.publish_diagnostics(
155
- file_path,
156
- utils.gen_diagnostics(file_path, self.errors_had, self.warnings_had)
160
+ uris.from_fs_path(fs_path) ,
161
+ utils.gen_diagnostics(fs_path, self.errors_had, self.warnings_had)
157
162
  );
158
163
  }
159
164
  self.log_py(
@@ -166,34 +171,34 @@ class JacLangServer(JacProgram , LanguageServer) {
166
171
  }
167
172
  }
168
173
 
169
- """Analyze and publish diagnostics."""
170
- async def launch_quick_check(self: JacLangServer, uri: str) -> bool {
171
- return await asyncio.get_event_loop().run_in_executor(
172
- self.executor,
173
- self.quick_check,
174
- uri
175
- );
174
+ """Analyze and publish diagnostics with debouncing."""
175
+ async def launch_quick_check(self: JacLangServer, uri: str, delay: float = 0.5) -> bool {
176
+ try {
177
+ return await self._debounced_run(self.quick_check, uri, delay, self._quick_tasks);
178
+ } except asyncio.CancelledError {
179
+ return False;
180
+ } except Exception as e {
181
+ self.log_error(f" 'Error during quick check launch: ' {e} ");
182
+ return False;
183
+ }
176
184
  }
177
-
178
- """Analyze and publish diagnostics."""
179
- async def launch_deep_check(self: JacLangServer, uri: str) -> None {
180
- async def run_in_executor(
181
- func: Callable[([str, Optional[str]], bool)],
182
- file_path: str,
183
- annex_view: Optional[str] = None
184
- ) -> None {
185
- loop = asyncio.get_event_loop();
186
- await loop.run_in_executor(self.executor, func, file_path, annex_view);
187
- }
188
- if uri in self.tasks and not self.tasks[uri].done() {
189
- self.log_py(f"'Canceling '{uri}' deep check...'");
190
- self.tasks[uri].cancel();
191
- del (self.tasks[uri], ) ;
192
- }
193
- self.log_py(f"'Analyzing '{uri}'...'");
194
- task = asyncio.create_task(run_in_executor(self.deep_check, uri));
195
- self.tasks[uri] = task;
196
- await task;
185
+ """Analyze and publish diagnostics with debouncing."""
186
+ async def launch_deep_check(
187
+ self: JacLangServer,
188
+ uri: str,
189
+ delay: float = 1.0,
190
+ annex_view: Optional[str] = None
191
+ ) -> bool {
192
+ def deep_check_wrapper(file_uri: str) -> bool {
193
+ return self.deep_check(file_uri, annex_view);
194
+ }
195
+ try {
196
+ return await self._debounced_run(deep_check_wrapper, uri, delay, self._deep_tasks);
197
+ } except asyncio.CancelledError {
198
+ return False;
199
+ } except Exception as e {
200
+ self.log_error(f" 'Error during deep check launch: ' {e} ");
201
+ }
197
202
  }
198
203
 
199
204
  """Return completion for a file."""
@@ -205,7 +210,7 @@ class JacLangServer(JacProgram , LanguageServer) {
205
210
  ) -> lspt.CompletionList {
206
211
  try {
207
212
  document = self.workspace.get_text_document(file_path);
208
- mod_ir = self.get_ir(file_path);
213
+ mod_ir = self.get_ir(document.path);
209
214
  if not mod_ir {
210
215
  return lspt.CompletionList(is_incomplete=False, items=[]);
211
216
  }
@@ -334,7 +339,7 @@ class JacLangServer(JacProgram , LanguageServer) {
334
339
  }
335
340
 
336
341
  """Return formatted jac."""
337
- def formatted_jac(self: JacLangServer, file_path: str) -> <>list[lspt.TextEdit] {
342
+ def formatted_jac(self: JacLangServer, file_path: str) -> list[lspt.TextEdit] {
338
343
  try {
339
344
  document = self.workspace.get_text_document(file_path);
340
345
  formatted_text =
@@ -365,11 +370,12 @@ class JacLangServer(JacProgram , LanguageServer) {
365
370
  file_path: str,
366
371
  position: lspt.Position
367
372
  ) -> Optional[lspt.Hover] {
368
- file_path_fs = file_path.removeprefix('file://');
369
- if file_path_fs not in self.mod.hub {
373
+ fs_path = uris.to_fs_path(file_path);
374
+
375
+ if fs_path not in self.mod.hub {
370
376
  return None;
371
377
  }
372
- sem_mgr = self.sem_managers.get(file_path_fs);
378
+ sem_mgr = self.sem_managers.get(fs_path);
373
379
  if not sem_mgr {
374
380
  return None;
375
381
  }
@@ -392,22 +398,25 @@ class JacLangServer(JacProgram , LanguageServer) {
392
398
  }
393
399
 
394
400
  """Extract meaningful information from the AST node."""
395
- def get_node_info(self: JacLangServer, <>node: uni.AstSymbolNode) -> Optional[str] {
401
+ def get_node_info(self: JacLangServer, sym_node: uni.AstSymbolNode) -> Optional[str] {
396
402
  try {
397
- if isinstance(<>node, uni.NameAtom) {
398
- <>node = <>node.name_of;
403
+ if isinstance(sym_node, uni.NameAtom) {
404
+ sym_node = sym_node.name_of;
399
405
  }
400
- access = (<>node.sym.access.value + ' ') if <>node.sym else None;
406
+ access = (sym_node.sym.access.value + ' ') if sym_node.sym else None;
401
407
  node_info =
402
- f"'('{access if access else ''}{<>node.sym_category.value}') '{<>node.sym_name}";
403
- if <>node.name_spec.clean_type {
404
- node_info += f"': '{<>node.name_spec.clean_type}";
408
+ f"'('{access if access else ''}{sym_node.sym_category.value}') '{sym_node.sym_name}";
409
+ if sym_node.name_spec.clean_type {
410
+ node_info += f"': '{sym_node.name_spec.clean_type}";
411
+ }
412
+ if isinstance(sym_node,uni.AstSymbolNode) and isinstance(sym_node.name_spec.type,ClassType) {
413
+ node_info += f"': '{sym_node.name_spec.type.shared.class_name}";
405
414
  }
406
- if isinstance(<>node, uni.AstDocNode) and <>node.doc {
407
- node_info += f"'\n'{<>node.doc.value}";
415
+ if isinstance(sym_node, uni.AstDocNode) and sym_node.doc {
416
+ node_info += f"'\n'{sym_node.doc.value}";
408
417
  }
409
- if isinstance(<>node, uni.Ability) and <>node.signature {
410
- node_info += f"'\n'{<>node.signature.unparse()}";
418
+ if isinstance(sym_node, uni.Ability) and sym_node.signature {
419
+ node_info += f"'\n'{sym_node.signature.unparse()}";
411
420
  }
412
421
  } except AttributeError as e {
413
422
  self.log_warning(f"'Attribute error when accessing node attributes: '{e}");
@@ -416,10 +425,10 @@ class JacLangServer(JacProgram , LanguageServer) {
416
425
  }
417
426
 
418
427
  """Return document symbols for a file."""
419
- def get_outline(self: JacLangServer, file_path: str) -> <>list[lspt.DocumentSymbol] {
420
- file_path_fs = file_path.removeprefix('file://');
421
- if file_path_fs in self.mod.hub
422
- and (root_node := self.mod.hub[file_path_fs].sym_tab)
428
+ def get_outline(self: JacLangServer, file_path: str) -> list[lspt.DocumentSymbol] {
429
+ fs_path = uris.to_fs_path(file_path);
430
+ if fs_path in self.mod.hub
431
+ and (root_node := self.mod.hub[fs_path].sym_tab)
423
432
  {
424
433
  return utils.get_symbols_for_outline(root_node);
425
434
  }
@@ -432,11 +441,12 @@ class JacLangServer(JacProgram , LanguageServer) {
432
441
  file_path: str,
433
442
  position: lspt.Position
434
443
  ) -> Optional[lspt.Location] {
435
- file_path_fs = file_path.removeprefix('file://');
436
- if file_path_fs not in self.mod.hub {
444
+ fs_path = uris.to_fs_path(file_path);
445
+
446
+ if fs_path not in self.mod.hub {
437
447
  return None;
438
448
  }
439
- sem_mgr = self.sem_managers.get(file_path_fs);
449
+ sem_mgr = self.sem_managers.get(fs_path);
440
450
  if not sem_mgr {
441
451
  return None;
442
452
  }
@@ -543,12 +553,13 @@ class JacLangServer(JacProgram , LanguageServer) {
543
553
  self: JacLangServer,
544
554
  file_path: str,
545
555
  position: lspt.Position
546
- ) -> <>list[lspt.Location] {
547
- file_path_fs = file_path.removeprefix('file://');
548
- if file_path_fs not in self.mod.hub {
556
+ ) -> list[lspt.Location] {
557
+ fs_path = uris.to_fs_path(file_path);
558
+
559
+ if fs_path not in self.mod.hub {
549
560
  return [];
550
561
  }
551
- sem_mgr = self.sem_managers.get(file_path_fs);
562
+ sem_mgr = self.sem_managers.get(fs_path);
552
563
  if not sem_mgr {
553
564
  return [];
554
565
  }
@@ -559,11 +570,11 @@ class JacLangServer(JacProgram , LanguageServer) {
559
570
  }
560
571
  node_selected = sem_mgr.static_sem_tokens[index1][3];
561
572
  if node_selected and node_selected.sym {
562
- list_of_references: <>list[lspt.Location] =
573
+ list_of_references: list[lspt.Location] =
563
574
  [ lspt.Location(
564
- uri=uris.from_fs_path(<>node.loc.mod_path),
565
- range=utils.create_range(<>node.loc)
566
- ) for <>node in node_selected.sym.uses ];
575
+ uri=uris.from_fs_path(cur_node.loc.mod_path),
576
+ range=utils.create_range(cur_node.loc)
577
+ ) for cur_node in node_selected.sym.uses ];
567
578
  return list_of_references;
568
579
  }
569
580
  return [];
@@ -576,11 +587,11 @@ class JacLangServer(JacProgram , LanguageServer) {
576
587
  position: lspt.Position,
577
588
  new_name: str
578
589
  ) -> Optional[lspt.WorkspaceEdit] {
579
- file_path_fs = file_path.removeprefix('file://');
580
- if file_path_fs not in self.mod.hub {
590
+ fs_path = uris.to_fs_path(file_path);
591
+ if fs_path not in self.mod.hub {
581
592
  return None;
582
593
  }
583
- sem_mgr = self.sem_managers.get(file_path_fs);
594
+ sem_mgr = self.sem_managers.get(fs_path);
584
595
  if not sem_mgr {
585
596
  return None;
586
597
  }
@@ -591,12 +602,12 @@ class JacLangServer(JacProgram , LanguageServer) {
591
602
  }
592
603
  node_selected = sem_mgr.static_sem_tokens[index1][3];
593
604
  if node_selected and node_selected.sym {
594
- changes: <>dict[(str, <>list[lspt.TextEdit])] = {};
595
- for <>node in [*node_selected.sym.uses, node_selected.sym.defn[0]] {
596
- key = uris.from_fs_path(<>node.loc.mod_path);
605
+ changes: dict[(str, list[lspt.TextEdit])] = {};
606
+ for node in [*node_selected.sym.uses, node_selected.sym.defn[0]] {
607
+ key = uris.from_fs_path(node.loc.mod_path);
597
608
  new_edit =
598
609
  lspt.TextEdit(
599
- range=utils.create_range(<>node.loc),
610
+ range=utils.create_range(node.loc),
600
611
  new_text=new_name
601
612
  );
602
613
  utils.add_unique_text_edit(changes, key, new_edit);
@@ -608,8 +619,8 @@ class JacLangServer(JacProgram , LanguageServer) {
608
619
 
609
620
  """Return semantic tokens for a file."""
610
621
  def get_semantic_tokens(self: JacLangServer, file_path: str) -> lspt.SemanticTokens {
611
- file_path_fs = file_path.removeprefix('file://');
612
- sem_mgr = self.sem_managers.get(file_path_fs);
622
+ fs_path = uris.to_fs_path(file_path);
623
+ sem_mgr = self.sem_managers.get(fs_path);
613
624
  if not sem_mgr {
614
625
  return lspt.SemanticTokens(data=[]);
615
626
  }
@@ -638,4 +649,37 @@ class JacLangServer(JacProgram , LanguageServer) {
638
649
  def log_py(self: JacLangServer, message: str) -> None {
639
650
  logging.info(message);
640
651
  }
652
+
653
+ """Run a function with debouncing per file."""
654
+ async def _debounced_run(
655
+ self: JacLangServer,
656
+ func: Callable,
657
+ file_uri: str,
658
+ delay: float,
659
+ task_dict: dict[(str, asyncio.Task)]
660
+ ) -> None {
661
+ if ((file_uri in task_dict) and not task_dict[file_uri].done() ) {
662
+ task_dict[file_uri].cancel();
663
+ self.log_py(
664
+ f" {func.__name__} ' was cancelled due to debounce for ' {file_uri} "
665
+ );
666
+ }
667
+ async def wrapper() {
668
+ try {
669
+ await asyncio.sleep(delay);
670
+ loop = asyncio.get_event_loop();
671
+ result = await loop.run_in_executor(None, func, file_uri);
672
+ return result;
673
+ } except asyncio.CancelledError {
674
+ self.log_py(
675
+ f" {func.__name__} ' was cancelled due to debounce for ' {file_uri} "
676
+ );
677
+ raise ;
678
+ }
679
+ }
680
+ new_task = asyncio.create_task(wrapper());
681
+ task_dict[file_uri] = new_task;
682
+ return await new_task;
683
+ }
684
+
641
685
  }
@@ -21,7 +21,7 @@ with entry {
21
21
  """Check syntax on change."""
22
22
  @server.feature(lspt.TEXT_DOCUMENT_DID_OPEN)
23
23
  async def did_open(ls: JacLangServer, params: lspt.DidOpenTextDocumentParams) -> None {
24
- await ls.launch_deep_check(params.text_document.uri);
24
+ await ls.launch_deep_check(params.text_document.uri, delay=0.001);
25
25
  ls.lsp.send_request(lspt.WORKSPACE_SEMANTIC_TOKENS_REFRESH);
26
26
  }
27
27
 
@@ -30,38 +30,45 @@ async def did_open(ls: JacLangServer, params: lspt.DidOpenTextDocumentParams) ->
30
30
  @server.feature(lspt.TEXT_DOCUMENT_DID_SAVE)
31
31
  async def did_save(ls: JacLangServer, params: lspt.DidOpenTextDocumentParams) -> None {
32
32
  file_path = params.text_document.uri;
33
- quick_check_passed = await ls.launch_quick_check(file_path);
33
+ quick_check_passed = await ls.launch_quick_check(file_path, delay=0.1);
34
34
  if not quick_check_passed {
35
35
  return;
36
36
  }
37
- await ls.launch_deep_check(file_path);
37
+ await ls.launch_deep_check(file_path, delay=0.2);
38
38
  ls.lsp.send_request(lspt.WORKSPACE_SEMANTIC_TOKENS_REFRESH);
39
39
  }
40
40
 
41
41
 
42
- """Check syntax on change."""
43
- @server.feature(lspt.TEXT_DOCUMENT_DID_CHANGE)
42
+ """Check syntax on change with debouncing."""
43
+ @ server.feature(lspt.TEXT_DOCUMENT_DID_CHANGE)
44
44
  async def did_change(
45
45
  ls: JacLangServer,
46
46
  params: lspt.DidChangeTextDocumentParams
47
47
  ) -> None {
48
48
  file_path = params.text_document.uri;
49
- quick_check_passed = await ls.launch_quick_check(file_path);
50
-
51
- if quick_check_passed {
49
+ try {
52
50
  document = ls.workspace.get_text_document(file_path);
53
51
  lines = document.source.splitlines();
54
- sem_manager = ls.sem_managers[file_path.removeprefix('file://')];
55
- sem_manager.update_sem_tokens(
56
- params,sem_manager.sem_tokens,lines
52
+ fs_path = document.path;
53
+ if (fs_path in ls.sem_managers) {
54
+ sem_manager = ls.sem_managers[fs_path];
55
+ sem_manager.update_sem_tokens(params, sem_manager.sem_tokens, lines);
56
+ ls.lsp.send_request(lspt.WORKSPACE_SEMANTIC_TOKENS_REFRESH);
57
+ }
58
+ } except Exception as e {
59
+ ls.log_py(f" 'Failed to update semantic tokens: ' {e} ");
60
+ }
61
+ ls.log_py(f" 'Launching debounced quick check for ' {file_path} ");
62
+ quick_check_passed = await ls.launch_quick_check(file_path, delay=0.5);
63
+ if quick_check_passed {
64
+ ls.log_py(
65
+ f" 'Quick check passed, launching debounced deep check for ' {file_path} "
57
66
  );
58
- ls.lsp.send_request(lspt.WORKSPACE_SEMANTIC_TOKENS_REFRESH);
59
- await ls.launch_deep_check(file_path);
67
+ await ls.launch_deep_check(file_path, delay=1.0);
60
68
  ls.lsp.send_request(lspt.WORKSPACE_SEMANTIC_TOKENS_REFRESH);
61
69
  }
62
70
  }
63
71
 
64
-
65
72
  """Format the given document."""
66
73
  @server.feature(lspt.TEXT_DOCUMENT_FORMATTING)
67
74
  def formatting(