jaclang 0.7.1__py3-none-any.whl → 0.7.2__py3-none-any.whl

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

Potentially problematic release.


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

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