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,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+ require 'thread'
5
+
6
+ module MilkTea
7
+ module LSP
8
+ # Handles JSON-RPC 2.0 protocol encoding/decoding over stdin/stdout
9
+ class Protocol
10
+ INVALID_MESSAGE = Object.new.freeze
11
+ @write_mutex = Mutex.new
12
+ @outgoing_mutex = Mutex.new
13
+ @outgoing_id_counter = 1
14
+
15
+ def self.outgoing_requests
16
+ @outgoing_requests ||= {}
17
+ end
18
+
19
+ def self.send_request(method, params, &callback)
20
+ id = @outgoing_mutex.synchronize { i = @outgoing_id_counter; @outgoing_id_counter += 1; i }
21
+ @outgoing_mutex.synchronize { outgoing_requests[id] = callback }
22
+ write_message({ jsonrpc: '2.0', id: id, method: method, params: params })
23
+ id
24
+ rescue StandardError => e
25
+ @outgoing_mutex.synchronize { outgoing_requests.delete(id) }
26
+ warn "Protocol error sending request #{method}: #{e.message}"
27
+ nil
28
+ end
29
+
30
+ def self.handle_response(message)
31
+ id = message['id']
32
+ callback = @outgoing_mutex.synchronize { outgoing_requests.delete(id) }
33
+ return unless callback
34
+
35
+ callback.call(message['result'], message['error'])
36
+ rescue StandardError => e
37
+ warn "Protocol error handling response: #{e.message}"
38
+ end
39
+
40
+ def self.read_message
41
+ headers = {}
42
+ loop do
43
+ line = $stdin.gets
44
+ return nil if line.nil?
45
+
46
+ line = line.chomp
47
+ break if line.empty?
48
+
49
+ key, value = line.split(':', 2)
50
+ headers[key&.strip] = value&.strip
51
+ end
52
+
53
+ content_length = headers['Content-Length']&.to_i
54
+ return INVALID_MESSAGE if content_length.nil? || content_length <= 0
55
+
56
+ content = $stdin.read(content_length)
57
+ JSON.parse(content)
58
+ rescue StandardError => e
59
+ warn "Protocol error reading message: #{e.message}"
60
+ INVALID_MESSAGE
61
+ end
62
+
63
+ def self.write_message(message)
64
+ json = JSON.dump(message)
65
+ length = json.bytesize
66
+ header = "Content-Length: #{length}\r\n\r\n"
67
+ @write_mutex.synchronize do
68
+ $stdout.write(header)
69
+ $stdout.write(json)
70
+ $stdout.flush
71
+ end
72
+ rescue StandardError => e
73
+ warn "Protocol error writing message: #{e.message}"
74
+ end
75
+
76
+ def self.write_notification(method, params)
77
+ write_message({
78
+ jsonrpc: '2.0',
79
+ method: method,
80
+ params: params
81
+ })
82
+ end
83
+
84
+ def self.write_response(id, result)
85
+ write_message({
86
+ jsonrpc: '2.0',
87
+ id: id,
88
+ result: result
89
+ })
90
+ end
91
+
92
+ def self.write_error(id, code, message)
93
+ write_message({
94
+ jsonrpc: '2.0',
95
+ id: id,
96
+ error: {
97
+ code: code,
98
+ message: message
99
+ }
100
+ })
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,274 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ module LSP
5
+ class Server
6
+ module ServerCallHierarchy
7
+ private
8
+
9
+ def handle_prepare_call_hierarchy(params)
10
+ text_document = params["textDocument"]
11
+ position = params["position"]
12
+ uri = text_document["uri"]
13
+ lsp_line = position["line"]
14
+ lsp_char = position["character"]
15
+
16
+ facts = @workspace.get_facts(uri)
17
+ return nil unless facts
18
+
19
+ token = @workspace.find_token_at(uri, lsp_line, lsp_char)
20
+ return nil unless token
21
+ return nil unless token.type == :identifier
22
+
23
+ func_name = token.lexeme
24
+
25
+ func_binding = facts.functions[func_name]
26
+ if func_binding
27
+ return [build_call_hierarchy_item(func_name, uri, func_binding, facts)]
28
+ end
29
+
30
+ dot_recv = @workspace.find_dot_receiver(uri, lsp_line, lsp_char)
31
+ if dot_recv
32
+ if (module_binding = facts.imports[dot_recv])
33
+ if module_binding.functions.key?(func_name)
34
+ return [build_call_hierarchy_item(func_name, uri, module_binding.functions[func_name], facts)]
35
+ end
36
+ end
37
+ end
38
+
39
+ if facts.methods
40
+ facts.methods.each_value do |method_map|
41
+ if method_map.key?(func_name)
42
+ return [build_call_hierarchy_item(func_name, uri, method_map[func_name], facts)]
43
+ end
44
+ end
45
+ end
46
+
47
+ nil
48
+ end
49
+
50
+ def handle_incoming_calls(params)
51
+ item = params["item"] || params[:item]
52
+ data = item["data"] || item[:data]
53
+ return [] unless data.is_a?(Hash) || data.respond_to?(:[])
54
+
55
+ target_name = data["name"] || data[:name]
56
+ return [] if target_name.to_s.empty?
57
+
58
+ target_uri = data["uri"] || data[:uri]
59
+ candidate_uris = nil
60
+ if target_uri
61
+ target_facts = @workspace.get_facts(target_uri)
62
+ if target_facts&.module_name
63
+ importing = @workspace.reverse_import_dependents_for(target_facts.module_name)
64
+ if importing && !importing.empty?
65
+ candidate_uris = importing.to_a + [target_uri]
66
+ end
67
+ end
68
+ end
69
+
70
+ refs = if candidate_uris
71
+ @workspace.find_all_references_in(target_name, candidate_uris)
72
+ else
73
+ @workspace.find_all_references(target_name)
74
+ end
75
+ return [] if refs.empty?
76
+
77
+ caller_map = {}
78
+ refs.each do |ref|
79
+ call_uri = ref[:uri]
80
+ ref_line = ref[:range][:start][:line] + 1
81
+
82
+ caller_func = find_enclosing_function(call_uri, ref_line)
83
+ next unless caller_func
84
+ next if caller_func[:name] == target_name && call_uri == data["uri"]
85
+
86
+ key = [call_uri, caller_func[:name]]
87
+ caller_map[key] ||= { uri: call_uri, name: caller_func[:name], ranges: [], kind: caller_func[:kind] }
88
+ caller_map[key][:ranges] << ref[:range]
89
+ end
90
+
91
+ caller_map.map do |_, caller|
92
+ {
93
+ from: {
94
+ name: caller[:name],
95
+ kind: caller[:kind],
96
+ uri: caller[:uri],
97
+ range: caller[:ranges].first,
98
+ selectionRange: caller[:ranges].first,
99
+ data: { name: caller[:name], uri: caller[:uri], kind: caller[:kind] },
100
+ },
101
+ fromRanges: caller[:ranges],
102
+ }
103
+ end
104
+ end
105
+
106
+ def handle_outgoing_calls(params)
107
+ item = params["item"] || params[:item]
108
+ data = item["data"] || item[:data]
109
+ return [] unless data.is_a?(Hash) || data.respond_to?(:[])
110
+
111
+ target_name = data["name"] || data[:name]
112
+ target_uri = data["uri"] || data[:uri]
113
+ return [] if target_name.to_s.empty?
114
+
115
+ facts = @workspace.get_facts(target_uri)
116
+ return [] unless facts
117
+
118
+ func_binding = facts.functions[target_name]
119
+ unless func_binding
120
+ facts.methods&.each_value do |method_map|
121
+ func_binding = method_map[target_name]
122
+ break if func_binding
123
+ end
124
+ end
125
+ return [] unless func_binding
126
+ return [] unless func_binding.ast.respond_to?(:body)
127
+
128
+ callees = collect_call_callees(func_binding.ast.body, facts)
129
+ return [] if callees.empty?
130
+
131
+ result = []
132
+ callees.each do |callee_name, ranges|
133
+ def_entry = @workspace.find_definition_token_global(callee_name, preferred_uri: target_uri)
134
+ def_uri = def_entry ? def_entry[:uri].to_s : target_uri
135
+ def_range = def_entry ? token_range(def_entry[:token]) : ranges.first
136
+
137
+ result << {
138
+ to: {
139
+ name: callee_name,
140
+ kind: 12,
141
+ uri: def_uri,
142
+ range: def_range,
143
+ selectionRange: def_range,
144
+ data: { name: callee_name, uri: def_uri, kind: 12 },
145
+ },
146
+ fromRanges: ranges,
147
+ }
148
+ end
149
+
150
+ result.take(50)
151
+ end
152
+
153
+ private
154
+
155
+ def build_call_hierarchy_item(name, uri, binding, facts)
156
+ range = binding_range(binding)
157
+ selection_range = selection_range_from_binding(binding)
158
+
159
+ receiver = nil
160
+ if binding.respond_to?(:receiver_type) && binding.receiver_type
161
+ receiver = binding.receiver_type.to_s
162
+ end
163
+
164
+ {
165
+ name: name,
166
+ kind: receiver ? 6 : 12,
167
+ uri: uri,
168
+ range: range,
169
+ selectionRange: selection_range,
170
+ data: {
171
+ name: name,
172
+ uri: uri,
173
+ receiver_type: receiver,
174
+ },
175
+ }
176
+ end
177
+
178
+ def binding_range(binding)
179
+ line = binding.ast.respond_to?(:line) ? binding.ast.line - 1 : 0
180
+ col = binding.ast.respond_to?(:column) ? binding.ast.column - 1 : 0
181
+ length = binding.name.length
182
+ {
183
+ start: { line: line, character: col },
184
+ end: { line: line, character: col + length },
185
+ }
186
+ end
187
+
188
+ def selection_range_from_binding(binding)
189
+ name_len = binding.name.length
190
+ ast = binding.ast
191
+ line = ast.respond_to?(:line) ? ast.line - 1 : 0
192
+ col = ast.respond_to?(:column) ? ast.column - 1 : 0
193
+
194
+ if ast.respond_to?(:name)
195
+ name_ast = ast.name
196
+ if name_ast.respond_to?(:parts)
197
+ line = name_ast.line - 1
198
+ col = name_ast.column - 1
199
+ end
200
+ end
201
+
202
+ {
203
+ start: { line: line, character: col },
204
+ end: { line: line, character: col + name_len },
205
+ }
206
+ end
207
+
208
+ def find_enclosing_function(uri, line)
209
+ symbols = @workspace.get_symbols(uri)
210
+ enclosing = symbols
211
+ .select { |s| [:function, :method].include?(s[:kind]) && s[:line] }
212
+ .select { |s| s[:line] <= line }
213
+ .max_by { |s| s[:line] }
214
+
215
+ return nil unless enclosing
216
+
217
+ kind = enclosing[:kind] == :method ? 6 : 12
218
+ { name: enclosing[:name], kind: kind }
219
+ end
220
+
221
+ def collect_call_callees(body, facts)
222
+ callees = {}
223
+ each_ast_node(body) do |node|
224
+ next unless node.is_a?(AST::Call)
225
+
226
+ callee_name = extract_callee_name(node.callee, facts)
227
+ next unless callee_name
228
+
229
+ range = {
230
+ start: { line: node.callee.respond_to?(:line) ? node.callee.line - 1 : 0,
231
+ character: node.callee.respond_to?(:column) ? node.callee.column - 1 : 0 },
232
+ end: { line: node.callee.respond_to?(:line) ? node.callee.line - 1 : 0,
233
+ character: (node.callee.respond_to?(:column) ? node.callee.column - 1 : 0) + callee_name.length },
234
+ }
235
+
236
+ callees[callee_name] ||= []
237
+ callees[callee_name] << range
238
+ end
239
+
240
+ callees
241
+ end
242
+
243
+ def extract_callee_name(callee, facts)
244
+ case callee
245
+ when AST::Identifier
246
+ callee.name
247
+ when AST::MemberAccess
248
+ if callee.receiver.is_a?(AST::Identifier)
249
+ mod_alias = callee.receiver.name
250
+ member = callee.member
251
+ if facts.imports.key?(mod_alias)
252
+ member
253
+ else
254
+ "#{mod_alias}.#{member}"
255
+ end
256
+ end
257
+ when AST::Specialization
258
+ extract_callee_name(callee.callee, facts) if callee.callee
259
+ end
260
+ end
261
+
262
+ def token_range(token)
263
+ line = token.respond_to?(:line) ? token.line - 1 : 0
264
+ col = token.respond_to?(:column) ? token.column - 1 : 0
265
+ len = token.respond_to?(:lexeme) ? token.lexeme.length : 0
266
+ {
267
+ start: { line: line, character: col },
268
+ end: { line: line, character: col + len },
269
+ }
270
+ end
271
+ end
272
+ end
273
+ end
274
+ end