mt-lang 0.3.9 → 0.3.11

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 (204) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -0
  3. data/docs/language-design.md +1 -2
  4. data/docs/language-manual.md +12 -1
  5. data/lib/milk_tea/base.rb +1 -2
  6. data/lib/milk_tea/bindings/bindgen/ast_parser.rb +0 -2
  7. data/lib/milk_tea/bindings/bindgen/declaration.rb +0 -2
  8. data/lib/milk_tea/bindings/bindgen/emitter.rb +0 -2
  9. data/lib/milk_tea/bindings/bindgen/overrides.rb +0 -2
  10. data/lib/milk_tea/bindings/bindgen/type_mapper.rb +51 -53
  11. data/lib/milk_tea/bindings/imported_bindings/generator.rb +7 -9
  12. data/lib/milk_tea/bindings/imported_bindings/method_source.rb +0 -2
  13. data/lib/milk_tea/bindings/imported_bindings/naming.rb +6 -8
  14. data/lib/milk_tea/bindings/imported_bindings.rb +0 -2
  15. data/lib/milk_tea/bindings/opengl_registry.rb +3 -7
  16. data/lib/milk_tea/bindings/steamworks.rb +12 -16
  17. data/lib/milk_tea/bindings/upstream_sources.rb +10 -14
  18. data/lib/milk_tea/bindings/vendored_box2d.rb +10 -12
  19. data/lib/milk_tea/bindings/vendored_cjson.rb +6 -8
  20. data/lib/milk_tea/bindings/vendored_flecs.rb +10 -12
  21. data/lib/milk_tea/bindings/vendored_glfw.rb +11 -13
  22. data/lib/milk_tea/bindings/vendored_libuv.rb +11 -13
  23. data/lib/milk_tea/bindings/vendored_pcre2.rb +11 -13
  24. data/lib/milk_tea/bindings/vendored_raylib.rb +6 -10
  25. data/lib/milk_tea/bindings/vendored_sdl3.rb +11 -13
  26. data/lib/milk_tea/bindings/vendored_steamworks.rb +1 -5
  27. data/lib/milk_tea/core/ast.rb +109 -51
  28. data/lib/milk_tea/core/async_runtime_installer.rb +0 -2
  29. data/lib/milk_tea/core/bindings/attribute_binding.rb +1 -3
  30. data/lib/milk_tea/core/c_backend/aggregate_utils.rb +147 -84
  31. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +394 -396
  32. data/lib/milk_tea/core/c_backend/expressions.rb +489 -476
  33. data/lib/milk_tea/core/c_backend/feature_detection.rb +446 -437
  34. data/lib/milk_tea/core/c_backend/format_helpers.rb +0 -2
  35. data/lib/milk_tea/core/c_backend/reachability.rb +339 -341
  36. data/lib/milk_tea/core/c_backend/reinterpret.rb +231 -217
  37. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +531 -526
  38. data/lib/milk_tea/core/c_backend/statements.rb +528 -530
  39. data/lib/milk_tea/core/c_backend/type_collectors.rb +1344 -1346
  40. data/lib/milk_tea/core/c_backend/type_declaration.rb +215 -213
  41. data/lib/milk_tea/core/c_backend/type_system.rb +347 -349
  42. data/lib/milk_tea/core/c_backend.rb +17 -9
  43. data/lib/milk_tea/core/compatibility_helpers.rb +0 -2
  44. data/lib/milk_tea/core/compile_time/const_eval.rb +7 -12
  45. data/lib/milk_tea/core/compile_time.rb +32 -38
  46. data/lib/milk_tea/core/control_flow/builder.rb +3 -5
  47. data/lib/milk_tea/core/control_flow/constant_propagation.rb +14 -17
  48. data/lib/milk_tea/core/control_flow/definite_assignment.rb +0 -1
  49. data/lib/milk_tea/core/ir.rb +10 -10
  50. data/lib/milk_tea/core/lexer/character_classes.rb +5 -7
  51. data/lib/milk_tea/core/lexer/format_strings.rb +7 -9
  52. data/lib/milk_tea/core/lexer/heredocs.rb +22 -22
  53. data/lib/milk_tea/core/lexer/indentation.rb +0 -2
  54. data/lib/milk_tea/core/lexer/numbers.rb +0 -2
  55. data/lib/milk_tea/core/lexer/recovery.rb +0 -2
  56. data/lib/milk_tea/core/lexer/strings.rb +0 -2
  57. data/lib/milk_tea/core/lexer/symbols.rb +0 -2
  58. data/lib/milk_tea/core/lexer/trivia.rb +0 -2
  59. data/lib/milk_tea/core/lexer.rb +0 -2
  60. data/lib/milk_tea/core/lowering/async/analysis.rb +0 -2
  61. data/lib/milk_tea/core/lowering/async/lowering.rb +1264 -1265
  62. data/lib/milk_tea/core/lowering/async/normalization.rb +5 -3
  63. data/lib/milk_tea/core/lowering/async.rb +627 -630
  64. data/lib/milk_tea/core/lowering/block.rb +413 -416
  65. data/lib/milk_tea/core/lowering/calls.rb +1381 -1384
  66. data/lib/milk_tea/core/lowering/declarations.rb +180 -183
  67. data/lib/milk_tea/core/lowering/dyn.rb +48 -50
  68. data/lib/milk_tea/core/lowering/events.rb +983 -985
  69. data/lib/milk_tea/core/lowering/expressions.rb +1513 -1518
  70. data/lib/milk_tea/core/lowering/foreign_cstr.rb +181 -184
  71. data/lib/milk_tea/core/lowering/functions.rb +185 -188
  72. data/lib/milk_tea/core/lowering/loops.rb +1028 -1031
  73. data/lib/milk_tea/core/lowering/proc.rb +363 -366
  74. data/lib/milk_tea/core/lowering/resolve.rb +2133 -2135
  75. data/lib/milk_tea/core/lowering/scans.rb +179 -181
  76. data/lib/milk_tea/core/lowering/str_buffer.rb +102 -105
  77. data/lib/milk_tea/core/lowering/utils.rb +1040 -1044
  78. data/lib/milk_tea/core/lowering.rb +5 -7
  79. data/lib/milk_tea/core/module_binder.rb +0 -2
  80. data/lib/milk_tea/core/module_loader/errors.rb +4 -2
  81. data/lib/milk_tea/core/module_loader.rb +113 -20
  82. data/lib/milk_tea/core/module_path_resolver.rb +0 -2
  83. data/lib/milk_tea/core/module_roots.rb +6 -8
  84. data/lib/milk_tea/core/parser/attributes.rb +0 -2
  85. data/lib/milk_tea/core/parser/blocks.rb +0 -2
  86. data/lib/milk_tea/core/parser/declarations.rb +7 -9
  87. data/lib/milk_tea/core/parser/expressions.rb +15 -10
  88. data/lib/milk_tea/core/parser/recovery.rb +0 -2
  89. data/lib/milk_tea/core/parser/statements.rb +8 -2
  90. data/lib/milk_tea/core/parser/types.rb +12 -14
  91. data/lib/milk_tea/core/parser.rb +0 -2
  92. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +31 -33
  93. data/lib/milk_tea/core/pretty_printer/base_formatter.rb +0 -2
  94. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +0 -2
  95. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +0 -2
  96. data/lib/milk_tea/core/semantic_analyzer/attributes.rb +14 -16
  97. data/lib/milk_tea/core/semantic_analyzer/calls.rb +0 -2
  98. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1 -14
  99. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +18 -21
  100. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +0 -3
  101. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +14 -17
  102. data/lib/milk_tea/core/semantic_analyzer/generics.rb +57 -59
  103. data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +0 -2
  104. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +5 -9
  105. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +0 -3
  106. data/lib/milk_tea/core/semantic_analyzer/statements.rb +35 -40
  107. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +10 -12
  108. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +0 -6
  109. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +106 -109
  110. data/lib/milk_tea/core/semantic_analyzer.rb +17 -8
  111. data/lib/milk_tea/core/types/layout.rb +21 -22
  112. data/lib/milk_tea/core/types/predicates.rb +0 -2
  113. data/lib/milk_tea/core/types/registry.rb +16 -18
  114. data/lib/milk_tea/core/types/visitor.rb +0 -4
  115. data/lib/milk_tea/dap/backends/lldb_dap.rb +0 -2
  116. data/lib/milk_tea/dap/protocol.rb +1 -1
  117. data/lib/milk_tea/dap/server/breakpoints.rb +0 -2
  118. data/lib/milk_tea/dap/server/debug_map.rb +0 -2
  119. data/lib/milk_tea/dap/server/handlers.rb +0 -2
  120. data/lib/milk_tea/dap/server/launch.rb +0 -2
  121. data/lib/milk_tea/dap/server/lldb_backend.rb +8 -10
  122. data/lib/milk_tea/dap/server/pause_diagnostics.rb +14 -16
  123. data/lib/milk_tea/dap/server/utilities.rb +0 -2
  124. data/lib/milk_tea/dap/server/wire.rb +0 -2
  125. data/lib/milk_tea/dap/server.rb +0 -2
  126. data/lib/milk_tea/lsp/diagnostics.rb +16 -16
  127. data/lib/milk_tea/lsp/server/call_hierarchy.rb +11 -12
  128. data/lib/milk_tea/lsp/server/code_actions.rb +355 -357
  129. data/lib/milk_tea/lsp/server/code_lens.rb +23 -25
  130. data/lib/milk_tea/lsp/server/completion.rb +873 -875
  131. data/lib/milk_tea/lsp/server/configuration.rb +129 -131
  132. data/lib/milk_tea/lsp/server/debug_info.rb +0 -2
  133. data/lib/milk_tea/lsp/server/definition.rb +564 -565
  134. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +191 -193
  135. data/lib/milk_tea/lsp/server/execute_command.rb +0 -2
  136. data/lib/milk_tea/lsp/server/folding_range.rb +0 -4
  137. data/lib/milk_tea/lsp/server/formatting.rb +478 -480
  138. data/lib/milk_tea/lsp/server/hover.rb +1282 -1284
  139. data/lib/milk_tea/lsp/server/inlay_hints.rb +179 -181
  140. data/lib/milk_tea/lsp/server/lifecycle.rb +192 -194
  141. data/lib/milk_tea/lsp/server/linked_editing_range.rb +0 -2
  142. data/lib/milk_tea/lsp/server/on_type_formatting.rb +0 -2
  143. data/lib/milk_tea/lsp/server/progress.rb +0 -2
  144. data/lib/milk_tea/lsp/server/references.rb +316 -318
  145. data/lib/milk_tea/lsp/server/rename.rb +445 -447
  146. data/lib/milk_tea/lsp/server/selection_range.rb +0 -4
  147. data/lib/milk_tea/lsp/server/semantic_tokens.rb +1376 -1314
  148. data/lib/milk_tea/lsp/server/signature_help.rb +146 -148
  149. data/lib/milk_tea/lsp/server/text_documents.rb +99 -101
  150. data/lib/milk_tea/lsp/server/type_hierarchy.rb +2 -4
  151. data/lib/milk_tea/lsp/server/utilities.rb +397 -399
  152. data/lib/milk_tea/lsp/server.rb +0 -3
  153. data/lib/milk_tea/lsp/workspace/analysis.rb +40 -42
  154. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +4 -10
  155. data/lib/milk_tea/lsp/workspace/utilities.rb +19 -19
  156. data/lib/milk_tea/packages/atomic_write.rb +4 -5
  157. data/lib/milk_tea/packages/dependency_solver.rb +15 -15
  158. data/lib/milk_tea/packages/lock.rb +4 -4
  159. data/lib/milk_tea/packages/manager_cli.rb +1 -1
  160. data/lib/milk_tea/packages/manifest.rb +3 -3
  161. data/lib/milk_tea/packages/registry_store.rb +4 -6
  162. data/lib/milk_tea/packages/services.rb +16 -16
  163. data/lib/milk_tea/packages/source_cache.rb +4 -6
  164. data/lib/milk_tea/packages/source_resolver.rb +44 -44
  165. data/lib/milk_tea/packages/version.rb +8 -8
  166. data/lib/milk_tea/tooling/build.rb +3 -7
  167. data/lib/milk_tea/tooling/build_cache.rb +0 -2
  168. data/lib/milk_tea/tooling/cli.rb +10 -4
  169. data/lib/milk_tea/tooling/debug_info_formatter.rb +31 -33
  170. data/lib/milk_tea/tooling/debug_map.rb +8 -10
  171. data/lib/milk_tea/tooling/docs_app.rb +0 -2
  172. data/lib/milk_tea/tooling/linter/doc_tags.rb +25 -27
  173. data/lib/milk_tea/tooling/linter/fix_engine.rb +87 -24
  174. data/lib/milk_tea/tooling/linter/flow_rules.rb +44 -46
  175. data/lib/milk_tea/tooling/linter/imports_platform.rb +14 -16
  176. data/lib/milk_tea/tooling/linter/release_rules.rb +3 -5
  177. data/lib/milk_tea/tooling/linter/reserved_names.rb +23 -25
  178. data/lib/milk_tea/tooling/linter/rules.rb +87 -89
  179. data/lib/milk_tea/tooling/linter/source_helpers.rb +40 -42
  180. data/lib/milk_tea/tooling/linter/trailing_comma.rb +17 -19
  181. data/lib/milk_tea/tooling/linter/visitors.rb +112 -54
  182. data/lib/milk_tea/tooling/linter.rb +0 -2
  183. data/lib/milk_tea/tooling/project_scaffold.rb +0 -2
  184. data/lib/milk_tea/tooling/run.rb +0 -2
  185. data/lib/milk_tea/tooling/toolchain_cli.rb +0 -2
  186. data/std/cli.mt +13 -32
  187. data/std/color.mt +28 -28
  188. data/std/encoding.mt +4 -4
  189. data/std/graph.mt +1 -2
  190. data/std/hash.mt +14 -14
  191. data/std/http/server.mt +3 -3
  192. data/std/input.mt +1 -1
  193. data/std/json.mt +1 -1
  194. data/std/linear_algebra.mt +6 -6
  195. data/std/net/mux.mt +1 -1
  196. data/std/noise.mt +23 -23
  197. data/std/parse.mt +2 -2
  198. data/std/process.mt +3 -4
  199. data/std/sparse_set.mt +1 -1
  200. data/std/steering.mt +5 -5
  201. data/std/str.mt +1 -1
  202. data/std/tween.mt +4 -4
  203. data/std/utility.mt +3 -3
  204. metadata +2 -2
@@ -4,401 +4,399 @@ module MilkTea
4
4
  module LSP
5
5
  class Server
6
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
7
+ def handle_references(params)
8
+ stages = new_perf_stages
9
+ total_start = stages ? monotonic_time : nil
10
+ uri = params['textDocument']['uri']
11
+ lsp_line = params['position']['line']
12
+ lsp_char = params['position']['character']
13
+ result_count = 0
14
+ result_state = 'miss'
15
+
16
+ token = measure_perf_stage(stages, 'token') { @workspace.find_token_at(uri, lsp_line, lsp_char) }
17
+ unless token&.type == :identifier
18
+ result_state = 'not-identifier'
19
+ return []
20
+ end
23
21
 
24
- include_declaration = params.dig('context', 'includeDeclaration') != false
22
+ include_declaration = params.dig('context', 'includeDeclaration') != false
25
23
 
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
24
+ context = measure_perf_stage(stages, 'token_context') { token_context_at(uri, lsp_line, lsp_char) }
25
+ if context
26
+ tokens = context[:tokens]
27
+ token_index = context[:token_index]
28
+ named_arg_refs = named_argument_label_references(uri, token.lexeme, tokens, token_index, include_declaration:, stages:)
29
+ return named_arg_refs if named_arg_refs
30
+ end
33
31
 
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
32
+ facts = measure_perf_stage(stages, 'facts') { @workspace.get_facts(uri) }
33
+ target = facts ? measure_perf_stage(stages, 'static_target') { resolve_static_type_reference_target(uri, token, facts) } : nil
34
+ if target
35
+ refs = measure_perf_stage(stages, 'static_refs') { static_type_method_references(target, include_declaration: include_declaration) }
36
+ result_count = refs.length
37
+ result_state = refs.empty? ? 'miss' : 'hit'
38
+ return refs
39
+ end
42
40
 
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)
41
+ if facts
42
+ scoped_refs = measure_perf_stage(stages, 'scoped_refs') do
43
+ scoped_local_reference_locations(uri, token, lsp_line, lsp_char, facts, include_declaration: include_declaration)
44
+ end
45
+ unless scoped_refs.nil?
46
+ result_count = scoped_refs.length
47
+ result_state = scoped_refs.empty? ? 'miss' : 'hit'
48
+ return scoped_refs
49
+ end
46
50
  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
+
52
+ level_kind = facts ? module_level_name?(facts, token.lexeme) : false
53
+ if level_kind
54
+ cross_file = true
55
+ refs = measure_perf_stage(stages, 'module_refs') { module_level_reference_locations(uri, token.lexeme, facts, include_declaration: include_declaration, cross_file_allowed: cross_file) }
56
+ result_count = refs.length
57
+ result_state = refs.empty? ? 'miss' : 'hit'
58
+ return refs
51
59
  end
52
- end
53
60
 
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
61
+ unless facts
62
+ ast = @workspace.get_ast(uri)
63
+ if ast && module_level_ast_name?(ast, token.lexeme)
64
+ refs = measure_perf_stage(stages, 'module_refs') { module_level_reference_locations(uri, token.lexeme, nil, include_declaration: include_declaration) }
65
+ result_count = refs.length
66
+ result_state = refs.empty? ? 'miss' : 'hit'
67
+ return refs
68
+ end
69
+ end
62
70
 
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) }
71
+ refs = measure_perf_stage(stages, 'refs_scan') { @workspace.find_all_references(token.lexeme) }
72
+ if include_declaration
67
73
  result_count = refs.length
68
74
  result_state = refs.empty? ? 'miss' : 'hit'
69
75
  return refs
70
76
  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
77
 
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
78
+ found = measure_perf_stage(stages, 'definition_lookup') { @workspace.find_definition_token_global(token.lexeme, preferred_uri: uri) }
79
+ unless found
80
+ result_count = refs.length
81
+ result_state = refs.empty? ? 'miss' : 'hit'
82
+ return refs
83
+ end
86
84
 
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
85
+ def_uri = found[:uri]
86
+ def_line = found[:token].line - 1
87
+ def_char = found[:token].column - 1
88
+ filtered = measure_perf_stage(stages, 'filter') do
89
+ refs.reject do |r|
90
+ r[:uri] == def_uri &&
91
+ r[:range][:start][:line] == def_line &&
92
+ r[:range][:start][:character] == def_char
93
+ end
95
94
  end
95
+ result_count = filtered.length
96
+ result_state = filtered.empty? ? 'miss' : 'hit'
97
+ filtered
98
+ rescue StandardError => e
99
+ result_state = 'error'
100
+ warn "Error in references handler: #{e.message}"
101
+ []
102
+ ensure
103
+ log_request_stage_breakdown('textDocument/references', total_start, uri: uri, stages: stages, summary: "result=#{result_state} refs=#{result_count}")
96
104
  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
105
 
108
- def handle_document_link(params)
109
- uri = params['textDocument']['uri']
110
- file_path = uri_to_path(uri)
111
- return [] unless file_path
106
+ def handle_document_link(params)
107
+ uri = params['textDocument']['uri']
108
+ file_path = uri_to_path(uri)
109
+ return [] unless file_path
112
110
 
113
- tokens = @workspace.get_tokens(uri)
114
- return [] unless tokens
111
+ tokens = @workspace.get_tokens(uri)
112
+ return [] unless tokens
115
113
 
116
- tokens.filter_map do |token|
117
- resource_document_link(uri, file_path, token)
114
+ tokens.filter_map do |token|
115
+ resource_document_link(uri, file_path, token)
116
+ end
117
+ rescue StandardError => e
118
+ warn "Error in documentLink handler: #{e.message}"
119
+ []
118
120
  end
119
- rescue StandardError => e
120
- warn "Error in documentLink handler: #{e.message}"
121
- []
122
- end
123
121
 
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
- )
122
+ def handle_document_link_resolve(params)
123
+ target = params['target']
124
+ if target && target.start_with?('file://')
125
+ path = uri_to_path(target)
126
+ if path && File.file?(path)
127
+ first_line = File.open(path, &:readline).strip rescue nil
128
+ params.merge(
129
+ 'tooltip' => first_line ? "#{path}\n#{first_line}" : path
130
+ )
131
+ else
132
+ params
133
+ end
133
134
  else
134
135
  params
135
136
  end
136
- else
137
+ rescue StandardError => e
138
+ warn "Error in documentLink/resolve handler: #{e.message}"
137
139
  params
138
140
  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
141
 
147
- facts.methods.each_value do |methods|
148
- return :method if methods.key?(name) || methods.key?("static:#{name}")
149
- end
142
+ def module_level_name?(facts, name)
143
+ return :function if facts.functions.key?(name) || facts.types.key?(name) || facts.values.key?(name)
150
144
 
151
- facts.interfaces.each_value do |interface|
152
- if interface.respond_to?(:methods) && interface.methods.key?(name)
153
- return :interface_method
145
+ facts.methods.each_value do |methods|
146
+ return :method if methods.key?(name) || methods.key?("static:#{name}")
154
147
  end
155
- end
156
148
 
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
149
+ facts.interfaces.each_value do |interface|
150
+ if interface.respond_to?(:methods) && interface.methods.key?(name)
151
+ return :interface_method
152
+ end
153
+ end
164
154
 
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
155
+ false
168
156
  end
169
- false
170
- end
171
157
 
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
158
+ def module_level_name_kind(facts, name)
159
+ result = module_level_name?(facts, name)
160
+ result.is_a?(Symbol) ? result : nil
161
+ end
175
162
 
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
163
+ def module_level_ast_name?(ast, name)
182
164
  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
165
+ return true if module_level_declaration_node?(node) && node.respond_to?(:name) && node.name == name
186
166
  end
167
+ false
187
168
  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
169
 
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)
170
+ def module_level_reference_locations(uri, name, facts, include_declaration:, cross_file_allowed: true)
171
+ ast = @workspace.get_ast(uri)
172
+ return [] unless ast
173
+
174
+ results = []
175
+ binding_ids = facts ? facts.binding_resolution&.identifier_binding_ids : nil
176
+ local_bid_set = nil
177
+ if binding_ids && facts&.binding_resolution
178
+ resolution = facts.binding_resolution
179
+ local_bid_set = Set.new
180
+ each_ast_node(ast) do |node|
181
+ next unless local_binding_declaration_node?(node)
182
+ bid = resolution.declaration_binding_ids[node.object_id]
183
+ local_bid_set << bid if bid
209
184
  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
185
  end
229
- end
186
+ each_ast_node(ast) do |node|
187
+ if node.is_a?(AST::Identifier) && node.name == name
188
+ if binding_ids&.key?(node.object_id)
189
+ bid = binding_ids[node.object_id]
190
+ next if local_bid_set&.include?(bid)
191
+ end
230
192
 
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
193
+ range = ast_name_range(node.name, node.line, node.column)
194
+ next unless range
195
+
196
+ results << {
197
+ uri: uri,
198
+ range: {
199
+ start: { line: range[:line] - 1, character: range[:column] - 1 },
200
+ end: { line: range[:line] - 1, character: range[:column] - 1 + name.length },
201
+ },
202
+ }
203
+ elsif include_declaration && node.respond_to?(:name) && node.name == name && module_level_declaration_node?(node)
204
+ range = declaration_name_range(node)
205
+ unless range
206
+ range = declaration_name_range_fallback(node, uri, name)
207
+ end
208
+ next unless range
209
+
210
+ results << {
211
+ uri: uri,
212
+ range: {
213
+ start: { line: range[:line] - 1, character: range[:column] - 1 },
214
+ end: { line: range[:line] - 1, character: range[:column] - 1 + name.length },
215
+ },
216
+ }
217
+ elsif node.is_a?(AST::MemberAccess) && node.member == name && node.line && node.column
218
+ member_col = node.column - 1
219
+ results << {
220
+ uri: uri,
221
+ range: {
222
+ start: { line: node.line - 1, character: member_col },
223
+ end: { line: node.line - 1, character: member_col + name.length },
224
+ },
225
+ }
226
+ end
236
227
  end
237
- end
238
228
 
239
- results.uniq { |r| [r[:uri], r.dig(:range, :start, :line), r.dig(:range, :start, :character)] }
240
- end
229
+ if cross_file_allowed && facts&.module_name
230
+ importing_uris = @workspace.reverse_import_dependents_for(facts.module_name)
231
+ if importing_uris && !importing_uris.empty?
232
+ cross_refs = @workspace.find_all_references_in(name, importing_uris.to_a - [uri])
233
+ results.concat(cross_refs) if cross_refs
234
+ end
235
+ end
241
236
 
242
- def get_content_line(uri, lsp_line)
243
- content = @workspace.get_content(uri)
244
- return nil unless content
237
+ results.uniq { |r| [r[:uri], r.dig(:range, :start, :line), r.dig(:range, :start, :character)] }
238
+ end
245
239
 
246
- content.split("\n", -1)[lsp_line]
247
- rescue StandardError
248
- nil
249
- end
240
+ def get_content_line(uri, lsp_line)
241
+ content = @workspace.get_content(uri)
242
+ return nil unless content
250
243
 
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
244
+ content.split("\n", -1)[lsp_line]
245
+ rescue StandardError
246
+ nil
255
247
  end
256
248
 
257
- dot_idx = line.index('.', receiver_start_col)
258
- return nil unless dot_idx && dot_idx < idx
249
+ def find_member_column_in_line(line, receiver_start_col, member_name)
250
+ idx = receiver_start_col
251
+ while idx < line.length && line[idx] =~ /[A-Za-z0-9_.]/
252
+ idx += 1
253
+ end
259
254
 
260
- member_start = dot_idx + 1
261
- match = line[member_start, member_name.length]
262
- return nil unless match == member_name
255
+ dot_idx = line.index('.', receiver_start_col)
256
+ return nil unless dot_idx && dot_idx < idx
263
257
 
264
- member_start
265
- end
258
+ member_start = dot_idx + 1
259
+ match = line[member_start, member_name.length]
260
+ return nil unless match == member_name
266
261
 
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
262
+ member_start
263
+ end
282
264
 
283
- def declaration_name_range_fallback(node, uri, name)
284
- return nil unless node.respond_to?(:line) && node.line
265
+ def module_level_declaration_node?(node)
266
+ node.is_a?(AST::FunctionDef) ||
267
+ node.is_a?(AST::MethodDef) ||
268
+ node.is_a?(AST::InterfaceMethodDecl) ||
269
+ node.is_a?(AST::ExternFunctionDecl) ||
270
+ node.is_a?(AST::ForeignFunctionDecl) ||
271
+ node.is_a?(AST::ConstDecl) ||
272
+ node.is_a?(AST::VarDecl) ||
273
+ node.is_a?(AST::TypeAliasDecl) ||
274
+ node.is_a?(AST::StructDecl) ||
275
+ node.is_a?(AST::UnionDecl) ||
276
+ node.is_a?(AST::EnumDecl) ||
277
+ node.is_a?(AST::FlagsDecl) ||
278
+ node.is_a?(AST::OpaqueDecl)
279
+ end
285
280
 
286
- row = get_content_line(uri, node.line - 1)
287
- return nil unless row
281
+ def declaration_name_range_fallback(node, uri, name)
282
+ return nil unless node.line
288
283
 
289
- col = row.index(/\b#{Regexp.escape(name)}\b/)
290
- return nil unless col
284
+ row = get_content_line(uri, node.line - 1)
285
+ return nil unless row
291
286
 
292
- { name: name, line: node.line, column: col + 1, length: name.length }
293
- rescue StandardError
294
- nil
295
- end
287
+ col = row.index(/\b#{Regexp.escape(name)}\b/)
288
+ return nil unless col
289
+
290
+ { name: name, line: node.line, column: col + 1, length: name.length }
291
+ rescue StandardError
292
+ nil
293
+ end
296
294
 
297
- def handle_document_highlight(params)
298
- uri = params['textDocument']['uri']
299
- lsp_line = params['position']['line']
300
- lsp_char = params['position']['character']
295
+ def handle_document_highlight(params)
296
+ uri = params['textDocument']['uri']
297
+ lsp_line = params['position']['line']
298
+ lsp_char = params['position']['character']
301
299
 
302
- token = @workspace.find_token_at(uri, lsp_line, lsp_char)
303
- return [] unless token&.type == :identifier
300
+ token = @workspace.find_token_at(uri, lsp_line, lsp_char)
301
+ return [] unless token&.type == :identifier
304
302
 
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
303
+ if (facts = @workspace.get_facts(uri))
304
+ scoped = begin
305
+ scoped_local_reference_locations(uri, token, lsp_line, lsp_char, facts, include_declaration: true)
306
+ rescue StandardError
307
+ nil
308
+ end
309
+ unless scoped.nil?
310
+ return scoped.map { |entry| { range: entry[:range], kind: 1 } }
311
+ end
314
312
 
315
- binding_id = begin
316
- rename_target_binding_id(uri, token, lsp_line, lsp_char, facts)
317
- rescue StandardError
318
- nil
319
- end
320
- if binding_id
321
- ranges = begin
322
- scoped_binding_occurrence_ranges(uri, token.lexeme, facts, binding_id, include_declaration: true)
313
+ binding_id = begin
314
+ rename_target_binding_id(uri, token, lsp_line, lsp_char, facts)
323
315
  rescue StandardError
324
- []
316
+ nil
325
317
  end
326
- unless ranges.empty?
327
- return ranges.map do |range|
328
- {
329
- range: {
330
- start: { line: range[:line] - 1, character: range[:column] - 1 },
331
- end: { line: range[:line] - 1, character: range[:column] - 1 + range[:length] },
332
- },
333
- kind: 1,
334
- }
318
+ if binding_id
319
+ ranges = begin
320
+ scoped_binding_occurrence_ranges(uri, token.lexeme, facts, binding_id, include_declaration: true)
321
+ rescue StandardError
322
+ []
323
+ end
324
+ unless ranges.empty?
325
+ return ranges.map do |range|
326
+ {
327
+ range: {
328
+ start: { line: range[:line] - 1, character: range[:column] - 1 },
329
+ end: { line: range[:line] - 1, character: range[:column] - 1 + range[:length] },
330
+ },
331
+ kind: 1,
332
+ }
333
+ end
335
334
  end
336
335
  end
337
336
  end
338
- end
339
337
 
340
- toks = @workspace.get_tokens(uri) || []
341
- toks.select { |t| t.type == :identifier && t.lexeme == token.lexeme }
342
- .map { |t| { range: token_to_range(t), kind: 1 } }
343
- rescue StandardError => e
344
- warn "Error in documentHighlight handler: #{e.message}"
345
- []
346
- end
338
+ toks = @workspace.get_tokens(uri) || []
339
+ toks.select { |t| t.type == :identifier && t.lexeme == token.lexeme }
340
+ .map { |t| { range: token_to_range(t), kind: 1 } }
341
+ rescue StandardError => e
342
+ warn "Error in documentHighlight handler: #{e.message}"
343
+ []
344
+ end
347
345
 
348
- def handle_workspace_symbol(params)
349
- query = (params['query'] || '').downcase
346
+ def handle_workspace_symbol(params)
347
+ query = (params['query'] || '').downcase
350
348
 
351
- @workspace.index_workspace(@root_uri) if @root_uri
349
+ @workspace.index_workspace(@root_uri) if @root_uri
352
350
 
353
- results = []
354
- @workspace.all_documents.each do |uri|
355
- @workspace.get_symbols(uri).each do |sym|
356
- next unless query.empty? || sym[:name].downcase.include?(query)
351
+ results = []
352
+ @workspace.all_documents.each do |uri|
353
+ @workspace.get_symbols(uri).each do |sym|
354
+ next unless query.empty? || sym[:name].downcase.include?(query)
357
355
 
358
- results << format_symbol(sym, uri)
356
+ results << format_symbol(sym, uri)
357
+ end
359
358
  end
360
- end
361
359
 
362
- results
363
- rescue StandardError => e
364
- warn "Error in workspace/symbol handler: #{e.message}"
365
- []
366
- end
360
+ results
361
+ rescue StandardError => e
362
+ warn "Error in workspace/symbol handler: #{e.message}"
363
+ []
364
+ end
367
365
 
368
- def resource_document_link(uri, file_path, token)
369
- return nil unless [:string, :cstring].include?(token.type)
366
+ def resource_document_link(uri, file_path, token)
367
+ return nil unless [:string, :cstring].include?(token.type)
370
368
 
371
- literal = token.literal
372
- return nil unless literal.is_a?(String)
373
- return nil unless path_like_string_literal?(literal)
369
+ literal = token.literal
370
+ return nil unless literal.is_a?(String)
371
+ return nil unless path_like_string_literal?(literal)
374
372
 
375
- resolved_path = resolve_document_link_path(file_path, literal)
376
- return nil unless resolved_path
373
+ resolved_path = resolve_document_link_path(file_path, literal)
374
+ return nil unless resolved_path
377
375
 
378
- {
379
- range: token_to_range(token),
380
- target: path_to_uri(resolved_path),
381
- tooltip: "Open linked file"
382
- }
383
- end
376
+ {
377
+ range: token_to_range(token),
378
+ target: path_to_uri(resolved_path),
379
+ tooltip: "Open linked file"
380
+ }
381
+ end
384
382
 
385
- def path_like_string_literal?(literal)
386
- literal.include?('/') || literal.include?(File::SEPARATOR)
387
- end
383
+ def path_like_string_literal?(literal)
384
+ literal.include?('/') || literal.include?(File::SEPARATOR)
385
+ end
388
386
 
389
- def resolve_document_link_path(source_path, literal)
390
- base_dir = File.dirname(source_path)
391
- candidate = if Pathname.new(literal).absolute?
392
- literal
393
- else
394
- File.expand_path(literal, base_dir)
395
- end
396
- return nil unless File.exist?(candidate)
397
-
398
- candidate
399
- rescue StandardError
400
- nil
401
- end
387
+ def resolve_document_link_path(source_path, literal)
388
+ base_dir = File.dirname(source_path)
389
+ candidate = if Pathname.new(literal).absolute?
390
+ literal
391
+ else
392
+ File.expand_path(literal, base_dir)
393
+ end
394
+ return nil unless File.exist?(candidate)
395
+
396
+ candidate
397
+ rescue StandardError
398
+ nil
399
+ end
402
400
  end
403
401
  def named_argument_label_references(uri, name, tokens, token_index, include_declaration:, stages: nil)
404
402
  return nil unless named_argument_label_token?(tokens, token_index)