jaclang 0.6.5__py3-none-any.whl → 0.7.1__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 (36) hide show
  1. jaclang/compiler/absyntree.py +4 -36
  2. jaclang/compiler/compile.py +21 -0
  3. jaclang/compiler/parser.py +13 -4
  4. jaclang/compiler/passes/main/__init__.py +2 -2
  5. jaclang/compiler/passes/main/def_impl_match_pass.py +1 -5
  6. jaclang/compiler/passes/main/def_use_pass.py +14 -7
  7. jaclang/compiler/passes/main/import_pass.py +56 -10
  8. jaclang/compiler/passes/main/pyast_gen_pass.py +55 -2
  9. jaclang/compiler/passes/main/schedules.py +4 -3
  10. jaclang/compiler/passes/main/tests/fixtures/incautoimpl.jac +7 -0
  11. jaclang/compiler/passes/main/tests/test_decl_def_match_pass.py +4 -4
  12. jaclang/compiler/passes/main/tests/test_import_pass.py +13 -0
  13. jaclang/compiler/passes/tool/jac_formatter_pass.py +2 -2
  14. jaclang/compiler/passes/transform.py +13 -4
  15. jaclang/compiler/tests/fixtures/mod_doc_test.jac +3 -1
  16. jaclang/langserve/engine.py +375 -0
  17. jaclang/langserve/server.py +112 -74
  18. jaclang/langserve/tests/fixtures/circle.jac +73 -0
  19. jaclang/langserve/tests/fixtures/circle_err.jac +73 -0
  20. jaclang/langserve/tests/fixtures/circle_pure.impl.jac +28 -0
  21. jaclang/langserve/tests/fixtures/circle_pure.jac +34 -0
  22. jaclang/langserve/tests/fixtures/circle_pure_err.impl.jac +32 -0
  23. jaclang/langserve/tests/fixtures/circle_pure_err.jac +34 -0
  24. jaclang/langserve/tests/test_server.py +33 -1
  25. jaclang/langserve/utils.py +53 -16
  26. jaclang/plugin/default.py +1 -1
  27. jaclang/tests/fixtures/type_info.jac +1 -1
  28. jaclang/tests/test_cli.py +1 -1
  29. jaclang/utils/helpers.py +3 -5
  30. jaclang/utils/test.py +1 -1
  31. {jaclang-0.6.5.dist-info → jaclang-0.7.1.dist-info}/METADATA +8 -16
  32. {jaclang-0.6.5.dist-info → jaclang-0.7.1.dist-info}/RECORD +34 -28
  33. jaclang/compiler/tests/test_workspace.py +0 -93
  34. jaclang/compiler/workspace.py +0 -234
  35. {jaclang-0.6.5.dist-info → jaclang-0.7.1.dist-info}/WHEEL +0 -0
  36. {jaclang-0.6.5.dist-info → jaclang-0.7.1.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,375 @@
1
+ """Living Workspace of Jac project."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from enum import IntEnum
6
+ from hashlib import md5
7
+ from typing import Optional, Sequence
8
+
9
+
10
+ import jaclang.compiler.absyntree as ast
11
+ from jaclang.compiler.compile import jac_ir_to_pass, jac_str_to_pass
12
+ from jaclang.compiler.parser import JacParser
13
+ from jaclang.compiler.passes import Pass
14
+ from jaclang.compiler.passes.main.schedules import type_checker_sched
15
+ from jaclang.compiler.passes.tool import FuseCommentsPass, JacFormatPass
16
+ from jaclang.compiler.passes.transform import Alert
17
+ from jaclang.langserve.utils import find_deepest_node_at_pos
18
+ from jaclang.vendor.pygls import uris
19
+ from jaclang.vendor.pygls.server import LanguageServer
20
+
21
+ import lsprotocol.types as lspt
22
+
23
+
24
+ class ALev(IntEnum):
25
+ """Analysis Level."""
26
+
27
+ QUICK = 1
28
+ DEEP = 2
29
+ TYPE = 3
30
+
31
+
32
+ class ModuleInfo:
33
+ """Module IR and Stats."""
34
+
35
+ def __init__(
36
+ self,
37
+ ir: ast.Module,
38
+ errors: Sequence[Alert],
39
+ warnings: Sequence[Alert],
40
+ alev: ALev,
41
+ parent: Optional[ModuleInfo] = None,
42
+ ) -> None:
43
+ """Initialize module info."""
44
+ self.ir = ir
45
+ self.errors = errors
46
+ self.warnings = warnings
47
+ self.alev = alev
48
+ self.parent: Optional[ModuleInfo] = parent
49
+ self.diagnostics = self.gen_diagnostics()
50
+
51
+ @property
52
+ def uri(self) -> str:
53
+ """Return uri."""
54
+ return uris.from_fs_path(self.ir.loc.mod_path)
55
+
56
+ @property
57
+ def has_syntax_error(self) -> bool:
58
+ """Return if there are syntax errors."""
59
+ return len(self.errors) > 0 and self.alev == ALev.QUICK
60
+
61
+ def gen_diagnostics(self) -> list[lspt.Diagnostic]:
62
+ """Return diagnostics."""
63
+ return [
64
+ 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
+ ),
74
+ message=error.msg,
75
+ severity=lspt.DiagnosticSeverity.Error,
76
+ )
77
+ for error in self.errors
78
+ ] + [
79
+ 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
+ ),
90
+ message=warning.msg,
91
+ severity=lspt.DiagnosticSeverity.Warning,
92
+ )
93
+ for warning in self.warnings
94
+ ]
95
+
96
+
97
+ class JacLangServer(LanguageServer):
98
+ """Class for managing workspace."""
99
+
100
+ def __init__(self) -> None:
101
+ """Initialize workspace."""
102
+ super().__init__("jac-lsp", "v0.1")
103
+ self.modules: dict[str, ModuleInfo] = {}
104
+
105
+ def module_not_diff(self, uri: str, alev: ALev) -> bool:
106
+ """Check if module was changed."""
107
+ doc = self.workspace.get_document(uri)
108
+ return (
109
+ doc.uri in self.modules
110
+ and self.modules[doc.uri].ir.source.hash
111
+ == md5(doc.source.encode()).hexdigest()
112
+ and (
113
+ self.modules[doc.uri].alev >= alev
114
+ or self.modules[doc.uri].has_syntax_error
115
+ )
116
+ )
117
+
118
+ def push_diagnostics(self, file_path: str) -> None:
119
+ """Push diagnostics for a file."""
120
+ if file_path in self.modules:
121
+ self.publish_diagnostics(
122
+ file_path,
123
+ self.modules[file_path].diagnostics,
124
+ )
125
+
126
+ def unwind_to_parent(self, file_path: str) -> str:
127
+ """Unwind to parent."""
128
+ if file_path in self.modules:
129
+ while cur := self.modules[file_path].parent:
130
+ file_path = cur.uri
131
+ return file_path
132
+
133
+ def update_modules(self, file_path: str, build: Pass, alev: ALev) -> None:
134
+ """Update modules."""
135
+ if not isinstance(build.ir, ast.Module):
136
+ self.log_error("Error with module build.")
137
+ return
138
+ self.modules[file_path] = ModuleInfo(
139
+ ir=build.ir,
140
+ errors=[
141
+ i
142
+ for i in build.errors_had
143
+ if i.loc.mod_path == uris.to_fs_path(file_path)
144
+ ],
145
+ warnings=[
146
+ i
147
+ for i in build.warnings_had
148
+ if i.loc.mod_path == uris.to_fs_path(file_path)
149
+ ],
150
+ alev=alev,
151
+ )
152
+ for p in build.ir.mod_deps.keys():
153
+ uri = uris.from_fs_path(p)
154
+ self.modules[uri] = ModuleInfo(
155
+ ir=build.ir.mod_deps[p],
156
+ errors=[i for i in build.errors_had if i.loc.mod_path == p],
157
+ warnings=[i for i in build.warnings_had if i.loc.mod_path == p],
158
+ alev=alev,
159
+ )
160
+
161
+ def quick_check(self, file_path: str) -> None:
162
+ """Rebuild a file."""
163
+ if self.module_not_diff(file_path, ALev.QUICK):
164
+ return
165
+ try:
166
+ document = self.workspace.get_document(file_path)
167
+ build = jac_str_to_pass(
168
+ jac_str=document.source, file_path=document.path, schedule=[]
169
+ )
170
+ except Exception as e:
171
+ self.log_error(f"Error during syntax check: {e}")
172
+ self.update_modules(file_path, build, ALev.QUICK)
173
+
174
+ def deep_check(self, file_path: str) -> None:
175
+ """Rebuild a file and its dependencies."""
176
+ if file_path in self.modules:
177
+ self.quick_check(file_path)
178
+ if self.module_not_diff(file_path, ALev.DEEP):
179
+ return
180
+ try:
181
+ file_path = self.unwind_to_parent(file_path)
182
+ build = jac_ir_to_pass(ir=self.modules[file_path].ir)
183
+ except Exception as e:
184
+ self.log_error(f"Error during syntax check: {e}")
185
+ self.update_modules(file_path, build, ALev.DEEP)
186
+
187
+ def type_check(self, file_path: str) -> None:
188
+ """Rebuild a file and its dependencies."""
189
+ if file_path not in self.modules:
190
+ self.deep_check(file_path)
191
+ if self.module_not_diff(file_path, ALev.TYPE):
192
+ return
193
+ try:
194
+ file_path = self.unwind_to_parent(file_path)
195
+ build = jac_ir_to_pass(
196
+ ir=self.modules[file_path].ir, schedule=type_checker_sched
197
+ )
198
+ except Exception as e:
199
+ self.log_error(f"Error during type check: {e}")
200
+ self.update_modules(file_path, build, ALev.TYPE)
201
+
202
+ def get_completion(
203
+ self, file_path: str, position: lspt.Position
204
+ ) -> lspt.CompletionList:
205
+ """Return completion for a file."""
206
+ items = []
207
+ document = self.workspace.get_document(file_path)
208
+ current_line = document.lines[position.line].strip()
209
+ if current_line.endswith("hello."):
210
+
211
+ items = [
212
+ lspt.CompletionItem(label="world"),
213
+ lspt.CompletionItem(label="friend"),
214
+ ]
215
+ return lspt.CompletionList(is_incomplete=False, items=items)
216
+
217
+ def rename_module(self, old_path: str, new_path: str) -> None:
218
+ """Rename module."""
219
+ if old_path in self.modules and new_path != old_path:
220
+ self.modules[new_path] = self.modules[old_path]
221
+ del self.modules[old_path]
222
+
223
+ def delete_module(self, uri: str) -> None:
224
+ """Delete module."""
225
+ if uri in self.modules:
226
+ del self.modules[uri]
227
+
228
+ def formatted_jac(self, file_path: str) -> list[lspt.TextEdit]:
229
+ """Return formatted jac."""
230
+ try:
231
+ document = self.workspace.get_document(file_path)
232
+ format = jac_str_to_pass(
233
+ jac_str=document.source,
234
+ file_path=document.path,
235
+ target=JacFormatPass,
236
+ schedule=[FuseCommentsPass, JacFormatPass],
237
+ )
238
+ formatted_text = (
239
+ format.ir.gen.jac
240
+ if JacParser not in [e.from_pass for e in format.errors_had]
241
+ else document.source
242
+ )
243
+ except Exception as e:
244
+ self.log_error(f"Error during formatting: {e}")
245
+ formatted_text = document.source
246
+ return [
247
+ lspt.TextEdit(
248
+ range=lspt.Range(
249
+ start=lspt.Position(line=0, character=0),
250
+ end=lspt.Position(
251
+ line=len(document.source.splitlines()) + 1, character=0
252
+ ),
253
+ ),
254
+ new_text=(formatted_text),
255
+ )
256
+ ]
257
+
258
+ def get_hover_info(
259
+ self, file_path: str, position: lspt.Position
260
+ ) -> Optional[lspt.Hover]:
261
+ """Return hover information for a file."""
262
+ node_selected = find_deepest_node_at_pos(
263
+ self.modules[file_path].ir, position.line, position.character
264
+ )
265
+ value = self.get_node_info(node_selected) if node_selected else None
266
+ if value:
267
+ return lspt.Hover(
268
+ contents=lspt.MarkupContent(
269
+ kind=lspt.MarkupKind.PlainText, value=f"{value}"
270
+ ),
271
+ )
272
+ return None
273
+
274
+ def get_node_info(self, node: ast.AstNode) -> Optional[str]:
275
+ """Extract meaningful information from the AST node."""
276
+ 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
358
+ except AttributeError as e:
359
+ self.log_warning(f"Attribute error when accessing node attributes: {e}")
360
+ return node_info.strip()
361
+
362
+ def log_error(self, message: str) -> None:
363
+ """Log an error message."""
364
+ self.show_message_log(message, lspt.MessageType.Error)
365
+ self.show_message(message, lspt.MessageType.Error)
366
+
367
+ def log_warning(self, message: str) -> None:
368
+ """Log a warning message."""
369
+ self.show_message_log(message, lspt.MessageType.Warning)
370
+ self.show_message(message, lspt.MessageType.Warning)
371
+
372
+ def log_info(self, message: str) -> None:
373
+ """Log an info message."""
374
+ self.show_message_log(message, lspt.MessageType.Info)
375
+ self.show_message(message, lspt.MessageType.Info)
@@ -2,101 +2,139 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from jaclang.compiler.compile import jac_str_to_pass
6
- from jaclang.compiler.passes.tool import FuseCommentsPass, JacFormatPass
7
- from jaclang.langserve.utils import debounce, log, log_error
8
- from jaclang.vendor.pygls.server import LanguageServer
5
+ import threading
6
+ from typing import Optional
7
+
8
+ from jaclang.langserve.engine import JacLangServer
9
+ from jaclang.langserve.utils import debounce
9
10
 
10
11
  import lsprotocol.types as lspt
11
12
 
12
- server = LanguageServer("jac-lsp", "v0.1")
13
+ server = JacLangServer()
14
+ analysis_thread: Optional[threading.Thread] = None
15
+ analysis_stop_event = threading.Event()
16
+
17
+
18
+ def analyze_and_publish(ls: JacLangServer, uri: str) -> None:
19
+ """Analyze and publish diagnostics."""
20
+ global analysis_thread, analysis_stop_event
21
+
22
+ def run_analysis() -> None:
23
+ ls.quick_check(uri)
24
+ ls.push_diagnostics(uri)
25
+ ls.deep_check(uri)
26
+ ls.push_diagnostics(uri)
27
+ ls.type_check(uri)
28
+ ls.push_diagnostics(uri)
29
+
30
+ analysis_thread = threading.Thread(target=run_analysis)
31
+ analysis_thread.start()
32
+
33
+
34
+ def stop_analysis() -> None:
35
+ """Stop analysis."""
36
+ global analysis_thread, analysis_stop_event
37
+ if analysis_thread is not None:
38
+ analysis_stop_event.set()
39
+ analysis_thread.join()
40
+ analysis_stop_event.clear()
41
+
42
+
43
+ @server.feature(lspt.TEXT_DOCUMENT_DID_OPEN)
44
+ async def did_open(ls: JacLangServer, params: lspt.DidOpenTextDocumentParams) -> None:
45
+ """Check syntax on change."""
46
+ stop_analysis()
47
+ analyze_and_publish(ls, params.text_document.uri)
13
48
 
14
49
 
15
50
  @server.feature(lspt.TEXT_DOCUMENT_DID_CHANGE)
16
- @debounce(0.3)
51
+ @debounce(0.1)
17
52
  async def did_change(
18
- ls: LanguageServer, params: lspt.DidChangeTextDocumentParams
53
+ ls: JacLangServer, params: lspt.DidChangeTextDocumentParams
19
54
  ) -> None:
20
55
  """Check syntax on change."""
21
- document = ls.workspace.get_document(params.text_document.uri)
22
- try:
23
- result = jac_str_to_pass(
24
- jac_str=document.source,
25
- file_path=document.path,
26
- schedule=[],
27
- )
28
- if not result.errors_had and not result.warnings_had:
29
- ls.publish_diagnostics(document.uri, [])
30
- else:
31
- ls.publish_diagnostics(
32
- document.uri,
33
- [
34
- lspt.Diagnostic(
35
- range=lspt.Range(
36
- start=lspt.Position(
37
- line=error.loc.first_line, character=error.loc.col_start
38
- ),
39
- end=lspt.Position(
40
- line=error.loc.last_line,
41
- character=error.loc.col_end,
42
- ),
43
- ),
44
- message=error.msg,
45
- severity=lspt.DiagnosticSeverity.Error,
46
- )
47
- for error in result.errors_had
48
- ],
49
- )
50
- except Exception as e:
51
- log_error(ls, f"Error during syntax check: {e}")
52
- log(f"Error during syntax check: {e}")
56
+ stop_analysis()
57
+ analyze_and_publish(ls, params.text_document.uri)
58
+
59
+
60
+ @server.feature(lspt.TEXT_DOCUMENT_DID_SAVE)
61
+ async def did_save(ls: JacLangServer, params: lspt.DidSaveTextDocumentParams) -> None:
62
+ """Check syntax on save."""
63
+ stop_analysis()
64
+ analyze_and_publish(ls, params.text_document.uri)
65
+
66
+
67
+ @server.feature(
68
+ lspt.WORKSPACE_DID_CREATE_FILES,
69
+ lspt.FileOperationRegistrationOptions(
70
+ filters=[
71
+ lspt.FileOperationFilter(pattern=lspt.FileOperationPattern("**/*.jac"))
72
+ ]
73
+ ),
74
+ )
75
+ async def did_create_files(ls: JacLangServer, params: lspt.CreateFilesParams) -> None:
76
+ """Check syntax on file creation."""
77
+ for file in params.files:
78
+ ls.quick_check(file.uri)
79
+ ls.push_diagnostics(file.uri)
80
+
81
+
82
+ @server.feature(
83
+ lspt.WORKSPACE_DID_RENAME_FILES,
84
+ lspt.FileOperationRegistrationOptions(
85
+ filters=[
86
+ lspt.FileOperationFilter(pattern=lspt.FileOperationPattern("**/*.jac"))
87
+ ]
88
+ ),
89
+ )
90
+ async def did_rename_files(ls: JacLangServer, params: lspt.RenameFilesParams) -> None:
91
+ """Check syntax on file rename."""
92
+ new_uris = [file.new_uri for file in params.files]
93
+ old_uris = [file.old_uri for file in params.files]
94
+ for i in range(len(new_uris)):
95
+ ls.rename_module(old_uris[i], new_uris[i])
96
+ ls.quick_check(new_uris[i])
97
+
98
+
99
+ @server.feature(
100
+ lspt.WORKSPACE_DID_DELETE_FILES,
101
+ lspt.FileOperationRegistrationOptions(
102
+ filters=[
103
+ lspt.FileOperationFilter(pattern=lspt.FileOperationPattern("**/*.jac"))
104
+ ]
105
+ ),
106
+ )
107
+ async def did_delete_files(ls: JacLangServer, params: lspt.DeleteFilesParams) -> None:
108
+ """Check syntax on file delete."""
109
+ for file in params.files:
110
+ ls.delete_module(file.uri)
53
111
 
54
112
 
55
113
  @server.feature(
56
114
  lspt.TEXT_DOCUMENT_COMPLETION,
57
115
  lspt.CompletionOptions(trigger_characters=[".", ":", ""]),
58
116
  )
59
- def completions(params: lspt.CompletionParams) -> lspt.CompletionList:
60
- """Provide completions for the given completion request."""
61
- items = []
62
- document = server.workspace.get_text_document(params.text_document.uri)
63
- current_line = document.lines[params.position.line].strip()
64
- if current_line.endswith("hello."):
65
-
66
- items = [
67
- lspt.CompletionItem(label="world"),
68
- lspt.CompletionItem(label="friend"),
69
- ]
70
- return lspt.CompletionList(is_incomplete=False, items=items)
117
+ async def completion(
118
+ ls: JacLangServer, params: lspt.CompletionParams
119
+ ) -> lspt.CompletionList:
120
+ """Provide completion."""
121
+ return ls.get_completion(params.text_document.uri, params.position)
71
122
 
72
123
 
73
124
  @server.feature(lspt.TEXT_DOCUMENT_FORMATTING)
74
125
  def formatting(
75
- ls: LanguageServer, params: lspt.DocumentFormattingParams
126
+ ls: JacLangServer, params: lspt.DocumentFormattingParams
76
127
  ) -> list[lspt.TextEdit]:
77
128
  """Format the given document."""
78
- try:
79
- document = ls.workspace.get_document(params.text_document.uri)
80
- formatted_text = jac_str_to_pass(
81
- jac_str=document.source,
82
- file_path=document.path,
83
- target=JacFormatPass,
84
- schedule=[FuseCommentsPass, JacFormatPass],
85
- ).ir.gen.jac
86
- except Exception as e:
87
- log_error(ls, f"Error during formatting: {e}")
88
- formatted_text = document.source
89
- return [
90
- lspt.TextEdit(
91
- range=lspt.Range(
92
- start=lspt.Position(line=0, character=0),
93
- end=lspt.Position(
94
- line=len(formatted_text.splitlines()) + 1, character=0
95
- ),
96
- ),
97
- new_text=formatted_text,
98
- )
99
- ]
129
+ return ls.formatted_jac(params.text_document.uri)
130
+
131
+
132
+ @server.feature(lspt.TEXT_DOCUMENT_HOVER, lspt.HoverOptions(work_done_progress=True))
133
+ def hover(
134
+ ls: JacLangServer, params: lspt.TextDocumentPositionParams
135
+ ) -> Optional[lspt.Hover]:
136
+ """Provide hover information for the given hover request."""
137
+ return ls.get_hover_info(params.text_document.uri, params.position)
100
138
 
101
139
 
102
140
  def run_lang_server() -> None:
@@ -0,0 +1,73 @@
1
+ """
2
+ This module demonstrates a simple circle class and a function to calculate
3
+ the area of a circle in all of Jac's glory.
4
+ """
5
+
6
+ import:py math;
7
+ # Module-level global var
8
+
9
+ glob RAD = 5;
10
+
11
+ """Function to calculate the area of a circle."""
12
+ can calculate_area(radius: float) -> float {
13
+ return math.pi * radius * radius;
14
+ }
15
+ #* (This is multiline comments in Jac)
16
+ Above we have the demonstration of a function to calculate the area of a circle.
17
+ Below we have the demonstration of a class to calculate the area of a circle.
18
+ *#
19
+
20
+ """Enum for shape types"""
21
+ enum ShapeType {
22
+ CIRCLE="Circle",
23
+ UNKNOWN="Unknown"
24
+ }
25
+
26
+ """Base class for a shape."""
27
+ obj Shape {
28
+ has shape_type: ShapeType;
29
+
30
+ """Abstract method to calculate the area of a shape."""
31
+ can area -> float abs;
32
+ }
33
+
34
+ """Circle class inherits from Shape."""
35
+ obj Circle :Shape: {
36
+ can init(radius: float) {
37
+ super.init(ShapeType.CIRCLE);
38
+ self.radius = radius;
39
+ }
40
+
41
+ """Overridden method to calculate the area of the circle."""
42
+ override can area -> float {
43
+ return math.pi * self.radius * self.radius;
44
+ }
45
+ }
46
+
47
+ with entry {
48
+ c = Circle(RAD);
49
+ }
50
+ # Global also works here
51
+
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()}");
56
+ }
57
+ # Unit Tests!
58
+
59
+ glob expected_area = 78.53981633974483;
60
+
61
+ test calc_area {
62
+ check.assertAlmostEqual(calculate_area(RAD), expected_area);
63
+ }
64
+
65
+ test circle_area {
66
+ c = Circle(RAD);
67
+ check.assertAlmostEqual(c.area(), expected_area);
68
+ }
69
+
70
+ test circle_type {
71
+ c = Circle(RAD);
72
+ check.assertEqual(c.shape_type, ShapeType.CIRCLE);
73
+ }