mt-lang 0.2.0

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.
Files changed (258) hide show
  1. checksums.yaml +7 -0
  2. data/.ruby-version +1 -0
  3. data/AUTHORS +3 -0
  4. data/Gemfile +19 -0
  5. data/Gemfile.lock +82 -0
  6. data/LICENSE +21 -0
  7. data/README.md +1208 -0
  8. data/Rakefile +332 -0
  9. data/bin/mtc +8 -0
  10. data/bin/profile-mtc-checks +133 -0
  11. data/bin/tracy-profiler +0 -0
  12. data/docs/build-guide.md +536 -0
  13. data/docs/index.html +2778 -0
  14. data/docs/language-design.md +1519 -0
  15. data/docs/language-manual.md +1534 -0
  16. data/lib/milk_tea/base.rb +49 -0
  17. data/lib/milk_tea/bindings/bindgen/ast_parser.rb +398 -0
  18. data/lib/milk_tea/bindings/bindgen/declaration.rb +319 -0
  19. data/lib/milk_tea/bindings/bindgen/emitter.rb +387 -0
  20. data/lib/milk_tea/bindings/bindgen/overrides.rb +134 -0
  21. data/lib/milk_tea/bindings/bindgen/type_mapper.rb +622 -0
  22. data/lib/milk_tea/bindings/bindgen.rb +207 -0
  23. data/lib/milk_tea/bindings/cli.rb +121 -0
  24. data/lib/milk_tea/bindings/imported_bindings/defaults.rb +210 -0
  25. data/lib/milk_tea/bindings/imported_bindings/generator.rb +1114 -0
  26. data/lib/milk_tea/bindings/imported_bindings/method_source.rb +190 -0
  27. data/lib/milk_tea/bindings/imported_bindings/naming.rb +286 -0
  28. data/lib/milk_tea/bindings/imported_bindings.rb +215 -0
  29. data/lib/milk_tea/bindings/opengl_registry.rb +547 -0
  30. data/lib/milk_tea/bindings/raw_bindings/defaults.rb +1338 -0
  31. data/lib/milk_tea/bindings/raw_bindings.rb +269 -0
  32. data/lib/milk_tea/bindings/steamworks.rb +629 -0
  33. data/lib/milk_tea/bindings/upstream_sources.rb +352 -0
  34. data/lib/milk_tea/bindings/vendored_box2d.rb +79 -0
  35. data/lib/milk_tea/bindings/vendored_c_library.rb +322 -0
  36. data/lib/milk_tea/bindings/vendored_cjson.rb +52 -0
  37. data/lib/milk_tea/bindings/vendored_flecs.rb +78 -0
  38. data/lib/milk_tea/bindings/vendored_glfw.rb +77 -0
  39. data/lib/milk_tea/bindings/vendored_libuv.rb +75 -0
  40. data/lib/milk_tea/bindings/vendored_pcre2.rb +84 -0
  41. data/lib/milk_tea/bindings/vendored_raylib.rb +131 -0
  42. data/lib/milk_tea/bindings/vendored_sdl3.rb +78 -0
  43. data/lib/milk_tea/bindings/vendored_steamworks.rb +71 -0
  44. data/lib/milk_tea/bindings/vendored_tool.rb +71 -0
  45. data/lib/milk_tea/bindings/vendored_tools.rb +23 -0
  46. data/lib/milk_tea/bindings/vendored_tracy.rb +37 -0
  47. data/lib/milk_tea/bindings.rb +23 -0
  48. data/lib/milk_tea/core/ast.rb +311 -0
  49. data/lib/milk_tea/core/async_runtime_installer.rb +28 -0
  50. data/lib/milk_tea/core/binding_types.rb +18 -0
  51. data/lib/milk_tea/core/bindings/attribute_binding.rb +58 -0
  52. data/lib/milk_tea/core/bindings/function_binding.rb +5 -0
  53. data/lib/milk_tea/core/bindings/module_binding.rb +58 -0
  54. data/lib/milk_tea/core/bindings/value_binding.rb +21 -0
  55. data/lib/milk_tea/core/c_backend/aggregate_utils.rb +103 -0
  56. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +412 -0
  57. data/lib/milk_tea/core/c_backend/expressions.rb +468 -0
  58. data/lib/milk_tea/core/c_backend/feature_detection.rb +483 -0
  59. data/lib/milk_tea/core/c_backend/reachability.rb +398 -0
  60. data/lib/milk_tea/core/c_backend/reinterpret.rb +226 -0
  61. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +1080 -0
  62. data/lib/milk_tea/core/c_backend/statements.rb +563 -0
  63. data/lib/milk_tea/core/c_backend/type_collectors.rb +1545 -0
  64. data/lib/milk_tea/core/c_backend/type_declaration.rb +223 -0
  65. data/lib/milk_tea/core/c_backend/type_system.rb +345 -0
  66. data/lib/milk_tea/core/c_backend.rb +287 -0
  67. data/lib/milk_tea/core/compatibility_helpers.rb +79 -0
  68. data/lib/milk_tea/core/compile_time/const_eval.rb +187 -0
  69. data/lib/milk_tea/core/compile_time.rb +329 -0
  70. data/lib/milk_tea/core/control_flow/builder.rb +582 -0
  71. data/lib/milk_tea/core/control_flow/constant_propagation.rb +143 -0
  72. data/lib/milk_tea/core/control_flow/dataflow.rb +74 -0
  73. data/lib/milk_tea/core/control_flow/definite_assignment.rb +79 -0
  74. data/lib/milk_tea/core/control_flow/graph.rb +90 -0
  75. data/lib/milk_tea/core/control_flow/liveness.rb +24 -0
  76. data/lib/milk_tea/core/control_flow/nullability_flow.rb +43 -0
  77. data/lib/milk_tea/core/control_flow/reachability.rb +22 -0
  78. data/lib/milk_tea/core/control_flow/termination.rb +31 -0
  79. data/lib/milk_tea/core/control_flow.rb +34 -0
  80. data/lib/milk_tea/core/cst.rb +48 -0
  81. data/lib/milk_tea/core/cst_builder.rb +19 -0
  82. data/lib/milk_tea/core/ir.rb +85 -0
  83. data/lib/milk_tea/core/keywords.rb +97 -0
  84. data/lib/milk_tea/core/lexer/character_classes.rb +60 -0
  85. data/lib/milk_tea/core/lexer/format_strings.rb +225 -0
  86. data/lib/milk_tea/core/lexer/heredocs.rb +199 -0
  87. data/lib/milk_tea/core/lexer/indentation.rb +62 -0
  88. data/lib/milk_tea/core/lexer/numbers.rb +96 -0
  89. data/lib/milk_tea/core/lexer/recovery.rb +32 -0
  90. data/lib/milk_tea/core/lexer/strings.rb +167 -0
  91. data/lib/milk_tea/core/lexer/symbols.rb +71 -0
  92. data/lib/milk_tea/core/lexer/trivia.rb +53 -0
  93. data/lib/milk_tea/core/lexer.rb +430 -0
  94. data/lib/milk_tea/core/lowering/artifacts.rb +26 -0
  95. data/lib/milk_tea/core/lowering/async/analysis.rb +245 -0
  96. data/lib/milk_tea/core/lowering/async/lowering.rb +1399 -0
  97. data/lib/milk_tea/core/lowering/async/normalization.rb +459 -0
  98. data/lib/milk_tea/core/lowering/async.rb +714 -0
  99. data/lib/milk_tea/core/lowering/block.rb +1052 -0
  100. data/lib/milk_tea/core/lowering/calls.rb +1565 -0
  101. data/lib/milk_tea/core/lowering/declarations.rb +214 -0
  102. data/lib/milk_tea/core/lowering/dyn.rb +206 -0
  103. data/lib/milk_tea/core/lowering/events.rb +1054 -0
  104. data/lib/milk_tea/core/lowering/expressions.rb +1645 -0
  105. data/lib/milk_tea/core/lowering/foreign_cstr.rb +206 -0
  106. data/lib/milk_tea/core/lowering/functions.rb +242 -0
  107. data/lib/milk_tea/core/lowering/loops.rb +1087 -0
  108. data/lib/milk_tea/core/lowering/lowering_context.rb +80 -0
  109. data/lib/milk_tea/core/lowering/proc.rb +419 -0
  110. data/lib/milk_tea/core/lowering/resolve.rb +2516 -0
  111. data/lib/milk_tea/core/lowering/scans.rb +220 -0
  112. data/lib/milk_tea/core/lowering/str_buffer.rb +125 -0
  113. data/lib/milk_tea/core/lowering/utils.rb +1453 -0
  114. data/lib/milk_tea/core/lowering.rb +378 -0
  115. data/lib/milk_tea/core/module_binder.rb +181 -0
  116. data/lib/milk_tea/core/module_loader/errors.rb +21 -0
  117. data/lib/milk_tea/core/module_loader.rb +479 -0
  118. data/lib/milk_tea/core/module_path_resolver.rb +153 -0
  119. data/lib/milk_tea/core/module_roots.rb +88 -0
  120. data/lib/milk_tea/core/parser/attributes.rb +71 -0
  121. data/lib/milk_tea/core/parser/blocks.rb +119 -0
  122. data/lib/milk_tea/core/parser/declarations.rb +749 -0
  123. data/lib/milk_tea/core/parser/expressions.rb +624 -0
  124. data/lib/milk_tea/core/parser/recovery.rb +131 -0
  125. data/lib/milk_tea/core/parser/statements.rb +756 -0
  126. data/lib/milk_tea/core/parser/types.rb +271 -0
  127. data/lib/milk_tea/core/parser.rb +400 -0
  128. data/lib/milk_tea/core/prelude_installer.rb +29 -0
  129. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +917 -0
  130. data/lib/milk_tea/core/pretty_printer/base_formatter.rb +87 -0
  131. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +300 -0
  132. data/lib/milk_tea/core/pretty_printer.rb +17 -0
  133. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +314 -0
  134. data/lib/milk_tea/core/semantic_analyzer/attributes.rb +184 -0
  135. data/lib/milk_tea/core/semantic_analyzer/calls.rb +992 -0
  136. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1735 -0
  137. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +355 -0
  138. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +155 -0
  139. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +354 -0
  140. data/lib/milk_tea/core/semantic_analyzer/generics.rb +383 -0
  141. data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +78 -0
  142. data/lib/milk_tea/core/semantic_analyzer/module_context.rb +35 -0
  143. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +1438 -0
  144. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +421 -0
  145. data/lib/milk_tea/core/semantic_analyzer/statements.rb +1308 -0
  146. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +588 -0
  147. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +307 -0
  148. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +851 -0
  149. data/lib/milk_tea/core/semantic_analyzer.rb +327 -0
  150. data/lib/milk_tea/core/token.rb +26 -0
  151. data/lib/milk_tea/core/token_stream.rb +30 -0
  152. data/lib/milk_tea/core/types/layout.rb +243 -0
  153. data/lib/milk_tea/core/types/predicates.rb +609 -0
  154. data/lib/milk_tea/core/types/registry.rb +83 -0
  155. data/lib/milk_tea/core/types/types.rb +1696 -0
  156. data/lib/milk_tea/core/types/visitor.rb +442 -0
  157. data/lib/milk_tea/core.rb +25 -0
  158. data/lib/milk_tea/dap/backends/lldb_dap.rb +158 -0
  159. data/lib/milk_tea/dap/protocol.rb +58 -0
  160. data/lib/milk_tea/dap/server/breakpoints.rb +66 -0
  161. data/lib/milk_tea/dap/server/debug_map.rb +291 -0
  162. data/lib/milk_tea/dap/server/handlers.rb +374 -0
  163. data/lib/milk_tea/dap/server/launch.rb +261 -0
  164. data/lib/milk_tea/dap/server/lldb_backend.rb +380 -0
  165. data/lib/milk_tea/dap/server/pause_diagnostics.rb +109 -0
  166. data/lib/milk_tea/dap/server/utilities.rb +160 -0
  167. data/lib/milk_tea/dap/server/wire.rb +55 -0
  168. data/lib/milk_tea/dap/server.rb +131 -0
  169. data/lib/milk_tea/dap/session.rb +152 -0
  170. data/lib/milk_tea/dap.rb +6 -0
  171. data/lib/milk_tea/lsp/dependency_resolution.rb +52 -0
  172. data/lib/milk_tea/lsp/diagnostics.rb +611 -0
  173. data/lib/milk_tea/lsp/protocol.rb +104 -0
  174. data/lib/milk_tea/lsp/server/call_hierarchy.rb +274 -0
  175. data/lib/milk_tea/lsp/server/code_actions.rb +446 -0
  176. data/lib/milk_tea/lsp/server/code_lens.rb +97 -0
  177. data/lib/milk_tea/lsp/server/completion.rb +1099 -0
  178. data/lib/milk_tea/lsp/server/configuration.rb +167 -0
  179. data/lib/milk_tea/lsp/server/debug_info.rb +45 -0
  180. data/lib/milk_tea/lsp/server/definition.rb +779 -0
  181. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +239 -0
  182. data/lib/milk_tea/lsp/server/execute_command.rb +25 -0
  183. data/lib/milk_tea/lsp/server/folding_range.rb +153 -0
  184. data/lib/milk_tea/lsp/server/formatting.rb +575 -0
  185. data/lib/milk_tea/lsp/server/hover.rb +1465 -0
  186. data/lib/milk_tea/lsp/server/inlay_hints.rb +204 -0
  187. data/lib/milk_tea/lsp/server/lifecycle.rb +234 -0
  188. data/lib/milk_tea/lsp/server/linked_editing_range.rb +43 -0
  189. data/lib/milk_tea/lsp/server/on_type_formatting.rb +73 -0
  190. data/lib/milk_tea/lsp/server/progress.rb +47 -0
  191. data/lib/milk_tea/lsp/server/references.rb +433 -0
  192. data/lib/milk_tea/lsp/server/rename.rb +598 -0
  193. data/lib/milk_tea/lsp/server/selection_range.rb +130 -0
  194. data/lib/milk_tea/lsp/server/semantic_tokens.rb +1745 -0
  195. data/lib/milk_tea/lsp/server/signature_help.rb +200 -0
  196. data/lib/milk_tea/lsp/server/text_documents.rb +125 -0
  197. data/lib/milk_tea/lsp/server/type_hierarchy.rb +167 -0
  198. data/lib/milk_tea/lsp/server/utilities.rb +520 -0
  199. data/lib/milk_tea/lsp/server.rb +415 -0
  200. data/lib/milk_tea/lsp/workspace/analysis.rb +209 -0
  201. data/lib/milk_tea/lsp/workspace/caches.rb +260 -0
  202. data/lib/milk_tea/lsp/workspace/collection.rb +98 -0
  203. data/lib/milk_tea/lsp/workspace/definition_index.rb +184 -0
  204. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +282 -0
  205. data/lib/milk_tea/lsp/workspace/store.rb +154 -0
  206. data/lib/milk_tea/lsp/workspace/utilities.rb +490 -0
  207. data/lib/milk_tea/lsp/workspace.rb +127 -0
  208. data/lib/milk_tea/lsp.rb +13 -0
  209. data/lib/milk_tea/packages/atomic_write.rb +63 -0
  210. data/lib/milk_tea/packages/dependency_solver.rb +194 -0
  211. data/lib/milk_tea/packages/graph.rb +139 -0
  212. data/lib/milk_tea/packages/lock.rb +421 -0
  213. data/lib/milk_tea/packages/manager_cli.rb +485 -0
  214. data/lib/milk_tea/packages/manifest.rb +356 -0
  215. data/lib/milk_tea/packages/manifest_editor.rb +134 -0
  216. data/lib/milk_tea/packages/registry_metadata_provider.rb +34 -0
  217. data/lib/milk_tea/packages/registry_store.rb +420 -0
  218. data/lib/milk_tea/packages/services.rb +41 -0
  219. data/lib/milk_tea/packages/source_cache.rb +71 -0
  220. data/lib/milk_tea/packages/source_fetcher.rb +124 -0
  221. data/lib/milk_tea/packages/source_resolver.rb +389 -0
  222. data/lib/milk_tea/packages/version.rb +164 -0
  223. data/lib/milk_tea/packages.rb +16 -0
  224. data/lib/milk_tea/tooling/asset_pack.rb +141 -0
  225. data/lib/milk_tea/tooling/build.rb +1124 -0
  226. data/lib/milk_tea/tooling/build_cache.rb +240 -0
  227. data/lib/milk_tea/tooling/cli.rb +2688 -0
  228. data/lib/milk_tea/tooling/cst_formatter.rb +13 -0
  229. data/lib/milk_tea/tooling/debug_info_formatter.rb +456 -0
  230. data/lib/milk_tea/tooling/debug_map.rb +248 -0
  231. data/lib/milk_tea/tooling/docs_app.rb +369 -0
  232. data/lib/milk_tea/tooling/error_formatter.rb +86 -0
  233. data/lib/milk_tea/tooling/formatter.rb +787 -0
  234. data/lib/milk_tea/tooling/linter/doc_tags.rb +212 -0
  235. data/lib/milk_tea/tooling/linter/fix_engine.rb +237 -0
  236. data/lib/milk_tea/tooling/linter/flow_rules.rb +380 -0
  237. data/lib/milk_tea/tooling/linter/imports_platform.rb +841 -0
  238. data/lib/milk_tea/tooling/linter/release_rules.rb +744 -0
  239. data/lib/milk_tea/tooling/linter/reserved_names.rb +199 -0
  240. data/lib/milk_tea/tooling/linter/rules.rb +593 -0
  241. data/lib/milk_tea/tooling/linter/source_helpers.rb +407 -0
  242. data/lib/milk_tea/tooling/linter/trailing_comma.rb +139 -0
  243. data/lib/milk_tea/tooling/linter/visitors.rb +1286 -0
  244. data/lib/milk_tea/tooling/linter.rb +798 -0
  245. data/lib/milk_tea/tooling/project_scaffold.rb +82 -0
  246. data/lib/milk_tea/tooling/public/css/docs.css +166 -0
  247. data/lib/milk_tea/tooling/public/js/docs.js +94 -0
  248. data/lib/milk_tea/tooling/run.rb +408 -0
  249. data/lib/milk_tea/tooling/templates/wasm_shell.html +48 -0
  250. data/lib/milk_tea/tooling/toolchain_cli.rb +157 -0
  251. data/lib/milk_tea/tooling/views/404.erb +7 -0
  252. data/lib/milk_tea/tooling/views/index.erb +87 -0
  253. data/lib/milk_tea/tooling/views/layout.erb +69 -0
  254. data/lib/milk_tea/tooling/views/module.erb +97 -0
  255. data/lib/milk_tea/tooling/views/stdlib.erb +21 -0
  256. data/lib/milk_tea/tooling.rb +20 -0
  257. data/lib/milk_tea.rb +8 -0
  258. metadata +426 -0
@@ -0,0 +1,433 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ module LSP
5
+ class Server
6
+ module ServerReferences
7
+ private
8
+
9
+ def handle_references(params)
10
+ stages = new_perf_stages
11
+ total_start = stages ? monotonic_time : nil
12
+ uri = params['textDocument']['uri']
13
+ lsp_line = params['position']['line']
14
+ lsp_char = params['position']['character']
15
+ result_count = 0
16
+ result_state = 'miss'
17
+
18
+ token = measure_perf_stage(stages, 'token') { @workspace.find_token_at(uri, lsp_line, lsp_char) }
19
+ unless token&.type == :identifier
20
+ result_state = 'not-identifier'
21
+ return []
22
+ end
23
+
24
+ include_declaration = params.dig('context', 'includeDeclaration') != false
25
+
26
+ context = measure_perf_stage(stages, 'token_context') { token_context_at(uri, lsp_line, lsp_char) }
27
+ if context
28
+ tokens = context[:tokens]
29
+ token_index = context[:token_index]
30
+ named_arg_refs = named_argument_label_references(uri, token.lexeme, tokens, token_index, include_declaration:, stages:)
31
+ return named_arg_refs if named_arg_refs
32
+ end
33
+
34
+ facts = measure_perf_stage(stages, 'facts') { @workspace.get_facts(uri) }
35
+ target = facts ? measure_perf_stage(stages, 'static_target') { resolve_static_type_reference_target(uri, token, facts) } : nil
36
+ if target
37
+ refs = measure_perf_stage(stages, 'static_refs') { static_type_method_references(target, include_declaration: include_declaration) }
38
+ result_count = refs.length
39
+ result_state = refs.empty? ? 'miss' : 'hit'
40
+ return refs
41
+ end
42
+
43
+ if facts
44
+ scoped_refs = measure_perf_stage(stages, 'scoped_refs') do
45
+ scoped_local_reference_locations(uri, token, lsp_line, lsp_char, facts, include_declaration: include_declaration)
46
+ end
47
+ unless scoped_refs.nil?
48
+ result_count = scoped_refs.length
49
+ result_state = scoped_refs.empty? ? 'miss' : 'hit'
50
+ return scoped_refs
51
+ end
52
+ end
53
+
54
+ level_kind = facts ? module_level_name?(facts, token.lexeme) : false
55
+ if level_kind
56
+ cross_file = true
57
+ refs = measure_perf_stage(stages, 'module_refs') { module_level_reference_locations(uri, token.lexeme, facts, include_declaration: include_declaration, cross_file_allowed: cross_file) }
58
+ result_count = refs.length
59
+ result_state = refs.empty? ? 'miss' : 'hit'
60
+ return refs
61
+ end
62
+
63
+ unless facts
64
+ ast = @workspace.get_ast(uri)
65
+ if ast && module_level_ast_name?(ast, token.lexeme)
66
+ refs = measure_perf_stage(stages, 'module_refs') { module_level_reference_locations(uri, token.lexeme, nil, include_declaration: include_declaration) }
67
+ result_count = refs.length
68
+ result_state = refs.empty? ? 'miss' : 'hit'
69
+ return refs
70
+ end
71
+ end
72
+
73
+ refs = measure_perf_stage(stages, 'refs_scan') { @workspace.find_all_references(token.lexeme) }
74
+ if include_declaration
75
+ result_count = refs.length
76
+ result_state = refs.empty? ? 'miss' : 'hit'
77
+ return refs
78
+ end
79
+
80
+ found = measure_perf_stage(stages, 'definition_lookup') { @workspace.find_definition_token_global(token.lexeme, preferred_uri: uri) }
81
+ unless found
82
+ result_count = refs.length
83
+ result_state = refs.empty? ? 'miss' : 'hit'
84
+ return refs
85
+ end
86
+
87
+ def_uri = found[:uri]
88
+ def_line = found[:token].line - 1
89
+ def_char = found[:token].column - 1
90
+ filtered = measure_perf_stage(stages, 'filter') do
91
+ refs.reject do |r|
92
+ r[:uri] == def_uri &&
93
+ r[:range][:start][:line] == def_line &&
94
+ r[:range][:start][:character] == def_char
95
+ end
96
+ end
97
+ result_count = filtered.length
98
+ result_state = filtered.empty? ? 'miss' : 'hit'
99
+ filtered
100
+ rescue StandardError => e
101
+ result_state = 'error'
102
+ warn "Error in references handler: #{e.message}"
103
+ []
104
+ ensure
105
+ log_request_stage_breakdown('textDocument/references', total_start, uri: uri, stages: stages, summary: "result=#{result_state} refs=#{result_count}")
106
+ end
107
+
108
+ def handle_document_link(params)
109
+ uri = params['textDocument']['uri']
110
+ file_path = uri_to_path(uri)
111
+ return [] unless file_path
112
+
113
+ tokens = @workspace.get_tokens(uri)
114
+ return [] unless tokens
115
+
116
+ tokens.filter_map do |token|
117
+ resource_document_link(uri, file_path, token)
118
+ end
119
+ rescue StandardError => e
120
+ warn "Error in documentLink handler: #{e.message}"
121
+ []
122
+ end
123
+
124
+ def handle_document_link_resolve(params)
125
+ target = params['target']
126
+ if target && target.start_with?('file://')
127
+ path = uri_to_path(target)
128
+ if path && File.file?(path)
129
+ first_line = File.open(path, &:readline).strip rescue nil
130
+ params.merge(
131
+ 'tooltip' => first_line ? "#{path}\n#{first_line}" : path
132
+ )
133
+ else
134
+ params
135
+ end
136
+ else
137
+ params
138
+ end
139
+ rescue StandardError => e
140
+ warn "Error in documentLink/resolve handler: #{e.message}"
141
+ params
142
+ end
143
+
144
+ def module_level_name?(facts, name)
145
+ return :function if facts.functions.key?(name) || facts.types.key?(name) || facts.values.key?(name)
146
+
147
+ facts.methods.each_value do |methods|
148
+ return :method if methods.key?(name) || methods.key?("static:#{name}")
149
+ end
150
+
151
+ facts.interfaces.each_value do |interface|
152
+ if interface.respond_to?(:methods) && interface.methods.key?(name)
153
+ return :interface_method
154
+ end
155
+ end
156
+
157
+ false
158
+ end
159
+
160
+ def module_level_name_kind(facts, name)
161
+ result = module_level_name?(facts, name)
162
+ result.is_a?(Symbol) ? result : nil
163
+ end
164
+
165
+ def module_level_ast_name?(ast, name)
166
+ each_ast_node(ast) do |node|
167
+ return true if module_level_declaration_node?(node) && node.respond_to?(:name) && node.name == name
168
+ end
169
+ false
170
+ end
171
+
172
+ def module_level_reference_locations(uri, name, facts, include_declaration:, cross_file_allowed: true)
173
+ ast = @workspace.get_ast(uri)
174
+ return [] unless ast
175
+
176
+ results = []
177
+ binding_ids = facts ? facts.binding_resolution&.identifier_binding_ids : nil
178
+ local_bid_set = nil
179
+ if binding_ids && facts&.binding_resolution
180
+ resolution = facts.binding_resolution
181
+ local_bid_set = Set.new
182
+ each_ast_node(ast) do |node|
183
+ next unless local_binding_declaration_node?(node)
184
+ bid = resolution.declaration_binding_ids[node.object_id]
185
+ local_bid_set << bid if bid
186
+ end
187
+ end
188
+ each_ast_node(ast) do |node|
189
+ if node.is_a?(AST::Identifier) && node.name == name
190
+ if binding_ids&.key?(node.object_id)
191
+ bid = binding_ids[node.object_id]
192
+ next if local_bid_set&.include?(bid)
193
+ end
194
+
195
+ range = ast_name_range(node.name, node.line, node.column)
196
+ next unless range
197
+
198
+ results << {
199
+ uri: uri,
200
+ range: {
201
+ start: { line: range[:line] - 1, character: range[:column] - 1 },
202
+ end: { line: range[:line] - 1, character: range[:column] - 1 + name.length },
203
+ },
204
+ }
205
+ elsif include_declaration && node.respond_to?(:name) && node.name == name && module_level_declaration_node?(node)
206
+ range = declaration_name_range(node)
207
+ unless range
208
+ range = declaration_name_range_fallback(node, uri, name)
209
+ end
210
+ next unless range
211
+
212
+ results << {
213
+ uri: uri,
214
+ range: {
215
+ start: { line: range[:line] - 1, character: range[:column] - 1 },
216
+ end: { line: range[:line] - 1, character: range[:column] - 1 + name.length },
217
+ },
218
+ }
219
+ elsif node.is_a?(AST::MemberAccess) && node.member == name && node.line && node.column
220
+ member_col = node.column - 1
221
+ results << {
222
+ uri: uri,
223
+ range: {
224
+ start: { line: node.line - 1, character: member_col },
225
+ end: { line: node.line - 1, character: member_col + name.length },
226
+ },
227
+ }
228
+ end
229
+ end
230
+
231
+ if cross_file_allowed && facts&.module_name
232
+ importing_uris = @workspace.reverse_import_dependents_for(facts.module_name)
233
+ if importing_uris && !importing_uris.empty?
234
+ cross_refs = @workspace.find_all_references_in(name, importing_uris.to_a - [uri])
235
+ results.concat(cross_refs) if cross_refs
236
+ end
237
+ end
238
+
239
+ results.uniq { |r| [r[:uri], r.dig(:range, :start, :line), r.dig(:range, :start, :character)] }
240
+ end
241
+
242
+ def get_content_line(uri, lsp_line)
243
+ content = @workspace.get_content(uri)
244
+ return nil unless content
245
+
246
+ content.split("\n", -1)[lsp_line]
247
+ rescue StandardError
248
+ nil
249
+ end
250
+
251
+ def find_member_column_in_line(line, receiver_start_col, member_name)
252
+ idx = receiver_start_col
253
+ while idx < line.length && line[idx] =~ /[A-Za-z0-9_.]/
254
+ idx += 1
255
+ end
256
+
257
+ dot_idx = line.index('.', receiver_start_col)
258
+ return nil unless dot_idx && dot_idx < idx
259
+
260
+ member_start = dot_idx + 1
261
+ match = line[member_start, member_name.length]
262
+ return nil unless match == member_name
263
+
264
+ member_start
265
+ end
266
+
267
+ def module_level_declaration_node?(node)
268
+ node.is_a?(AST::FunctionDef) ||
269
+ node.is_a?(AST::MethodDef) ||
270
+ node.is_a?(AST::InterfaceMethodDecl) ||
271
+ node.is_a?(AST::ExternFunctionDecl) ||
272
+ node.is_a?(AST::ForeignFunctionDecl) ||
273
+ node.is_a?(AST::ConstDecl) ||
274
+ node.is_a?(AST::VarDecl) ||
275
+ node.is_a?(AST::TypeAliasDecl) ||
276
+ node.is_a?(AST::StructDecl) ||
277
+ node.is_a?(AST::UnionDecl) ||
278
+ node.is_a?(AST::EnumDecl) ||
279
+ node.is_a?(AST::FlagsDecl) ||
280
+ node.is_a?(AST::OpaqueDecl)
281
+ end
282
+
283
+ def declaration_name_range_fallback(node, uri, name)
284
+ return nil unless node.respond_to?(:line) && node.line
285
+
286
+ row = get_content_line(uri, node.line - 1)
287
+ return nil unless row
288
+
289
+ col = row.index(/\b#{Regexp.escape(name)}\b/)
290
+ return nil unless col
291
+
292
+ { name: name, line: node.line, column: col + 1, length: name.length }
293
+ rescue StandardError
294
+ nil
295
+ end
296
+
297
+ def handle_document_highlight(params)
298
+ uri = params['textDocument']['uri']
299
+ lsp_line = params['position']['line']
300
+ lsp_char = params['position']['character']
301
+
302
+ token = @workspace.find_token_at(uri, lsp_line, lsp_char)
303
+ return [] unless token&.type == :identifier
304
+
305
+ if (facts = @workspace.get_facts(uri))
306
+ scoped = begin
307
+ scoped_local_reference_locations(uri, token, lsp_line, lsp_char, facts, include_declaration: true)
308
+ rescue StandardError
309
+ nil
310
+ end
311
+ unless scoped.nil?
312
+ return scoped.map { |entry| { range: entry[:range], kind: 1 } }
313
+ end
314
+ end
315
+
316
+ toks = @workspace.get_tokens(uri) || []
317
+ toks.select { |t| t.type == :identifier && t.lexeme == token.lexeme }
318
+ .map { |t| { range: token_to_range(t), kind: 1 } }
319
+ rescue StandardError => e
320
+ warn "Error in documentHighlight handler: #{e.message}"
321
+ []
322
+ end
323
+
324
+ def handle_workspace_symbol(params)
325
+ query = (params['query'] || '').downcase
326
+
327
+ @workspace.index_workspace(@root_uri) if @root_uri
328
+
329
+ results = []
330
+ @workspace.all_documents.each do |uri|
331
+ @workspace.get_symbols(uri).each do |sym|
332
+ next unless query.empty? || sym[:name].downcase.include?(query)
333
+
334
+ results << format_symbol(sym, uri)
335
+ end
336
+ end
337
+
338
+ results
339
+ rescue StandardError => e
340
+ warn "Error in workspace/symbol handler: #{e.message}"
341
+ []
342
+ end
343
+
344
+ def resource_document_link(uri, file_path, token)
345
+ return nil unless [:string, :cstring].include?(token.type)
346
+
347
+ literal = token.literal
348
+ return nil unless literal.is_a?(String)
349
+ return nil unless path_like_string_literal?(literal)
350
+
351
+ resolved_path = resolve_document_link_path(file_path, literal)
352
+ return nil unless resolved_path
353
+
354
+ {
355
+ range: token_to_range(token),
356
+ target: path_to_uri(resolved_path),
357
+ tooltip: "Open linked file"
358
+ }
359
+ end
360
+
361
+ def path_like_string_literal?(literal)
362
+ literal.include?('/') || literal.include?(File::SEPARATOR)
363
+ end
364
+
365
+ def resolve_document_link_path(source_path, literal)
366
+ base_dir = File.dirname(source_path)
367
+ candidate = if Pathname.new(literal).absolute?
368
+ literal
369
+ else
370
+ File.expand_path(literal, base_dir)
371
+ end
372
+ return nil unless File.exist?(candidate)
373
+
374
+ candidate
375
+ rescue StandardError
376
+ nil
377
+ end
378
+ end
379
+ def named_argument_label_references(uri, name, tokens, token_index, include_declaration:, stages: nil)
380
+ return nil unless named_argument_label_token?(tokens, token_index)
381
+
382
+ callee_name = named_argument_callee_name(tokens, token_index)
383
+ return nil unless callee_name
384
+
385
+ facts = measure_perf_stage(stages, 'nav_ref_facts') { @workspace.get_facts(uri) }
386
+ return nil unless facts
387
+
388
+ func = facts.functions[callee_name]
389
+ unless func
390
+ facts.methods.each_value do |methods|
391
+ method = methods[callee_name] || methods["static:#{callee_name}"]
392
+ if method
393
+ func = method
394
+ break
395
+ end
396
+ end
397
+ end
398
+ return nil unless func
399
+
400
+ param = func.ast.params.find { |p| p.name == name }
401
+ return nil unless param
402
+
403
+ range = declaration_name_range(param)
404
+ unless range
405
+ range = ast_name_range(name, param.line, param.column)
406
+ end
407
+ return [] unless range
408
+
409
+ declaration_location = {
410
+ uri: uri,
411
+ range: {
412
+ start: { line: range[:line] - 1, character: range[:column] - 1 },
413
+ end: { line: range[:line] - 1, character: range[:column] - 1 + name.length },
414
+ },
415
+ }
416
+
417
+ refs = if include_declaration
418
+ [declaration_location]
419
+ else
420
+ []
421
+ end
422
+
423
+ mock_token = Struct.new(:lexeme, :type, :line, :column).new(name, :identifier, param.line, param.column)
424
+ scoped_refs = scoped_local_reference_locations(uri, mock_token, param.line - 1, param.column - 1, facts, include_declaration: false)
425
+ if scoped_refs
426
+ refs.concat(scoped_refs)
427
+ else
428
+ refs
429
+ end
430
+ end
431
+ end
432
+ end
433
+ end