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,779 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MilkTea
4
+ module LSP
5
+ class Server
6
+ module ServerDefinition
7
+ private
8
+
9
+ def is_builtin_name?(name, tokens, token_index)
10
+ return false unless token_index
11
+ builtin_hover_info(name, tokens, token_index)
12
+ end
13
+
14
+ def handle_definition(params)
15
+ handle_definition_request('textDocument/definition', params, error_label: 'definition')
16
+ end
17
+
18
+ def handle_declaration(params)
19
+ handle_definition_request('textDocument/declaration', params, error_label: 'declaration')
20
+ end
21
+
22
+ def handle_type_definition(params)
23
+ stages = new_perf_stages
24
+ total_start = stages ? monotonic_time : nil
25
+ uri = params.dig('textDocument', 'uri')
26
+ result_state = 'miss'
27
+
28
+ location = resolve_type_definition_location(params, stages: stages)
29
+ result_state = location ? 'hit' : 'miss'
30
+ location
31
+ rescue StandardError => e
32
+ result_state = 'error'
33
+ warn "Error in typeDefinition handler: #{e.message}"
34
+ nil
35
+ ensure
36
+ log_request_stage_breakdown('textDocument/typeDefinition', total_start, uri: uri, stages: stages, summary: "result=#{result_state}")
37
+ end
38
+
39
+ def handle_implementation(params)
40
+ stages = new_perf_stages
41
+ total_start = stages ? monotonic_time : nil
42
+ uri = params.dig('textDocument', 'uri')
43
+ result_state = 'miss'
44
+
45
+ locations = resolve_implementation_locations(params, stages: stages)
46
+ result_state = locations.empty? ? 'miss' : 'hit'
47
+ locations
48
+ rescue StandardError => e
49
+ result_state = 'error'
50
+ warn "Error in implementation handler: #{e.message}"
51
+ []
52
+ ensure
53
+ log_request_stage_breakdown('textDocument/implementation', total_start, uri: uri, stages: stages, summary: "result=#{result_state}")
54
+ end
55
+
56
+ def handle_definition_request(method_name, params, error_label:)
57
+ stages = new_perf_stages
58
+ total_start = stages ? monotonic_time : nil
59
+ uri = params.dig('textDocument', 'uri')
60
+ result_state = 'miss'
61
+
62
+ location = resolve_definition_location(params, stages: stages)
63
+ result_state = location ? 'hit' : 'miss'
64
+ location
65
+ rescue StandardError => e
66
+ result_state = 'error'
67
+ warn "Error in #{error_label} handler: #{e.message}"
68
+ nil
69
+ ensure
70
+ log_request_stage_breakdown(method_name, total_start, uri: uri, stages: stages, summary: "result=#{result_state}")
71
+ end
72
+
73
+ def resolve_definition_location(params, stages: nil)
74
+ uri = params['textDocument']['uri']
75
+ lsp_line = params['position']['line']
76
+ lsp_char = params['position']['character']
77
+
78
+ context = measure_perf_stage(stages, 'context') { token_context_at(uri, lsp_line, lsp_char) }
79
+ token = context&.fetch(:token, nil)
80
+ return nil unless token&.type == :identifier
81
+
82
+ tokens = context[:tokens]
83
+ token_index = context[:token_index]
84
+ return nil if token_index && module_declaration_info_at(tokens, token_index)
85
+
86
+ if token_index && named_argument_label_token?(tokens, token_index)
87
+ location = named_argument_definition_location(uri, token.lexeme, tokens, token_index, stages:)
88
+ return location if location
89
+ end
90
+
91
+ import_info = token_index ? measure_perf_stage(stages, 'import_path') { import_path_info_at(tokens, token_index) } : nil
92
+ if import_info
93
+ return module_definition_location(uri, import_info[:module_name])
94
+ end
95
+
96
+ facts = measure_perf_stage(stages, 'facts') { @workspace.get_facts(uri) }
97
+ if facts
98
+ facts_location = measure_perf_stage(stages, 'facts_lookup') do
99
+ location = nil
100
+ dot_receiver = @workspace.find_dot_receiver(uri, lsp_line, lsp_char)
101
+ dot_receiver_path = @workspace.find_dot_receiver_path(uri, lsp_line, lsp_char)
102
+ imported_module_name = dot_receiver ? (facts.imports[dot_receiver]&.name || imported_module_name_from_ast(uri, dot_receiver)) : nil
103
+
104
+ if token_index && (field_location = resolve_field_member_definition_location(uri, facts, tokens, token_index))
105
+ location = field_location
106
+ elsif token_index && (enum_member_location = resolve_enum_member_definition_location(uri, facts, tokens, token_index))
107
+ location = enum_member_location
108
+ elsif token_index && imported_module_name && module_member_access_info(tokens, token_index)
109
+ location = module_member_definition_location(uri, imported_module_name, token.lexeme)
110
+ location ||= module_definition_location(uri, imported_module_name)
111
+ elsif (type_method = resolve_static_type_receiver_method(facts, dot_receiver, dot_receiver_path, token.lexeme))
112
+ location = module_member_binding_location(uri, type_method[:module_name], token.lexeme, type_method[:binding]) ||
113
+ module_member_definition_location(uri, type_method[:module_name], token.lexeme) ||
114
+ module_definition_location(uri, type_method[:module_name])
115
+ elsif (binding = method_binding_at_token(facts, token))
116
+ location = module_member_binding_location(uri, facts.module_name, token.lexeme, binding)
117
+ location ||= module_member_definition_location(uri, facts.module_name, token.lexeme)
118
+ elsif (binding = facts.functions[token.lexeme])
119
+ location = module_member_binding_location(uri, facts.module_name, token.lexeme, binding)
120
+ location ||= module_member_definition_location(uri, facts.module_name, token.lexeme)
121
+ elsif facts.interfaces[token.lexeme]
122
+ location = module_member_definition_location(uri, facts.module_name, token.lexeme)
123
+ elsif facts.types.key?(token.lexeme)
124
+ location = module_member_definition_location(uri, facts.module_name, token.lexeme)
125
+ elsif (binding = facts.values[token.lexeme])
126
+ location = module_member_binding_location(uri, facts.module_name, token.lexeme, binding)
127
+ location ||= module_member_definition_location(uri, facts.module_name, token.lexeme)
128
+ else
129
+ if imported_module_name
130
+ location = module_member_definition_location(uri, imported_module_name, token.lexeme) || module_definition_location(uri, imported_module_name)
131
+ elsif facts.imports.key?(token.lexeme)
132
+ module_name = facts.imports.fetch(token.lexeme).name
133
+ location = module_member_definition_location(uri, module_name, token.lexeme)
134
+ location ||= module_definition_location(uri, module_name)
135
+ elsif (module_name = imported_module_name_from_ast(uri, token.lexeme))
136
+ location = module_definition_location(uri, module_name)
137
+ end
138
+ end
139
+
140
+ location
141
+ end
142
+
143
+ return facts_location if facts_location
144
+ end
145
+
146
+ return nil if token_index && is_builtin_name?(token.lexeme, tokens, token_index)
147
+
148
+ if facts && token_index && !facts_location
149
+ local_location = measure_perf_stage(stages, 'local_binding') do
150
+ name = token.lexeme
151
+ line = lsp_line + 1
152
+ char = lsp_char + 1
153
+ local_binding = resolve_local_hover_binding(facts, name, line, char)
154
+ next nil unless local_binding
155
+
156
+ ast_node = local_binding.respond_to?(:ast) ? local_binding.ast : nil
157
+ if ast_node&.line && ast_node.respond_to?(:column) && ast_node.column
158
+ {
159
+ uri: uri,
160
+ range: {
161
+ start: { line: ast_node.line - 1, character: ast_node.column - 1 },
162
+ end: { line: ast_node.line - 1, character: ast_node.column - 1 + name.length }
163
+ }
164
+ }
165
+ else
166
+ declaration_node = find_local_declaration_ast_node(uri, name, line)
167
+ if declaration_node
168
+ {
169
+ uri: uri,
170
+ range: {
171
+ start: { line: declaration_node.line - 1, character: declaration_node.column - 1 },
172
+ end: { line: declaration_node.line - 1, character: declaration_node.column - 1 + name.length }
173
+ }
174
+ }
175
+ end
176
+ end
177
+ end
178
+ return local_location if local_location
179
+ end
180
+
181
+ found = measure_perf_stage(stages, 'global_lookup') do
182
+ @workspace.find_definition_token_global(
183
+ token.lexeme,
184
+ preferred_uri: uri,
185
+ before_line: lsp_line + 1,
186
+ before_char: lsp_char + 1,
187
+ )
188
+ end
189
+ return nil unless found
190
+
191
+ {
192
+ uri: found[:uri],
193
+ range: token_to_range(found[:token])
194
+ }
195
+ end
196
+
197
+ def find_local_declaration_ast_node(uri, name, before_line)
198
+ ast = @workspace.get_ast(uri)
199
+ return nil unless ast
200
+
201
+ result = nil
202
+ find_local_decl_node(ast, name, before_line) { |node| result = node; true }
203
+ result
204
+ end
205
+
206
+ def find_local_decl_node(node, name, before_line, &block)
207
+ return false if node.nil?
208
+
209
+ if (node.is_a?(AST::LocalDecl) || node.is_a?(AST::ForBinding)) && node.name == name && node.line && node.line < before_line
210
+ return true if yield node
211
+ end
212
+
213
+ if node.is_a?(Array)
214
+ node.each { |item| return true if find_local_decl_node(item, name, before_line, &block) }
215
+ return false
216
+ end
217
+
218
+ return false unless node.class.name&.start_with?("MilkTea::AST::")
219
+
220
+ if node.respond_to?(:members)
221
+ node.members.each do |member|
222
+ next unless member.is_a?(Symbol)
223
+ return true if find_local_decl_node(node.public_send(member), name, before_line, &block)
224
+ end
225
+ end
226
+
227
+ false
228
+ end
229
+
230
+ def resolve_field_member_definition_location(current_uri, facts, tokens, token_index)
231
+ chain = member_access_chain_at(tokens, token_index)
232
+ return nil unless chain
233
+
234
+ hovered_segment = chain[:segments].find { |segment| segment[:token_index] == token_index }
235
+ return nil unless hovered_segment && hovered_segment[:position].positive?
236
+
237
+ current_type = resolve_dot_receiver_value_type(
238
+ facts,
239
+ chain[:segments].first[:name],
240
+ chain[:line],
241
+ chain[:char],
242
+ )
243
+ unless current_type
244
+ first_name = chain[:segments].first[:name]
245
+ current_type = facts.types[first_name]
246
+ end
247
+ return nil unless current_type
248
+
249
+ chain[:segments][1..hovered_segment[:position]].each do |segment|
250
+ field_receiver_type = project_field_receiver_type_for_completion(current_type)
251
+ if field_receiver_type.respond_to?(:field) && (field_type = field_receiver_type.field(segment[:name]))
252
+ return field_definition_location(current_uri, field_receiver_type, segment[:name]) if segment[:token_index] == token_index
253
+
254
+ current_type = field_type
255
+ next
256
+ end
257
+
258
+ if current_type.respond_to?(:nested_types) && (nested = current_type.nested_types[segment[:name]])
259
+ if segment[:token_index] == token_index
260
+ decl = nested.respond_to?(:ast_declaration) ? nested.ast_declaration : nil
261
+ if decl&.line && decl.respond_to?(:column) && decl.column
262
+ return {
263
+ uri: current_uri,
264
+ range: {
265
+ start: { line: decl.line - 1, character: decl.column - 1 },
266
+ end: { line: decl.line - 1, character: decl.column - 1 + segment[:name].length },
267
+ },
268
+ }
269
+ end
270
+ return nil
271
+ end
272
+ current_type = nested
273
+ next
274
+ end
275
+
276
+ if segment[:token_index] == token_index
277
+ method_receiver_type = project_method_receiver_type_for_completion(current_type)
278
+ method_info = member_method_info_for_receiver_type(facts, method_receiver_type, segment[:name])
279
+ return nil unless method_info
280
+
281
+ return module_member_binding_location(current_uri, method_info[:module_name], segment[:name], method_info[:binding]) ||
282
+ module_member_definition_location(current_uri, method_info[:module_name], segment[:name])
283
+ end
284
+
285
+ break
286
+ end
287
+
288
+ nil
289
+ end
290
+
291
+ def resolve_type_definition_location(params, stages: nil)
292
+ uri = params['textDocument']['uri']
293
+ lsp_line = params['position']['line']
294
+ lsp_char = params['position']['character']
295
+
296
+ context = measure_perf_stage(stages, 'context') { token_context_at(uri, lsp_line, lsp_char) }
297
+ token = context&.fetch(:token, nil)
298
+ return nil unless token&.type == :identifier
299
+
300
+ name = token.lexeme
301
+ tokens = context[:tokens]
302
+ token_index = context[:token_index]
303
+
304
+ import_info = token_index ? measure_perf_stage(stages, 'import_path') { import_path_info_at(tokens, token_index) } : nil
305
+ if import_info
306
+ return module_definition_location(uri, import_info[:module_name])
307
+ end
308
+
309
+ facts = measure_perf_stage(stages, 'facts') { @workspace.get_facts(uri) }
310
+ return nil unless facts
311
+
312
+ dot_receiver = @workspace.find_dot_receiver(uri, lsp_line, lsp_char)
313
+ imported_module_name = dot_receiver ? (facts.imports[dot_receiver]&.name || imported_module_name_from_ast(uri, dot_receiver)) : nil
314
+
315
+ if facts.types.key?(name) || facts.interfaces.key?(name)
316
+ return module_member_definition_location(uri, facts.module_name, name)
317
+ end
318
+
319
+ if token_index && imported_module_name && module_member_access_info(tokens, token_index)
320
+ return module_member_definition_location(uri, imported_module_name, name) ||
321
+ module_definition_location(uri, imported_module_name)
322
+ end
323
+
324
+ if imported_module_name && (facts.types.key?(imported_module_name) || facts.interfaces.key?(imported_module_name))
325
+ return module_member_definition_location(uri, imported_module_name, name) ||
326
+ module_definition_location(uri, imported_module_name)
327
+ end
328
+
329
+ handle_definition_request('textDocument/typeDefinition', params, error_label: 'typeDefinition')
330
+ end
331
+
332
+ def resolve_implementation_locations(params, stages: nil)
333
+ uri = params['textDocument']['uri']
334
+ lsp_line = params['position']['line']
335
+ lsp_char = params['position']['character']
336
+
337
+ token = measure_perf_stage(stages, 'token') { @workspace.find_token_at(uri, lsp_line, lsp_char) }
338
+ return [] unless token&.type == :identifier
339
+
340
+ facts = measure_perf_stage(stages, 'facts') { @workspace.get_facts(uri) }
341
+ return [] unless facts
342
+
343
+ target = measure_perf_stage(stages, 'target_lookup') { resolve_interface_method_target_at_token(facts, token) }
344
+ if target
345
+ return measure_perf_stage(stages, 'implementation_lookup') do
346
+ interface_method_implementation_locations(target[:interface], target[:method])
347
+ end
348
+ end
349
+
350
+ interface_binding = measure_perf_stage(stages, 'binding_lookup') do
351
+ resolve_interface_binding_at_position(uri, facts, token, lsp_line, lsp_char)
352
+ end
353
+ return [] unless interface_binding
354
+
355
+ measure_perf_stage(stages, 'implementation_lookup') { interface_implementation_locations(interface_binding) }
356
+ end
357
+
358
+ def module_definition_location(current_uri, module_name)
359
+ path = module_path_for_name(current_uri, module_name)
360
+ return nil unless path
361
+
362
+ {
363
+ uri: path_to_uri(File.expand_path(path)),
364
+ range: {
365
+ start: { line: 0, character: 0 },
366
+ end: { line: 0, character: 0 }
367
+ }
368
+ }
369
+ end
370
+
371
+ def module_member_definition_location(current_uri, module_name, member_name)
372
+ path = module_path_for_name(current_uri, module_name)
373
+ return nil unless path
374
+
375
+ token = find_definition_token_in_file(path, member_name)
376
+ return nil unless token
377
+
378
+ {
379
+ uri: path_to_uri(File.expand_path(path)),
380
+ range: token_to_range(token)
381
+ }
382
+ end
383
+
384
+ def field_definition_location(current_uri, receiver_type, field_name)
385
+ owner_type = field_owner_type(receiver_type)
386
+ return nil unless owner_type&.respond_to?(:name)
387
+
388
+ path = module_path_for_name(current_uri, owner_type.module_name)
389
+ return nil unless path
390
+
391
+ token = find_field_token_in_type(path, owner_type.name, field_name, current_uri: current_uri)
392
+ return nil unless token
393
+
394
+ {
395
+ uri: path_to_uri(File.expand_path(path)),
396
+ range: token_to_range(token)
397
+ }
398
+ end
399
+
400
+ def enum_member_definition_location(current_uri, module_name, type_name, member_name)
401
+ path = module_path_for_name(current_uri, module_name)
402
+ return nil unless path
403
+
404
+ token = find_enum_member_token_in_file(path, type_name, member_name)
405
+ return nil unless token
406
+
407
+ {
408
+ uri: path_to_uri(File.expand_path(path)),
409
+ range: token_to_range(token)
410
+ }
411
+ end
412
+
413
+ def module_member_binding_location(current_uri, module_name, member_name, binding)
414
+ path = module_path_for_name(current_uri, module_name)
415
+ return nil unless path
416
+
417
+ ast = binding.respond_to?(:ast) ? binding.ast : nil
418
+ if ast&.line && ast.respond_to?(:column) && ast.column
419
+ start_line = ast.line - 1
420
+ start_char = ast.column - 1
421
+
422
+ return {
423
+ uri: path_to_uri(File.expand_path(path)),
424
+ range: {
425
+ start: { line: start_line, character: start_char },
426
+ end: { line: start_line, character: start_char + member_name.length }
427
+ }
428
+ }
429
+ end
430
+
431
+ module_member_definition_location(current_uri, module_name, member_name)
432
+ end
433
+
434
+ def module_path_for_name(current_uri, module_name)
435
+ current_path = uri_to_path(current_uri)
436
+ return nil unless current_path
437
+ return current_path if module_name.nil? || module_name.empty?
438
+
439
+ current_facts = @workspace.get_facts(current_uri)
440
+ return current_path if current_facts&.module_name == module_name
441
+
442
+ resolution = DependencyResolution.resolve(current_path, mode: @workspace.dependency_resolution_mode)
443
+ module_roots = if resolution.ok?
444
+ MilkTea::ModuleRoots.roots_for_path(current_path, locked: resolution.locked)
445
+ else
446
+ MilkTea::ModuleRoots.roots_for_path(current_path)
447
+ end
448
+ relative_path = File.join(*module_name.split('.')) + '.mt'
449
+ resolved_path = module_roots.lazy.map { |root| File.join(root, relative_path) }.find { |candidate| File.file?(candidate) }
450
+ return resolved_path if resolved_path
451
+
452
+ workspace_root = @root_uri ? uri_to_path(@root_uri) : nil
453
+ return nil unless workspace_root && File.directory?(workspace_root)
454
+
455
+ workspace_candidate = File.join(workspace_root, relative_path)
456
+ return workspace_candidate if File.file?(workspace_candidate)
457
+
458
+ nil
459
+ rescue PackageLockError
460
+ module_roots = MilkTea::ModuleRoots.roots_for_path(current_path)
461
+ relative_path = File.join(*module_name.split('.')) + '.mt'
462
+ resolved_path = module_roots.lazy.map { |root| File.join(root, relative_path) }.find { |candidate| File.file?(candidate) }
463
+ return resolved_path if resolved_path
464
+
465
+ workspace_root = @root_uri ? uri_to_path(@root_uri) : nil
466
+ return nil unless workspace_root && File.directory?(workspace_root)
467
+
468
+ workspace_candidate = File.join(workspace_root, relative_path)
469
+ return workspace_candidate if File.file?(workspace_candidate)
470
+
471
+ nil
472
+ end
473
+
474
+ def imported_module_name_from_ast(uri, alias_name)
475
+ return nil if alias_name.nil? || alias_name.empty?
476
+
477
+ ast = @workspace.get_ast(uri)
478
+ return nil unless ast
479
+
480
+ import = ast.imports.find do |entry|
481
+ resolved_alias = entry.alias_name || entry.path.parts.last
482
+ resolved_alias == alias_name
483
+ end
484
+ import&.path&.to_s
485
+ rescue StandardError
486
+ nil
487
+ end
488
+
489
+ def find_definition_token_in_file(path, name)
490
+ tokens = definition_file_tokens(path)
491
+
492
+ tokens.each_cons(2) do |kw_tok, id_tok|
493
+ next unless MilkTea::LSP::Workspace::DEFINITION_KEYWORDS.include?(kw_tok.type)
494
+ next unless id_tok.type == :identifier && id_tok.lexeme == name
495
+
496
+ return id_tok
497
+ end
498
+
499
+ nil
500
+ rescue StandardError
501
+ nil
502
+ end
503
+
504
+ def find_field_token_in_type(path, type_name, field_name, current_uri: nil)
505
+ tokens = definition_lookup_tokens(path, current_uri: current_uri)
506
+
507
+ tokens.each_with_index do |token, index|
508
+ next unless [:struct, :union].include?(token.type)
509
+
510
+ name_index = next_non_trivia_token_index(tokens, index + 1)
511
+ next unless name_index && tokens[name_index].type == :identifier && tokens[name_index].lexeme == type_name
512
+
513
+ field_token = find_field_token_in_body(tokens, index, field_name)
514
+ return field_token if field_token
515
+ end
516
+
517
+ nil
518
+ rescue StandardError
519
+ nil
520
+ end
521
+
522
+ def find_field_token_in_body(tokens, header_index, field_name)
523
+ header = tokens[header_index]
524
+ i = header_index + 1
525
+
526
+ while i < tokens.length
527
+ token = tokens[i]
528
+
529
+ if token.line > header.line && ![:newline, :indent, :dedent, :eof].include?(token.type) &&
530
+ first_non_trivia_token_on_line?(tokens, i) && token.column <= header.column
531
+ break
532
+ end
533
+
534
+ if token.type == :identifier && token.lexeme == field_name && token.line > header.line &&
535
+ first_non_trivia_token_on_line?(tokens, i) && token.column > header.column
536
+ colon_index = next_non_trivia_token_index(tokens, i + 1)
537
+ return token if colon_index && tokens[colon_index].type == :colon
538
+ end
539
+
540
+ i += 1
541
+ end
542
+
543
+ nil
544
+ end
545
+
546
+ def find_enum_member_token_in_file(path, type_name, member_name)
547
+ tokens = definition_file_tokens(path)
548
+
549
+ tokens.each_with_index do |token, index|
550
+ next unless [:enum, :flags].include?(token.type)
551
+
552
+ name_index = next_non_trivia_token_index(tokens, index + 1)
553
+ next unless name_index && tokens[name_index].type == :identifier && tokens[name_index].lexeme == type_name
554
+
555
+ member_token = find_enum_member_token_in_body(tokens, index, member_name)
556
+ return member_token if member_token
557
+ end
558
+
559
+ nil
560
+ rescue StandardError
561
+ nil
562
+ end
563
+
564
+ def find_enum_member_token_in_body(tokens, header_index, member_name)
565
+ header = tokens[header_index]
566
+ i = header_index + 1
567
+
568
+ while i < tokens.length
569
+ token = tokens[i]
570
+
571
+ if token.line > header.line && ![:newline, :indent, :dedent, :eof].include?(token.type) &&
572
+ first_non_trivia_token_on_line?(tokens, i) && token.column <= header.column
573
+ break
574
+ end
575
+
576
+ if token.type == :identifier && token.lexeme == member_name && token.line > header.line &&
577
+ first_non_trivia_token_on_line?(tokens, i) && token.column > header.column
578
+ return token
579
+ end
580
+
581
+ i += 1
582
+ end
583
+
584
+ nil
585
+ end
586
+
587
+ def enum_member_value_text(current_uri, module_name, type_name, member_name)
588
+ path = module_path_for_name(current_uri, module_name)
589
+ return nil unless path
590
+
591
+ declaration = definition_file_ast(path)&.declarations&.find do |decl|
592
+ (decl.is_a?(AST::EnumDecl) || decl.is_a?(AST::FlagsDecl)) && decl.name == type_name
593
+ end
594
+ return nil unless declaration
595
+
596
+ member = declaration.members.find { |candidate| candidate.name == member_name }
597
+ return nil unless member&.value
598
+
599
+ MilkTea::PrettyPrinter::ASTFormatter.new.send(:render_expression, member.value)
600
+ rescue StandardError
601
+ nil
602
+ end
603
+
604
+ def definition_file_mtime_key(path)
605
+ stat = File.stat(path)
606
+ "#{stat.mtime.to_i}:#{stat.mtime.nsec}"
607
+ rescue StandardError
608
+ 'missing'
609
+ end
610
+
611
+ def definition_file_tokens(path, mtime_key: nil)
612
+ cache_key = "#{path}:#{mtime_key || definition_file_mtime_key(path)}"
613
+ @definition_file_token_cache[cache_key] ||= begin
614
+ MilkTea::Lexer.lex(File.read(path), path: path_to_uri(path))
615
+ end
616
+ end
617
+
618
+ def definition_lookup_tokens(path, current_uri: nil)
619
+ current_path = current_uri ? uri_to_path(current_uri) : nil
620
+ if current_path && File.expand_path(current_path) == File.expand_path(path)
621
+ workspace_tokens = @workspace.get_tokens(current_uri)
622
+ return workspace_tokens if workspace_tokens
623
+ end
624
+
625
+ definition_file_tokens(path)
626
+ end
627
+
628
+ def definition_file_ast(path)
629
+ mtime_key = definition_file_mtime_key(path)
630
+ cache_key = "#{path}:#{mtime_key}"
631
+ @definition_file_ast_cache[cache_key] ||= begin
632
+ MilkTea::Parser.parse(nil, path: path_to_uri(path), tokens: definition_file_tokens(path, mtime_key: mtime_key))
633
+ end
634
+ end
635
+
636
+ def resolve_interface_binding_at_position(uri, facts, token, lsp_line, lsp_char)
637
+ binding = facts.interfaces[token.lexeme]
638
+ return binding if binding
639
+
640
+ dot_receiver = @workspace.find_dot_receiver(uri, lsp_line, lsp_char)
641
+ return nil unless dot_receiver
642
+
643
+ facts.imports[dot_receiver]&.interfaces&.fetch(token.lexeme, nil)
644
+ end
645
+
646
+ def resolve_interface_method_target_at_token(facts, token)
647
+ facts.interfaces.each_value do |interface_binding|
648
+ method_binding = interface_binding.methods[token.lexeme]
649
+ next unless method_binding
650
+ next unless method_binding.ast.line == token.line
651
+ next unless method_binding.ast.respond_to?(:column) && method_binding.ast.column == token.column
652
+
653
+ return { interface: interface_binding, method: method_binding }
654
+ end
655
+
656
+ nil
657
+ end
658
+
659
+ def interface_implementation_locations(interface_binding)
660
+ seen = Set.new
661
+ @workspace.all_documents.filter_map do |doc_uri|
662
+ facts = @workspace.get_facts(doc_uri)
663
+ next unless facts
664
+
665
+ facts.implemented_interfaces.each_with_object([]) do |(receiver_type, interfaces), locations|
666
+ next unless interfaces.any? { |candidate| same_interface_binding?(candidate, interface_binding) }
667
+
668
+ location = interface_receiver_definition_location(doc_uri, receiver_type)
669
+ next unless location
670
+
671
+ key = [location[:uri], location.dig(:range, :start, :line), location.dig(:range, :start, :character)]
672
+ next if seen.include?(key)
673
+
674
+ seen << key
675
+ locations << location
676
+ end
677
+ end.flatten
678
+ end
679
+
680
+ def interface_method_implementation_locations(interface_binding, interface_method)
681
+ seen = Set.new
682
+ @workspace.all_documents.filter_map do |doc_uri|
683
+ facts = @workspace.get_facts(doc_uri)
684
+ next unless facts
685
+
686
+ facts.implemented_interfaces.each_with_object([]) do |(receiver_type, interfaces), locations|
687
+ next unless interfaces.any? { |candidate| same_interface_binding?(candidate, interface_binding) }
688
+
689
+ method = methods_for_receiver_type(facts, receiver_type)[interface_method.name]
690
+ next unless method
691
+
692
+ module_name = receiver_module_name(receiver_type)
693
+ location = module_member_binding_location(doc_uri, module_name, interface_method.name, method)
694
+ location ||= module_member_definition_location(doc_uri, module_name, interface_method.name)
695
+ next unless location
696
+
697
+ key = [location[:uri], location.dig(:range, :start, :line), location.dig(:range, :start, :character)]
698
+ next if seen.include?(key)
699
+
700
+ seen << key
701
+ locations << location
702
+ end
703
+ end.flatten
704
+ end
705
+
706
+ def interface_receiver_definition_location(current_uri, receiver_type)
707
+ receiver_type = receiver_type.definition if receiver_type.is_a?(Types::StructInstance)
708
+
709
+ if receiver_type.module_name.nil? || receiver_type.module_name.empty?
710
+ token = local_type_definition_token(current_uri, receiver_type.name)
711
+ token ||= @workspace.find_definition_token(current_uri, receiver_type.name)
712
+ return { uri: current_uri, range: token_to_range(token) } if token
713
+ end
714
+
715
+ module_member_definition_location(current_uri, receiver_type.module_name, receiver_type.name)
716
+ end
717
+
718
+ def receiver_module_name(receiver_type)
719
+ receiver_type = receiver_type.definition if receiver_type.is_a?(Types::StructInstance)
720
+
721
+ receiver_type.module_name
722
+ end
723
+
724
+ def local_type_definition_token(uri, name)
725
+ tokens = @workspace.get_tokens(uri)
726
+ return nil unless tokens
727
+
728
+ tokens.each_cons(2) do |kw_tok, id_tok|
729
+ next unless [:struct, :opaque, :enum, :flags, :variant, :union].include?(kw_tok.type)
730
+ next unless id_tok.type == :identifier && id_tok.lexeme == name
731
+
732
+ return id_tok
733
+ end
734
+
735
+ nil
736
+ end
737
+
738
+ def same_interface_binding?(left, right)
739
+ left.name == right.name && left.module_name == right.module_name
740
+ end
741
+ end
742
+ def named_argument_definition_location(uri, name, tokens, token_index, stages: nil)
743
+ return nil unless named_argument_label_token?(tokens, token_index)
744
+
745
+ callee_name = named_argument_callee_name(tokens, token_index)
746
+ return nil unless callee_name
747
+
748
+ facts = measure_perf_stage(stages, 'definition_facts') { @workspace.get_facts(uri) }
749
+ return nil unless facts
750
+
751
+ func = facts.functions[callee_name]
752
+ unless func
753
+ facts.methods.each_value do |methods|
754
+ method = methods[callee_name] || methods["static:#{callee_name}"]
755
+ if method
756
+ func = method
757
+ break
758
+ end
759
+ end
760
+ end
761
+ return nil unless func
762
+
763
+ param = func.ast.params.find { |p| p.name == name }
764
+ return nil unless param
765
+
766
+ range = ast_name_range(name, param.line, param.column)
767
+ return nil unless range
768
+
769
+ {
770
+ uri: uri,
771
+ range: {
772
+ start: { line: range[:line] - 1, character: range[:column] - 1 },
773
+ end: { line: range[:line] - 1, character: range[:column] - 1 + name.length },
774
+ },
775
+ }
776
+ end
777
+ end
778
+ end
779
+ end