mt-lang 0.3.8 → 0.3.10

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 (200) hide show
  1. checksums.yaml +4 -4
  2. data/lib/milk_tea/base.rb +1 -2
  3. data/lib/milk_tea/bindings/bindgen/ast_parser.rb +0 -2
  4. data/lib/milk_tea/bindings/bindgen/declaration.rb +0 -2
  5. data/lib/milk_tea/bindings/bindgen/emitter.rb +0 -2
  6. data/lib/milk_tea/bindings/bindgen/overrides.rb +0 -2
  7. data/lib/milk_tea/bindings/bindgen/type_mapper.rb +51 -53
  8. data/lib/milk_tea/bindings/imported_bindings/generator.rb +7 -9
  9. data/lib/milk_tea/bindings/imported_bindings/method_source.rb +0 -2
  10. data/lib/milk_tea/bindings/imported_bindings/naming.rb +6 -8
  11. data/lib/milk_tea/bindings/imported_bindings.rb +0 -2
  12. data/lib/milk_tea/bindings/opengl_registry.rb +3 -7
  13. data/lib/milk_tea/bindings/steamworks.rb +12 -16
  14. data/lib/milk_tea/bindings/upstream_sources.rb +10 -14
  15. data/lib/milk_tea/bindings/vendored_box2d.rb +10 -12
  16. data/lib/milk_tea/bindings/vendored_cjson.rb +6 -8
  17. data/lib/milk_tea/bindings/vendored_flecs.rb +10 -12
  18. data/lib/milk_tea/bindings/vendored_glfw.rb +11 -13
  19. data/lib/milk_tea/bindings/vendored_libuv.rb +11 -13
  20. data/lib/milk_tea/bindings/vendored_pcre2.rb +11 -13
  21. data/lib/milk_tea/bindings/vendored_raylib.rb +6 -10
  22. data/lib/milk_tea/bindings/vendored_sdl3.rb +11 -13
  23. data/lib/milk_tea/bindings/vendored_steamworks.rb +1 -5
  24. data/lib/milk_tea/core/ast.rb +103 -45
  25. data/lib/milk_tea/core/async_runtime_installer.rb +0 -2
  26. data/lib/milk_tea/core/bindings/attribute_binding.rb +1 -3
  27. data/lib/milk_tea/core/c_backend/aggregate_utils.rb +147 -84
  28. data/lib/milk_tea/core/c_backend/control_flow_emission.rb +394 -396
  29. data/lib/milk_tea/core/c_backend/expressions.rb +489 -476
  30. data/lib/milk_tea/core/c_backend/feature_detection.rb +446 -437
  31. data/lib/milk_tea/core/c_backend/format_helpers.rb +0 -2
  32. data/lib/milk_tea/core/c_backend/reachability.rb +339 -341
  33. data/lib/milk_tea/core/c_backend/reinterpret.rb +231 -217
  34. data/lib/milk_tea/core/c_backend/runtime_helpers.rb +531 -526
  35. data/lib/milk_tea/core/c_backend/statements.rb +528 -530
  36. data/lib/milk_tea/core/c_backend/type_collectors.rb +1344 -1346
  37. data/lib/milk_tea/core/c_backend/type_declaration.rb +215 -213
  38. data/lib/milk_tea/core/c_backend/type_system.rb +347 -349
  39. data/lib/milk_tea/core/c_backend.rb +17 -9
  40. data/lib/milk_tea/core/compatibility_helpers.rb +0 -2
  41. data/lib/milk_tea/core/compile_time/const_eval.rb +7 -12
  42. data/lib/milk_tea/core/compile_time.rb +32 -38
  43. data/lib/milk_tea/core/control_flow/builder.rb +3 -5
  44. data/lib/milk_tea/core/control_flow/constant_propagation.rb +14 -17
  45. data/lib/milk_tea/core/control_flow/definite_assignment.rb +0 -1
  46. data/lib/milk_tea/core/ir.rb +10 -10
  47. data/lib/milk_tea/core/lexer/character_classes.rb +5 -7
  48. data/lib/milk_tea/core/lexer/format_strings.rb +7 -9
  49. data/lib/milk_tea/core/lexer/heredocs.rb +22 -22
  50. data/lib/milk_tea/core/lexer/indentation.rb +0 -2
  51. data/lib/milk_tea/core/lexer/numbers.rb +0 -2
  52. data/lib/milk_tea/core/lexer/recovery.rb +0 -2
  53. data/lib/milk_tea/core/lexer/strings.rb +0 -2
  54. data/lib/milk_tea/core/lexer/symbols.rb +0 -2
  55. data/lib/milk_tea/core/lexer/trivia.rb +0 -2
  56. data/lib/milk_tea/core/lexer.rb +0 -2
  57. data/lib/milk_tea/core/lowering/async/analysis.rb +0 -2
  58. data/lib/milk_tea/core/lowering/async/lowering.rb +1264 -1265
  59. data/lib/milk_tea/core/lowering/async/normalization.rb +0 -2
  60. data/lib/milk_tea/core/lowering/async.rb +627 -630
  61. data/lib/milk_tea/core/lowering/block.rb +413 -416
  62. data/lib/milk_tea/core/lowering/calls.rb +1381 -1384
  63. data/lib/milk_tea/core/lowering/declarations.rb +180 -183
  64. data/lib/milk_tea/core/lowering/dyn.rb +48 -50
  65. data/lib/milk_tea/core/lowering/events.rb +983 -985
  66. data/lib/milk_tea/core/lowering/expressions.rb +1513 -1518
  67. data/lib/milk_tea/core/lowering/foreign_cstr.rb +181 -184
  68. data/lib/milk_tea/core/lowering/functions.rb +185 -188
  69. data/lib/milk_tea/core/lowering/loops.rb +1028 -1031
  70. data/lib/milk_tea/core/lowering/proc.rb +363 -366
  71. data/lib/milk_tea/core/lowering/resolve.rb +2133 -2135
  72. data/lib/milk_tea/core/lowering/scans.rb +179 -181
  73. data/lib/milk_tea/core/lowering/str_buffer.rb +102 -105
  74. data/lib/milk_tea/core/lowering/utils.rb +1040 -1044
  75. data/lib/milk_tea/core/lowering.rb +5 -7
  76. data/lib/milk_tea/core/module_binder.rb +0 -2
  77. data/lib/milk_tea/core/module_loader/errors.rb +4 -2
  78. data/lib/milk_tea/core/module_loader.rb +113 -20
  79. data/lib/milk_tea/core/module_path_resolver.rb +0 -2
  80. data/lib/milk_tea/core/module_roots.rb +6 -8
  81. data/lib/milk_tea/core/parser/attributes.rb +0 -2
  82. data/lib/milk_tea/core/parser/blocks.rb +0 -2
  83. data/lib/milk_tea/core/parser/declarations.rb +0 -2
  84. data/lib/milk_tea/core/parser/expressions.rb +31 -14
  85. data/lib/milk_tea/core/parser/recovery.rb +0 -2
  86. data/lib/milk_tea/core/parser/statements.rb +0 -2
  87. data/lib/milk_tea/core/parser/types.rb +8 -10
  88. data/lib/milk_tea/core/parser.rb +1 -3
  89. data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +31 -33
  90. data/lib/milk_tea/core/pretty_printer/base_formatter.rb +0 -2
  91. data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +0 -2
  92. data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +0 -2
  93. data/lib/milk_tea/core/semantic_analyzer/attributes.rb +14 -16
  94. data/lib/milk_tea/core/semantic_analyzer/calls.rb +0 -2
  95. data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1 -14
  96. data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +18 -21
  97. data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +0 -3
  98. data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +12 -15
  99. data/lib/milk_tea/core/semantic_analyzer/generics.rb +57 -59
  100. data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +0 -2
  101. data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +5 -9
  102. data/lib/milk_tea/core/semantic_analyzer/nullability.rb +0 -3
  103. data/lib/milk_tea/core/semantic_analyzer/statements.rb +35 -40
  104. data/lib/milk_tea/core/semantic_analyzer/top_level.rb +10 -12
  105. data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +0 -6
  106. data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +102 -105
  107. data/lib/milk_tea/core/semantic_analyzer.rb +17 -8
  108. data/lib/milk_tea/core/types/layout.rb +21 -22
  109. data/lib/milk_tea/core/types/predicates.rb +0 -2
  110. data/lib/milk_tea/core/types/registry.rb +16 -18
  111. data/lib/milk_tea/core/types/visitor.rb +0 -4
  112. data/lib/milk_tea/dap/backends/lldb_dap.rb +0 -2
  113. data/lib/milk_tea/dap/protocol.rb +1 -1
  114. data/lib/milk_tea/dap/server/breakpoints.rb +0 -2
  115. data/lib/milk_tea/dap/server/debug_map.rb +0 -2
  116. data/lib/milk_tea/dap/server/handlers.rb +0 -2
  117. data/lib/milk_tea/dap/server/launch.rb +0 -2
  118. data/lib/milk_tea/dap/server/lldb_backend.rb +8 -10
  119. data/lib/milk_tea/dap/server/pause_diagnostics.rb +14 -16
  120. data/lib/milk_tea/dap/server/utilities.rb +0 -2
  121. data/lib/milk_tea/dap/server/wire.rb +0 -2
  122. data/lib/milk_tea/dap/server.rb +0 -2
  123. data/lib/milk_tea/lsp/diagnostics.rb +16 -16
  124. data/lib/milk_tea/lsp/server/call_hierarchy.rb +11 -12
  125. data/lib/milk_tea/lsp/server/code_actions.rb +355 -357
  126. data/lib/milk_tea/lsp/server/code_lens.rb +23 -25
  127. data/lib/milk_tea/lsp/server/completion.rb +873 -875
  128. data/lib/milk_tea/lsp/server/configuration.rb +129 -131
  129. data/lib/milk_tea/lsp/server/debug_info.rb +0 -2
  130. data/lib/milk_tea/lsp/server/definition.rb +564 -565
  131. data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +191 -193
  132. data/lib/milk_tea/lsp/server/execute_command.rb +0 -2
  133. data/lib/milk_tea/lsp/server/folding_range.rb +0 -4
  134. data/lib/milk_tea/lsp/server/formatting.rb +478 -480
  135. data/lib/milk_tea/lsp/server/hover.rb +1282 -1284
  136. data/lib/milk_tea/lsp/server/inlay_hints.rb +179 -181
  137. data/lib/milk_tea/lsp/server/lifecycle.rb +192 -194
  138. data/lib/milk_tea/lsp/server/linked_editing_range.rb +0 -2
  139. data/lib/milk_tea/lsp/server/on_type_formatting.rb +0 -2
  140. data/lib/milk_tea/lsp/server/progress.rb +0 -2
  141. data/lib/milk_tea/lsp/server/references.rb +316 -318
  142. data/lib/milk_tea/lsp/server/rename.rb +445 -447
  143. data/lib/milk_tea/lsp/server/selection_range.rb +0 -4
  144. data/lib/milk_tea/lsp/server/semantic_tokens.rb +1318 -1320
  145. data/lib/milk_tea/lsp/server/signature_help.rb +146 -148
  146. data/lib/milk_tea/lsp/server/text_documents.rb +99 -101
  147. data/lib/milk_tea/lsp/server/type_hierarchy.rb +2 -4
  148. data/lib/milk_tea/lsp/server/utilities.rb +397 -399
  149. data/lib/milk_tea/lsp/server.rb +0 -3
  150. data/lib/milk_tea/lsp/workspace/analysis.rb +40 -42
  151. data/lib/milk_tea/lsp/workspace/dependency_graph.rb +4 -10
  152. data/lib/milk_tea/lsp/workspace/utilities.rb +19 -19
  153. data/lib/milk_tea/packages/atomic_write.rb +4 -5
  154. data/lib/milk_tea/packages/dependency_solver.rb +15 -15
  155. data/lib/milk_tea/packages/lock.rb +4 -4
  156. data/lib/milk_tea/packages/manager_cli.rb +1 -1
  157. data/lib/milk_tea/packages/manifest.rb +3 -3
  158. data/lib/milk_tea/packages/registry_store.rb +4 -6
  159. data/lib/milk_tea/packages/services.rb +16 -16
  160. data/lib/milk_tea/packages/source_cache.rb +4 -6
  161. data/lib/milk_tea/packages/source_resolver.rb +44 -44
  162. data/lib/milk_tea/packages/version.rb +8 -8
  163. data/lib/milk_tea/tooling/build.rb +3 -7
  164. data/lib/milk_tea/tooling/build_cache.rb +0 -2
  165. data/lib/milk_tea/tooling/debug_info_formatter.rb +31 -33
  166. data/lib/milk_tea/tooling/debug_map.rb +8 -10
  167. data/lib/milk_tea/tooling/docs_app.rb +0 -2
  168. data/lib/milk_tea/tooling/linter/doc_tags.rb +25 -27
  169. data/lib/milk_tea/tooling/linter/fix_engine.rb +87 -24
  170. data/lib/milk_tea/tooling/linter/flow_rules.rb +44 -46
  171. data/lib/milk_tea/tooling/linter/imports_platform.rb +14 -16
  172. data/lib/milk_tea/tooling/linter/release_rules.rb +3 -5
  173. data/lib/milk_tea/tooling/linter/reserved_names.rb +23 -25
  174. data/lib/milk_tea/tooling/linter/rules.rb +87 -89
  175. data/lib/milk_tea/tooling/linter/source_helpers.rb +40 -42
  176. data/lib/milk_tea/tooling/linter/trailing_comma.rb +17 -19
  177. data/lib/milk_tea/tooling/linter/visitors.rb +162 -50
  178. data/lib/milk_tea/tooling/linter.rb +0 -2
  179. data/lib/milk_tea/tooling/project_scaffold.rb +0 -2
  180. data/lib/milk_tea/tooling/run.rb +0 -2
  181. data/lib/milk_tea/tooling/toolchain_cli.rb +0 -2
  182. data/std/cli.mt +13 -32
  183. data/std/color.mt +28 -28
  184. data/std/encoding.mt +4 -4
  185. data/std/graph.mt +1 -2
  186. data/std/hash.mt +14 -14
  187. data/std/http/server.mt +3 -3
  188. data/std/input.mt +1 -1
  189. data/std/json.mt +1 -1
  190. data/std/linear_algebra.mt +6 -6
  191. data/std/net/mux.mt +1 -1
  192. data/std/noise.mt +23 -23
  193. data/std/parse.mt +2 -2
  194. data/std/process.mt +3 -4
  195. data/std/sparse_set.mt +1 -1
  196. data/std/steering.mt +5 -5
  197. data/std/str.mt +1 -1
  198. data/std/tween.mt +4 -4
  199. data/std/utility.mt +3 -3
  200. metadata +2 -2
@@ -4,596 +4,594 @@ module MilkTea
4
4
  module LSP
5
5
  class Server
6
6
  module ServerRename
7
- private
7
+ def handle_prepare_rename(params)
8
+ uri = params['textDocument']['uri']
9
+ lsp_line = params['position']['line']
10
+ lsp_char = params['position']['character']
11
+
12
+ token = @workspace.find_token_at(uri, lsp_line, lsp_char)
13
+ return nil unless name_like_token?(token)
14
+
15
+ { range: token_to_range(token), placeholder: token.lexeme }
16
+ rescue StandardError => e
17
+ warn "Error in prepareRename handler: #{e.message}"
18
+ nil
19
+ end
8
20
 
9
- def handle_prepare_rename(params)
10
- uri = params['textDocument']['uri']
11
- lsp_line = params['position']['line']
12
- lsp_char = params['position']['character']
21
+ def name_like_token?(tok)
22
+ return false if tok.nil?
23
+ tok.type == :identifier || Token::KEYWORDS.value?(tok.type)
24
+ end
13
25
 
14
- token = @workspace.find_token_at(uri, lsp_line, lsp_char)
15
- return nil unless name_like_token?(token)
26
+ def allow_hover_last_good_fallback?(uri)
27
+ return true unless @workspace.dependency_resolution_mode == :frozen
16
28
 
17
- { range: token_to_range(token), placeholder: token.lexeme }
18
- rescue StandardError => e
19
- warn "Error in prepareRename handler: #{e.message}"
20
- nil
21
- end
29
+ path = uri_to_path(uri)
30
+ return true unless path && File.file?(path)
22
31
 
23
- def name_like_token?(tok)
24
- return false if tok.nil?
25
- tok.type == :identifier || Token::KEYWORDS.value?(tok.type)
26
- end
32
+ DependencyResolution.resolve(path, mode: @workspace.dependency_resolution_mode).ok?
33
+ rescue StandardError
34
+ true
35
+ end
27
36
 
28
- def allow_hover_last_good_fallback?(uri)
29
- return true unless @workspace.dependency_resolution_mode == :frozen
37
+ def handle_rename(params)
38
+ uri = params['textDocument']['uri']
39
+ lsp_line = params['position']['line']
40
+ lsp_char = params['position']['character']
41
+ new_name = params['newName'].to_s
30
42
 
31
- path = uri_to_path(uri)
32
- return true unless path && File.file?(path)
43
+ token = @workspace.find_token_at(uri, lsp_line, lsp_char)
44
+ return nil unless name_like_token?(token)
33
45
 
34
- DependencyResolution.resolve(path, mode: @workspace.dependency_resolution_mode).ok?
35
- rescue StandardError
36
- true
37
- end
46
+ import_alias_changes = import_alias_rename_changes(uri, token, lsp_line, lsp_char, nil, new_name)
47
+ return { changes: import_alias_changes } if import_alias_changes
38
48
 
39
- def handle_rename(params)
40
- uri = params['textDocument']['uri']
41
- lsp_line = params['position']['line']
42
- lsp_char = params['position']['character']
43
- new_name = params['newName'].to_s
49
+ if (facts = @workspace.get_facts(uri))
50
+ sc = struct_field_rename_changes(uri, token, lsp_line, lsp_char, facts, new_name)
51
+ return { changes: sc } if sc && !sc.empty?
44
52
 
45
- token = @workspace.find_token_at(uri, lsp_line, lsp_char)
46
- return nil unless name_like_token?(token)
53
+ mc = method_rename_changes(uri, token, lsp_line, lsp_char, facts, new_name)
54
+ return { changes: mc } if mc && !mc.empty?
55
+ end
47
56
 
48
- import_alias_changes = import_alias_rename_changes(uri, token, lsp_line, lsp_char, nil, new_name)
49
- return { changes: import_alias_changes } if import_alias_changes
57
+ enum_member_changes = enum_member_rename_changes(uri, token, lsp_line, lsp_char, nil, new_name)
58
+ return { changes: enum_member_changes } if enum_member_changes
50
59
 
51
- if (facts = @workspace.get_facts(uri))
52
- sc = struct_field_rename_changes(uri, token, lsp_line, lsp_char, facts, new_name)
53
- return { changes: sc } if sc && !sc.empty?
60
+ if (facts = @workspace.get_facts(uri))
61
+ scoped_changes = scoped_rename_changes(uri, token, lsp_line, lsp_char, facts, new_name)
62
+ return { changes: scoped_changes } if scoped_changes
54
63
 
55
- mc = method_rename_changes(uri, token, lsp_line, lsp_char, facts, new_name)
56
- return { changes: mc } if mc && !mc.empty?
57
- end
64
+ enum_member_changes = enum_member_rename_changes(uri, token, lsp_line, lsp_char, facts, new_name)
65
+ return { changes: enum_member_changes } if enum_member_changes
66
+ end
58
67
 
59
- enum_member_changes = enum_member_rename_changes(uri, token, lsp_line, lsp_char, nil, new_name)
60
- return { changes: enum_member_changes } if enum_member_changes
68
+ workspace_symbol_changes = workspace_symbol_identity_rename_changes(uri, token, lsp_line, lsp_char, new_name)
69
+ return { changes: workspace_symbol_changes } if workspace_symbol_changes
61
70
 
62
- if (facts = @workspace.get_facts(uri))
63
- scoped_changes = scoped_rename_changes(uri, token, lsp_line, lsp_char, facts, new_name)
64
- return { changes: scoped_changes } if scoped_changes
71
+ lexical_changes = lexical_rename_changes_in_document(uri, token.lexeme, new_name)
72
+ return { changes: lexical_changes } if lexical_changes
65
73
 
66
- enum_member_changes = enum_member_rename_changes(uri, token, lsp_line, lsp_char, facts, new_name)
67
- return { changes: enum_member_changes } if enum_member_changes
74
+ nil
75
+ rescue StandardError => e
76
+ warn "Error in rename handler: #{e.message}"
77
+ nil
68
78
  end
69
79
 
70
- workspace_symbol_changes = workspace_symbol_identity_rename_changes(uri, token, lsp_line, lsp_char, new_name)
71
- return { changes: workspace_symbol_changes } if workspace_symbol_changes
72
-
73
- lexical_changes = lexical_rename_changes_in_document(uri, token.lexeme, new_name)
74
- return { changes: lexical_changes } if lexical_changes
75
-
76
- nil
77
- rescue StandardError => e
78
- warn "Error in rename handler: #{e.message}"
79
- nil
80
- end
81
-
82
- def lexical_rename_changes_in_document(uri, name, new_name)
83
- tokens = @workspace.get_tokens(uri) || []
84
- edits = tokens.flat_map do |tok|
85
- result = []
86
-
87
- if tok.type == :fstring && tok.literal.is_a?(Array)
88
- tok.literal.each do |part|
89
- next unless part.is_a?(Hash) && part[:kind] == :expr
90
- next unless part[:line] && part[:column]
91
-
92
- expr_tokens = MilkTea::Lexer.lex(part[:source], path: uri_to_path(uri))
93
- expr_tokens.each do |etok|
94
- next unless name_like_token?(etok) && etok.lexeme == name
95
-
96
- result << {
97
- range: {
98
- start: { line: part[:line] - 1, character: part[:column] + etok.column - 2 },
99
- end: { line: part[:line] - 1, character: part[:column] + etok.column - 2 + etok.lexeme.length },
100
- },
101
- newText: new_name,
102
- }
80
+ def lexical_rename_changes_in_document(uri, name, new_name)
81
+ tokens = @workspace.get_tokens(uri) || []
82
+ edits = tokens.flat_map do |tok|
83
+ result = []
84
+
85
+ if tok.type == :fstring && tok.literal.is_a?(Array)
86
+ tok.literal.each do |part|
87
+ next unless part.is_a?(Hash) && part[:kind] == :expr
88
+ next unless part[:line] && part[:column]
89
+
90
+ expr_tokens = MilkTea::Lexer.lex(part[:source], path: uri_to_path(uri))
91
+ expr_tokens.each do |etok|
92
+ next unless name_like_token?(etok) && etok.lexeme == name
93
+
94
+ result << {
95
+ range: {
96
+ start: { line: part[:line] - 1, character: part[:column] + etok.column - 2 },
97
+ end: { line: part[:line] - 1, character: part[:column] + etok.column - 2 + etok.lexeme.length },
98
+ },
99
+ newText: new_name,
100
+ }
101
+ end
102
+ rescue MilkTea::LexError
103
+ nil
103
104
  end
104
- rescue MilkTea::LexError
105
- nil
106
105
  end
107
- end
108
106
 
109
- if name_like_token?(tok) && tok.lexeme == name
110
- result << { range: token_to_range(tok), newText: new_name }
107
+ if name_like_token?(tok) && tok.lexeme == name
108
+ result << { range: token_to_range(tok), newText: new_name }
109
+ end
110
+
111
+ result
111
112
  end
112
113
 
113
- result
114
+ return nil if edits.empty?
115
+
116
+ { uri => edits }
114
117
  end
115
118
 
116
- return nil if edits.empty?
119
+ def workspace_symbol_identity_rename_changes(uri, token, lsp_line, lsp_char, new_name)
120
+ target_location = resolve_definition_location({
121
+ 'textDocument' => { 'uri' => uri },
122
+ 'position' => { 'line' => lsp_line, 'character' => lsp_char },
123
+ }, stages: nil)
124
+ return nil unless target_location
125
+
126
+ target_identity = definition_identity_key(target_location)
127
+ return nil unless target_identity
128
+
129
+ related_uris = @workspace.related_open_document_uris(uri)
130
+ return nil if related_uris.empty?
131
+
132
+ changes = {}
133
+ related_uris.each do |related_uri|
134
+ tokens = @workspace.get_tokens(related_uri) || []
135
+ next if tokens.empty?
136
+
137
+ edits = tokens.filter_map do |candidate|
138
+ next unless name_like_token?(candidate) && candidate.lexeme == token.lexeme
139
+
140
+ candidate_location = resolve_definition_location({
141
+ 'textDocument' => { 'uri' => related_uri },
142
+ 'position' => { 'line' => candidate.line - 1, 'character' => candidate.column - 1 },
143
+ }, stages: nil)
144
+ next unless candidate_location
145
+ next unless definition_identity_key(candidate_location) == target_identity
146
+
147
+ {
148
+ range: token_to_range(candidate),
149
+ newText: new_name,
150
+ }
151
+ end
117
152
 
118
- { uri => edits }
119
- end
153
+ changes[related_uri] = edits unless edits.empty?
154
+ end
155
+
156
+ return nil if changes.empty?
157
+
158
+ changes
159
+ end
120
160
 
121
- def workspace_symbol_identity_rename_changes(uri, token, lsp_line, lsp_char, new_name)
122
- target_location = resolve_definition_location({
123
- 'textDocument' => { 'uri' => uri },
124
- 'position' => { 'line' => lsp_line, 'character' => lsp_char },
125
- }, stages: nil)
126
- return nil unless target_location
161
+ def definition_identity_key(location)
162
+ uri = location[:uri] || location['uri']
163
+ range = location[:range] || location['range']
164
+ return nil unless uri && range
127
165
 
128
- target_identity = definition_identity_key(target_location)
129
- return nil unless target_identity
166
+ start = range[:start] || range['start']
167
+ return nil unless start
130
168
 
131
- related_uris = @workspace.related_open_document_uris(uri)
132
- return nil if related_uris.empty?
169
+ line = start[:line] || start['line']
170
+ character = start[:character] || start['character']
171
+ return nil if line.nil? || character.nil?
133
172
 
134
- changes = {}
135
- related_uris.each do |related_uri|
136
- tokens = @workspace.get_tokens(related_uri) || []
137
- next if tokens.empty?
173
+ [uri, line, character]
174
+ end
138
175
 
139
- edits = tokens.filter_map do |candidate|
140
- next unless name_like_token?(candidate) && candidate.lexeme == token.lexeme
176
+ def scoped_rename_changes(uri, token, lsp_line, lsp_char, facts, new_name)
177
+ binding_id = rename_target_binding_id(uri, token, lsp_line, lsp_char, facts)
178
+ unless binding_id
179
+ return import_alias_rename_changes(uri, token, lsp_line, lsp_char, facts, new_name)
180
+ end
141
181
 
142
- candidate_location = resolve_definition_location({
143
- 'textDocument' => { 'uri' => related_uri },
144
- 'position' => { 'line' => candidate.line - 1, 'character' => candidate.column - 1 },
145
- }, stages: nil)
146
- next unless candidate_location
147
- next unless definition_identity_key(candidate_location) == target_identity
182
+ ranges = scoped_binding_occurrence_ranges(uri, token.lexeme, facts, binding_id, include_declaration: true)
183
+ return nil if ranges.empty?
148
184
 
185
+ edits = ranges.map do |range|
149
186
  {
150
- range: token_to_range(candidate),
187
+ range: {
188
+ start: { line: range[:line] - 1, character: range[:column] - 1 },
189
+ end: { line: range[:line] - 1, character: range[:column] - 1 + range[:length] },
190
+ },
151
191
  newText: new_name,
152
192
  }
153
193
  end
154
194
 
155
- changes[related_uri] = edits unless edits.empty?
195
+ { uri => edits }
156
196
  end
157
197
 
158
- return nil if changes.empty?
159
-
160
- changes
161
- end
162
-
163
- def definition_identity_key(location)
164
- uri = location[:uri] || location['uri']
165
- range = location[:range] || location['range']
166
- return nil unless uri && range
198
+ def import_alias_rename_changes(uri, token, lsp_line, lsp_char, facts, new_name)
199
+ alias_name = token.lexeme
200
+ if facts
201
+ return nil unless facts.imports.key?(alias_name)
202
+ end
167
203
 
168
- start = range[:start] || range['start']
169
- return nil unless start
204
+ ast = @workspace.get_ast(uri)
205
+ return nil unless ast
170
206
 
171
- line = start[:line] || start['line']
172
- character = start[:character] || start['character']
173
- return nil if line.nil? || character.nil?
207
+ import_node = ast.imports.find do |imp|
208
+ (imp.alias_name || imp.path.parts.last) == alias_name
209
+ end
210
+ return nil unless import_node
174
211
 
175
- [uri, line, character]
176
- end
212
+ # Validate the cursor is at the declaration site or a module-qualifier usage (alias followed by dot).
213
+ tokens = @workspace.get_tokens(uri) || []
177
214
 
178
- def scoped_rename_changes(uri, token, lsp_line, lsp_char, facts, new_name)
179
- binding_id = rename_target_binding_id(uri, token, lsp_line, lsp_char, facts)
180
- unless binding_id
181
- return import_alias_rename_changes(uri, token, lsp_line, lsp_char, facts, new_name)
182
- end
215
+ cursor_at_declaration = token.line == import_node.line && token.column == import_node.column
216
+ unless cursor_at_declaration
217
+ # token was already located by find_token_at; verify it's used as a module qualifier
218
+ tok_idx = tokens.index { |t| t.line == token.line && t.column == token.column }
219
+ return nil unless tok_idx && tokens[tok_idx + 1]&.type == :dot
220
+ end
183
221
 
184
- ranges = scoped_binding_occurrence_ranges(uri, token.lexeme, facts, binding_id, include_declaration: true)
185
- return nil if ranges.empty?
222
+ edits = []
186
223
 
187
- edits = ranges.map do |range|
188
- {
224
+ # Declaration site
225
+ decl_char = import_node.column - 1
226
+ edits << {
189
227
  range: {
190
- start: { line: range[:line] - 1, character: range[:column] - 1 },
191
- end: { line: range[:line] - 1, character: range[:column] - 1 + range[:length] },
228
+ start: { line: import_node.line - 1, character: decl_char },
229
+ end: { line: import_node.line - 1, character: decl_char + alias_name.length },
192
230
  },
193
231
  newText: new_name,
194
232
  }
195
- end
196
233
 
197
- { uri => edits }
198
- end
199
-
200
- def import_alias_rename_changes(uri, token, lsp_line, lsp_char, facts, new_name)
201
- alias_name = token.lexeme
202
- if facts
203
- return nil unless facts.imports.key?(alias_name)
204
- end
234
+ # Usage sites: every token with this lexeme immediately followed by a dot
235
+ tokens.each_with_index do |tok, i|
236
+ next unless tok.type == :identifier && tok.lexeme == alias_name
237
+ next unless tokens[i + 1]&.type == :dot
205
238
 
206
- ast = @workspace.get_ast(uri)
207
- return nil unless ast
239
+ edits << {
240
+ range: {
241
+ start: { line: tok.line - 1, character: tok.column - 1 },
242
+ end: { line: tok.line - 1, character: tok.column - 1 + alias_name.length },
243
+ },
244
+ newText: new_name,
245
+ }
246
+ end
208
247
 
209
- import_node = ast.imports.find do |imp|
210
- (imp.alias_name || imp.path.parts.last) == alias_name
248
+ { uri => edits }
211
249
  end
212
- return nil unless import_node
213
250
 
214
- # Validate the cursor is at the declaration site or a module-qualifier usage (alias followed by dot).
215
- tokens = @workspace.get_tokens(uri) || []
251
+ def enum_member_rename_changes(uri, token, lsp_line, lsp_char, facts, new_name)
252
+ tokens = @workspace.get_tokens(uri) || []
216
253
 
217
- cursor_at_declaration = token.line == import_node.line && token.column == import_node.column
218
- unless cursor_at_declaration
219
- # token was already located by find_token_at; verify it's used as a module qualifier
220
- tok_idx = tokens.index { |t| t.line == token.line && t.column == token.column }
221
- return nil unless tok_idx && tokens[tok_idx + 1]&.type == :dot
222
- end
254
+ cursor_index = tokens.index { |t| t.line == token.line && t.column == token.column }
255
+ return nil unless cursor_index
223
256
 
224
- edits = []
257
+ cursor_is_enum_member = variant_enum_member_declaration?(tokens, cursor_index) ||
258
+ type_name_member_access?(tokens, cursor_index, facts)
259
+ return nil unless cursor_is_enum_member
225
260
 
226
- # Declaration site
227
- decl_char = import_node.column - 1
228
- edits << {
229
- range: {
230
- start: { line: import_node.line - 1, character: decl_char },
231
- end: { line: import_node.line - 1, character: decl_char + alias_name.length },
232
- },
233
- newText: new_name,
234
- }
261
+ changes = {}
262
+ [uri, *@workspace.related_open_document_uris(uri)].uniq.each do |doc_uri|
263
+ doc_tokens = @workspace.get_tokens(doc_uri) || []
264
+ doc_facts = doc_uri == uri ? facts : @workspace.get_facts(doc_uri)
265
+ edits = doc_tokens.each_with_index.filter_map do |tok, i|
266
+ next unless tok.type == :identifier && tok.lexeme == token.lexeme
235
267
 
236
- # Usage sites: every token with this lexeme immediately followed by a dot
237
- tokens.each_with_index do |tok, i|
238
- next unless tok.type == :identifier && tok.lexeme == alias_name
239
- next unless tokens[i + 1]&.type == :dot
268
+ enum_member_decl = variant_enum_member_declaration?(doc_tokens, i)
269
+ enum_member_access = type_name_member_access?(doc_tokens, i, doc_facts)
270
+ next unless enum_member_decl || enum_member_access
240
271
 
241
- edits << {
242
- range: {
243
- start: { line: tok.line - 1, character: tok.column - 1 },
244
- end: { line: tok.line - 1, character: tok.column - 1 + alias_name.length },
245
- },
246
- newText: new_name,
247
- }
248
- end
249
-
250
- { uri => edits }
251
- end
252
-
253
- def enum_member_rename_changes(uri, token, lsp_line, lsp_char, facts, new_name)
254
- tokens = @workspace.get_tokens(uri) || []
255
-
256
- cursor_index = tokens.index { |t| t.line == token.line && t.column == token.column }
257
- return nil unless cursor_index
272
+ {
273
+ range: token_to_range(tok),
274
+ newText: new_name,
275
+ }
276
+ end
258
277
 
259
- cursor_is_enum_member = variant_enum_member_declaration?(tokens, cursor_index) ||
260
- type_name_member_access?(tokens, cursor_index, facts)
261
- return nil unless cursor_is_enum_member
278
+ changes[doc_uri] = edits unless edits.empty?
279
+ end
262
280
 
263
- changes = {}
264
- [uri, *@workspace.related_open_document_uris(uri)].uniq.each do |doc_uri|
265
- doc_tokens = @workspace.get_tokens(doc_uri) || []
266
- doc_facts = doc_uri == uri ? facts : @workspace.get_facts(doc_uri)
267
- edits = doc_tokens.each_with_index.filter_map do |tok, i|
268
- next unless tok.type == :identifier && tok.lexeme == token.lexeme
281
+ return nil if changes.empty?
282
+ changes
283
+ end
269
284
 
270
- enum_member_decl = variant_enum_member_declaration?(doc_tokens, i)
271
- enum_member_access = type_name_member_access?(doc_tokens, i, doc_facts)
272
- next unless enum_member_decl || enum_member_access
285
+ def scoped_local_reference_locations(uri, token, lsp_line, lsp_char, facts, include_declaration:)
286
+ binding_id = rename_target_binding_id(uri, token, lsp_line, lsp_char, facts)
287
+ return nil unless binding_id
288
+ return nil unless local_binding_id?(uri, facts, binding_id)
273
289
 
290
+ ranges = scoped_binding_occurrence_ranges(uri, token.lexeme, facts, binding_id, include_declaration: include_declaration)
291
+ ranges.map do |range|
274
292
  {
275
- range: token_to_range(tok),
276
- newText: new_name,
293
+ uri: uri,
294
+ range: {
295
+ start: { line: range[:line] - 1, character: range[:column] - 1 },
296
+ end: { line: range[:line] - 1, character: range[:column] - 1 + range[:length] },
297
+ },
277
298
  }
278
299
  end
279
-
280
- changes[doc_uri] = edits unless edits.empty?
281
300
  end
282
301
 
283
- return nil if changes.empty?
284
- changes
285
- end
302
+ def local_binding_id?(uri, facts, binding_id)
303
+ ast = @workspace.get_ast(uri)
304
+ return false unless ast
286
305
 
287
- def scoped_local_reference_locations(uri, token, lsp_line, lsp_char, facts, include_declaration:)
288
- binding_id = rename_target_binding_id(uri, token, lsp_line, lsp_char, facts)
289
- return nil unless binding_id
290
- return nil unless local_binding_id?(uri, facts, binding_id)
306
+ resolution = facts.binding_resolution
307
+ return false unless resolution
291
308
 
292
- ranges = scoped_binding_occurrence_ranges(uri, token.lexeme, facts, binding_id, include_declaration: include_declaration)
293
- ranges.map do |range|
294
- {
295
- uri: uri,
296
- range: {
297
- start: { line: range[:line] - 1, character: range[:column] - 1 },
298
- end: { line: range[:line] - 1, character: range[:column] - 1 + range[:length] },
299
- },
300
- }
301
- end
302
- end
309
+ each_ast_node(ast) do |node|
310
+ next unless local_binding_declaration_node?(node)
311
+ next unless resolution.declaration_binding_ids[node.object_id] == binding_id
303
312
 
304
- def local_binding_id?(uri, facts, binding_id)
305
- ast = @workspace.get_ast(uri)
306
- return false unless ast
307
-
308
- resolution = facts.binding_resolution
309
- return false unless resolution
313
+ return true
314
+ end
310
315
 
311
- each_ast_node(ast) do |node|
312
- next unless local_binding_declaration_node?(node)
313
- next unless resolution.declaration_binding_ids[node.object_id] == binding_id
316
+ false
317
+ end
314
318
 
315
- return true
319
+ def local_binding_declaration_node?(node)
320
+ node.is_a?(AST::LocalDecl) ||
321
+ node.is_a?(AST::Param) ||
322
+ node.is_a?(AST::ForBinding) ||
323
+ node.is_a?(AST::MatchArm) ||
324
+ node.is_a?(AST::MatchExprArm)
316
325
  end
317
326
 
318
- false
319
- end
327
+ def rename_target_binding_id(uri, token, lsp_line, lsp_char, facts)
328
+ resolution = facts.binding_resolution
329
+ return nil unless resolution
320
330
 
321
- def local_binding_declaration_node?(node)
322
- node.is_a?(AST::LocalDecl) ||
323
- node.is_a?(AST::Param) ||
324
- node.is_a?(AST::ForBinding) ||
325
- node.is_a?(AST::MatchArm) ||
326
- node.is_a?(AST::MatchExprArm)
327
- end
331
+ line = lsp_line + 1
332
+ char = lsp_char + 1
333
+ ast = @workspace.get_ast(uri)
334
+ return nil unless ast
328
335
 
329
- def rename_target_binding_id(uri, token, lsp_line, lsp_char, facts)
330
- resolution = facts.binding_resolution
331
- return nil unless resolution
336
+ identifier_node = find_identifier_ast_node_at(ast, token.lexeme, line, char)
337
+ if identifier_node && (id = resolution.identifier_binding_ids[identifier_node.object_id])
338
+ return id
339
+ end
332
340
 
333
- line = lsp_line + 1
334
- char = lsp_char + 1
335
- ast = @workspace.get_ast(uri)
336
- return nil unless ast
341
+ declaration_node = find_declaration_ast_node_at(ast, token.lexeme, line, char)
342
+ if declaration_node && (id = resolution.declaration_binding_ids[declaration_node.object_id])
343
+ return id
344
+ end
337
345
 
338
- identifier_node = find_identifier_ast_node_at(ast, token.lexeme, line, char)
339
- if identifier_node && (id = resolution.identifier_binding_ids[identifier_node.object_id])
340
- return id
346
+ resolve_local_hover_binding(facts, token.lexeme, line, char)&.id
341
347
  end
342
348
 
343
- declaration_node = find_declaration_ast_node_at(ast, token.lexeme, line, char)
344
- if declaration_node && (id = resolution.declaration_binding_ids[declaration_node.object_id])
345
- return id
346
- end
349
+ def scoped_binding_occurrence_ranges(uri, name, facts, binding_id, include_declaration:)
350
+ ast = @workspace.get_ast(uri)
351
+ return [] unless ast
347
352
 
348
- resolve_local_hover_binding(facts, token.lexeme, line, char)&.id
349
- end
353
+ resolution = facts.binding_resolution
354
+ return [] unless resolution
350
355
 
351
- def scoped_binding_occurrence_ranges(uri, name, facts, binding_id, include_declaration:)
352
- ast = @workspace.get_ast(uri)
353
- return [] unless ast
356
+ ranges = []
357
+ seen = Set.new
354
358
 
355
- resolution = facts.binding_resolution
356
- return [] unless resolution
359
+ each_ast_node(ast) do |node|
360
+ range = nil
357
361
 
358
- ranges = []
359
- seen = Set.new
360
-
361
- each_ast_node(ast) do |node|
362
- range = nil
362
+ if node.is_a?(AST::Identifier) && node.name == name && resolution.identifier_binding_ids[node.object_id] == binding_id
363
+ range = ast_name_range(node.name, node.line, node.column)
364
+ elsif include_declaration && resolution.declaration_binding_ids[node.object_id] == binding_id
365
+ range = declaration_name_range(node)
366
+ end
363
367
 
364
- if node.is_a?(AST::Identifier) && node.name == name && resolution.identifier_binding_ids[node.object_id] == binding_id
365
- range = ast_name_range(node.name, node.line, node.column)
366
- elsif include_declaration && resolution.declaration_binding_ids[node.object_id] == binding_id
367
- range = declaration_name_range(node)
368
- end
368
+ next unless range
369
369
 
370
- next unless range
370
+ key = [range[:line], range[:column], range[:length]]
371
+ next if seen.include?(key)
371
372
 
372
- key = [range[:line], range[:column], range[:length]]
373
- next if seen.include?(key)
373
+ seen << key
374
+ ranges << range
375
+ end
374
376
 
375
- seen << key
376
- ranges << range
377
+ ranges
377
378
  end
378
379
 
379
- ranges
380
- end
380
+ def find_identifier_ast_node_at(ast, name, line, char)
381
+ each_ast_node(ast) do |node|
382
+ next unless node.is_a?(AST::Identifier)
383
+ next unless node.name == name
384
+ next unless node.line == line
385
+ next unless char >= node.column && char < node.column + name.length
381
386
 
382
- def find_identifier_ast_node_at(ast, name, line, char)
383
- each_ast_node(ast) do |node|
384
- next unless node.is_a?(AST::Identifier)
385
- next unless node.name == name
386
- next unless node.line == line
387
- next unless char >= node.column && char < node.column + name.length
387
+ return node
388
+ end
388
389
 
389
- return node
390
+ nil
390
391
  end
391
392
 
392
- nil
393
- end
393
+ def find_declaration_ast_node_at(ast, name, line, char)
394
+ each_ast_node(ast) do |node|
395
+ range = declaration_name_range(node)
396
+ next unless range
397
+ next unless range[:name] == name
398
+ next unless range[:line] == line
399
+ next unless char >= range[:column] && char < range[:column] + range[:length]
394
400
 
395
- def find_declaration_ast_node_at(ast, name, line, char)
396
- each_ast_node(ast) do |node|
397
- range = declaration_name_range(node)
398
- next unless range
399
- next unless range[:name] == name
400
- next unless range[:line] == line
401
- next unless char >= range[:column] && char < range[:column] + range[:length]
401
+ return node
402
+ end
402
403
 
403
- return node
404
+ nil
404
405
  end
405
406
 
406
- nil
407
- end
408
-
409
- def declaration_name_range(node)
410
- if node.is_a?(AST::MatchArm) || node.is_a?(AST::MatchExprArm)
411
- return nil unless node.binding_name && node.binding_line && node.binding_column
407
+ def declaration_name_range(node)
408
+ if node.is_a?(AST::MatchArm) || node.is_a?(AST::MatchExprArm)
409
+ return nil unless node.binding_name && node.binding_line && node.binding_column
412
410
 
413
- return {
414
- name: node.binding_name,
415
- line: node.binding_line,
416
- column: node.binding_column,
417
- length: node.binding_name.length,
418
- }
419
- end
411
+ return {
412
+ name: node.binding_name,
413
+ line: node.binding_line,
414
+ column: node.binding_column,
415
+ length: node.binding_name.length,
416
+ }
417
+ end
420
418
 
421
- return nil if node.is_a?(AST::ForStmt)
422
- return nil unless node.respond_to?(:name) && node.respond_to?(:line) && node.respond_to?(:column)
423
- return nil unless node.name.is_a?(String) && node.line && node.column
419
+ return nil if node.is_a?(AST::ForStmt)
420
+ return nil unless node.respond_to?(:name)
421
+ return nil unless node.name.is_a?(String) && node.line && node.column
424
422
 
425
- ast_name_range(node.name, node.line, node.column)
426
- end
423
+ ast_name_range(node.name, node.line, node.column)
424
+ end
427
425
 
428
- def ast_name_range(name, line, column)
429
- { name: name, line: line, column: column, length: name.length }
430
- end
426
+ def ast_name_range(name, line, column)
427
+ { name: name, line: line, column: column, length: name.length }
428
+ end
431
429
 
432
- def each_ast_node(node, &block)
433
- return if node.nil?
430
+ def each_ast_node(node, &block)
431
+ return if node.nil?
434
432
 
435
- if node.is_a?(Array)
436
- node.each { |item| each_ast_node(item, &block) }
437
- return
438
- end
433
+ if node.is_a?(Array)
434
+ node.each { |item| each_ast_node(item, &block) }
435
+ return
436
+ end
439
437
 
440
- class_name = node.class.name
441
- return unless class_name && class_name.start_with?('MilkTea::AST::')
438
+ class_name = node.class.name
439
+ return unless class_name && class_name.start_with?('MilkTea::AST::')
442
440
 
443
- yield node
441
+ yield node
444
442
 
445
- if node.respond_to?(:members)
446
- node.members.each do |member|
447
- next unless member.is_a?(Symbol)
448
- each_ast_node(node.public_send(member), &block)
443
+ if node.respond_to?(:members)
444
+ node.members.each do |member|
445
+ next unless member.is_a?(Symbol)
446
+ each_ast_node(node.public_send(member), &block)
447
+ end
449
448
  end
450
449
  end
451
- end
452
450
 
453
- def struct_field_rename_changes(uri, token, _lsp_line, _lsp_char, facts, new_name)
454
- field_name = token.lexeme
455
- tokens = @workspace.get_tokens(uri) || []
456
- idx = tokens.index { |t| t.line == token.line && t.column == token.column }
457
- return nil unless idx
458
-
459
- cursor_in_field_context = struct_field_cursor_context?(tokens, idx)
460
- return nil unless cursor_in_field_context
461
-
462
- struct_type = find_struct_containing_field(facts, field_name)
463
- return nil unless struct_type
464
-
465
- changes = {}
466
- edits = collect_struct_field_changes(uri, field_name, new_name)
467
- changes[uri] = edits if edits
468
- related_uris = @workspace.related_open_document_uris(uri)
469
- related_uris.each do |related_uri|
470
- next if related_uri == uri
471
- related_changes = collect_struct_field_changes(related_uri, field_name, new_name)
472
- changes[related_uri] = related_changes if related_changes
473
- end
451
+ def struct_field_rename_changes(uri, token, _lsp_line, _lsp_char, facts, new_name)
452
+ field_name = token.lexeme
453
+ tokens = @workspace.get_tokens(uri) || []
454
+ idx = tokens.index { |t| t.line == token.line && t.column == token.column }
455
+ return nil unless idx
456
+
457
+ cursor_in_field_context = struct_field_cursor_context?(tokens, idx)
458
+ return nil unless cursor_in_field_context
459
+
460
+ struct_type = find_struct_containing_field(facts, field_name)
461
+ return nil unless struct_type
462
+
463
+ changes = {}
464
+ edits = collect_struct_field_changes(uri, field_name, new_name)
465
+ changes[uri] = edits if edits
466
+ related_uris = @workspace.related_open_document_uris(uri)
467
+ related_uris.each do |related_uri|
468
+ next if related_uri == uri
469
+ related_changes = collect_struct_field_changes(related_uri, field_name, new_name)
470
+ changes[related_uri] = related_changes if related_changes
471
+ end
474
472
 
475
- return nil if changes.empty?
476
- changes
477
- end
473
+ return nil if changes.empty?
474
+ changes
475
+ end
478
476
 
479
- def collect_struct_field_changes(uri, field_name, new_name)
480
- tokens = @workspace.get_tokens(uri) || []
481
- edits = []
482
- tokens.each_with_index do |tok, i|
483
- next unless tok.type == :identifier && tok.lexeme == field_name
484
- next unless struct_field_edit_context?(tokens, i, nil)
477
+ def collect_struct_field_changes(uri, field_name, new_name)
478
+ tokens = @workspace.get_tokens(uri) || []
479
+ edits = []
480
+ tokens.each_with_index do |tok, i|
481
+ next unless tok.type == :identifier && tok.lexeme == field_name
482
+ next unless struct_field_edit_context?(tokens, i, nil)
485
483
 
486
- edits << { range: token_to_range(tok), newText: new_name }
484
+ edits << { range: token_to_range(tok), newText: new_name }
485
+ end
486
+ edits.empty? ? nil : edits
487
487
  end
488
- edits.empty? ? nil : edits
489
- end
490
488
 
491
- def struct_field_cursor_context?(tokens, index)
492
- next_nt = skip_trivia_forward(tokens, index + 1)
493
- return true if next_nt && tokens[next_nt]&.type == :colon
489
+ def struct_field_cursor_context?(tokens, index)
490
+ next_nt = skip_trivia_forward(tokens, index + 1)
491
+ return true if next_nt && tokens[next_nt]&.type == :colon
494
492
 
495
- return true if index > 0 && tokens[index - 1].type == :dot
493
+ return true if index > 0 && tokens[index - 1].type == :dot
496
494
 
497
- if index > 0 && (tokens[index - 1].type == :lparen || tokens[index - 1].type == :comma)
498
- next_nt = skip_trivia_forward(tokens, index + 1)
499
- return true if next_nt && (tokens[next_nt]&.type == :equal || tokens[next_nt]&.type == :colon)
500
- return true if next_nt.nil? || !(tokens[next_nt]&.type == :equal || tokens[next_nt]&.type == :colon)
495
+ if index > 0 && (tokens[index - 1].type == :lparen || tokens[index - 1].type == :comma)
496
+ next_nt = skip_trivia_forward(tokens, index + 1)
497
+ return true if next_nt && (tokens[next_nt]&.type == :equal || tokens[next_nt]&.type == :colon)
498
+ return true if next_nt.nil? || !(tokens[next_nt]&.type == :equal || tokens[next_nt]&.type == :colon)
499
+ end
500
+
501
+ false
501
502
  end
502
503
 
503
- false
504
- end
504
+ def struct_field_edit_context?(tokens, index, _struct_type)
505
+ return true if index > 0 && tokens[index - 1].type == :dot
505
506
 
506
- def struct_field_edit_context?(tokens, index, _struct_type)
507
- return true if index > 0 && tokens[index - 1].type == :dot
507
+ next_nt = skip_trivia_forward(tokens, index + 1)
508
+ return true if next_nt && tokens[next_nt]&.type == :colon
508
509
 
509
- next_nt = skip_trivia_forward(tokens, index + 1)
510
- return true if next_nt && tokens[next_nt]&.type == :colon
510
+ if index > 0 && (tokens[index - 1].type == :lparen || tokens[index - 1].type == :comma)
511
+ next_nt = skip_trivia_forward(tokens, index + 1)
512
+ return true if next_nt.nil? || (tokens[next_nt]&.type == :equal || tokens[next_nt]&.type == :colon) || tokens[next_nt]&.type == :comma || tokens[next_nt]&.type == :rparen
513
+ end
511
514
 
512
- if index > 0 && (tokens[index - 1].type == :lparen || tokens[index - 1].type == :comma)
513
- next_nt = skip_trivia_forward(tokens, index + 1)
514
- return true if next_nt.nil? || (tokens[next_nt]&.type == :equal || tokens[next_nt]&.type == :colon) || tokens[next_nt]&.type == :comma || tokens[next_nt]&.type == :rparen
515
+ false
515
516
  end
516
517
 
517
- false
518
- end
519
-
520
- def skip_trivia_forward(tokens, start_index)
521
- i = start_index
522
- while i < tokens.length
523
- return i unless tokens[i].type == :whitespace || tokens[i].type == :newline || tokens[i].type == :comment
524
- i += 1
518
+ def skip_trivia_forward(tokens, start_index)
519
+ i = start_index
520
+ while i < tokens.length
521
+ return i unless tokens[i].type == :whitespace || tokens[i].type == :newline || tokens[i].type == :comment
522
+ i += 1
523
+ end
524
+ nil
525
525
  end
526
- nil
527
- end
528
526
 
529
- def find_struct_containing_field(facts, field_name)
530
- facts.types.each_value do |type|
531
- target = if type.is_a?(Types::GenericStructDefinition)
532
- type
533
- elsif type.is_a?(Types::Struct) || type.is_a?(Types::StructInstance)
534
- type.is_a?(Types::StructInstance) ? type.definition : type
535
- else
536
- next
537
- end
538
- next unless target.respond_to?(:fields) && target.fields.key?(field_name)
539
- return type
527
+ def find_struct_containing_field(facts, field_name)
528
+ facts.types.each_value do |type|
529
+ target = if type.is_a?(Types::GenericStructDefinition)
530
+ type
531
+ elsif type.is_a?(Types::Struct) || type.is_a?(Types::StructInstance)
532
+ type.is_a?(Types::StructInstance) ? type.definition : type
533
+ else
534
+ next
535
+ end
536
+ next unless target.respond_to?(:fields) && target.fields.key?(field_name)
537
+ return type
538
+ end
539
+ nil
540
540
  end
541
- nil
542
- end
543
541
 
544
- def method_rename_changes(uri, token, _lsp_line, _lsp_char, facts, new_name)
545
- method_name = token.lexeme
546
- method_info = find_method_at_position(uri, token, facts)
547
- return nil unless method_info
542
+ def method_rename_changes(uri, token, _lsp_line, _lsp_char, facts, new_name)
543
+ method_name = token.lexeme
544
+ method_info = find_method_at_position(uri, token, facts)
545
+ return nil unless method_info
548
546
 
549
- receiver_type = method_info[:receiver_type]
550
- return nil unless receiver_type
547
+ receiver_type = method_info[:receiver_type]
548
+ return nil unless receiver_type
551
549
 
552
- changes = {}
553
- [uri, *@workspace.related_open_document_uris(uri)].uniq.each do |doc_uri|
554
- edits = []
555
- doc_tokens = @workspace.get_tokens(doc_uri) || []
556
- doc_tokens.each_with_index do |tok, i|
557
- next unless tok.type == :identifier && tok.lexeme == method_name
550
+ changes = {}
551
+ [uri, *@workspace.related_open_document_uris(uri)].uniq.each do |doc_uri|
552
+ edits = []
553
+ doc_tokens = @workspace.get_tokens(doc_uri) || []
554
+ doc_tokens.each_with_index do |tok, i|
555
+ next unless tok.type == :identifier && tok.lexeme == method_name
558
556
 
559
- is_call = i > 0 && doc_tokens[i - 1].type == :dot
560
- is_def = doc_uri == uri && tok.line == token.line && tok.column == token.column
561
- next unless is_call || is_def
557
+ is_call = i > 0 && doc_tokens[i - 1].type == :dot
558
+ is_def = doc_uri == uri && tok.line == token.line && tok.column == token.column
559
+ next unless is_call || is_def
562
560
 
563
- edits << { range: token_to_range(tok), newText: new_name }
561
+ edits << { range: token_to_range(tok), newText: new_name }
562
+ end
563
+
564
+ changes[doc_uri] = edits unless edits.empty?
564
565
  end
565
566
 
566
- changes[doc_uri] = edits unless edits.empty?
567
+ return nil if changes.empty?
568
+ changes
567
569
  end
568
570
 
569
- return nil if changes.empty?
570
- changes
571
- end
572
-
573
- def find_method_at_position(uri, token, facts)
574
- ast = @workspace.get_ast(uri)
575
- return nil unless ast
571
+ def find_method_at_position(uri, token, facts)
572
+ ast = @workspace.get_ast(uri)
573
+ return nil unless ast
576
574
 
577
- each_ast_node(ast) do |node|
578
- next unless node.is_a?(AST::MethodDef)
579
- next unless node.line == token.line
580
- next unless token.column >= node.column && token.column < node.column + node.name.length
575
+ each_ast_node(ast) do |node|
576
+ next unless node.is_a?(AST::MethodDef)
577
+ next unless node.line == token.line
578
+ next unless token.column >= node.column && token.column < node.column + node.name.length
581
579
 
582
- type_name = find_extending_type_for_method(ast, node)
583
- next unless type_name
580
+ type_name = find_extending_type_for_method(ast, node)
581
+ next unless type_name
584
582
 
585
- { receiver_type: facts.types[type_name], method_name: node.name }
583
+ { receiver_type: facts.types[type_name], method_name: node.name }
584
+ end
585
+ nil
586
586
  end
587
- nil
588
- end
589
587
 
590
- def find_extending_type_for_method(ast, method_node)
591
- each_ast_node(ast) do |node|
592
- next unless node.is_a?(AST::ExtendingBlock)
593
- return node.type_name if node.methods.any? { |m| m.object_id == method_node.object_id }
588
+ def find_extending_type_for_method(ast, method_node)
589
+ each_ast_node(ast) do |node|
590
+ next unless node.is_a?(AST::ExtendingBlock)
591
+ return node.type_name if node.methods.any? { |m| m.object_id == method_node.object_id }
592
+ end
593
+ nil
594
594
  end
595
- nil
596
- end
597
595
  end
598
596
  end
599
597
  end