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.
- checksums.yaml +4 -4
- data/lib/milk_tea/base.rb +1 -2
- data/lib/milk_tea/bindings/bindgen/ast_parser.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/declaration.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/emitter.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/overrides.rb +0 -2
- data/lib/milk_tea/bindings/bindgen/type_mapper.rb +51 -53
- data/lib/milk_tea/bindings/imported_bindings/generator.rb +7 -9
- data/lib/milk_tea/bindings/imported_bindings/method_source.rb +0 -2
- data/lib/milk_tea/bindings/imported_bindings/naming.rb +6 -8
- data/lib/milk_tea/bindings/imported_bindings.rb +0 -2
- data/lib/milk_tea/bindings/opengl_registry.rb +3 -7
- data/lib/milk_tea/bindings/steamworks.rb +12 -16
- data/lib/milk_tea/bindings/upstream_sources.rb +10 -14
- data/lib/milk_tea/bindings/vendored_box2d.rb +10 -12
- data/lib/milk_tea/bindings/vendored_cjson.rb +6 -8
- data/lib/milk_tea/bindings/vendored_flecs.rb +10 -12
- data/lib/milk_tea/bindings/vendored_glfw.rb +11 -13
- data/lib/milk_tea/bindings/vendored_libuv.rb +11 -13
- data/lib/milk_tea/bindings/vendored_pcre2.rb +11 -13
- data/lib/milk_tea/bindings/vendored_raylib.rb +6 -10
- data/lib/milk_tea/bindings/vendored_sdl3.rb +11 -13
- data/lib/milk_tea/bindings/vendored_steamworks.rb +1 -5
- data/lib/milk_tea/core/ast.rb +103 -45
- data/lib/milk_tea/core/async_runtime_installer.rb +0 -2
- data/lib/milk_tea/core/bindings/attribute_binding.rb +1 -3
- data/lib/milk_tea/core/c_backend/aggregate_utils.rb +147 -84
- data/lib/milk_tea/core/c_backend/control_flow_emission.rb +394 -396
- data/lib/milk_tea/core/c_backend/expressions.rb +489 -476
- data/lib/milk_tea/core/c_backend/feature_detection.rb +446 -437
- data/lib/milk_tea/core/c_backend/format_helpers.rb +0 -2
- data/lib/milk_tea/core/c_backend/reachability.rb +339 -341
- data/lib/milk_tea/core/c_backend/reinterpret.rb +231 -217
- data/lib/milk_tea/core/c_backend/runtime_helpers.rb +531 -526
- data/lib/milk_tea/core/c_backend/statements.rb +528 -530
- data/lib/milk_tea/core/c_backend/type_collectors.rb +1344 -1346
- data/lib/milk_tea/core/c_backend/type_declaration.rb +215 -213
- data/lib/milk_tea/core/c_backend/type_system.rb +347 -349
- data/lib/milk_tea/core/c_backend.rb +17 -9
- data/lib/milk_tea/core/compatibility_helpers.rb +0 -2
- data/lib/milk_tea/core/compile_time/const_eval.rb +7 -12
- data/lib/milk_tea/core/compile_time.rb +32 -38
- data/lib/milk_tea/core/control_flow/builder.rb +3 -5
- data/lib/milk_tea/core/control_flow/constant_propagation.rb +14 -17
- data/lib/milk_tea/core/control_flow/definite_assignment.rb +0 -1
- data/lib/milk_tea/core/ir.rb +10 -10
- data/lib/milk_tea/core/lexer/character_classes.rb +5 -7
- data/lib/milk_tea/core/lexer/format_strings.rb +7 -9
- data/lib/milk_tea/core/lexer/heredocs.rb +22 -22
- data/lib/milk_tea/core/lexer/indentation.rb +0 -2
- data/lib/milk_tea/core/lexer/numbers.rb +0 -2
- data/lib/milk_tea/core/lexer/recovery.rb +0 -2
- data/lib/milk_tea/core/lexer/strings.rb +0 -2
- data/lib/milk_tea/core/lexer/symbols.rb +0 -2
- data/lib/milk_tea/core/lexer/trivia.rb +0 -2
- data/lib/milk_tea/core/lexer.rb +0 -2
- data/lib/milk_tea/core/lowering/async/analysis.rb +0 -2
- data/lib/milk_tea/core/lowering/async/lowering.rb +1264 -1265
- data/lib/milk_tea/core/lowering/async/normalization.rb +0 -2
- data/lib/milk_tea/core/lowering/async.rb +627 -630
- data/lib/milk_tea/core/lowering/block.rb +413 -416
- data/lib/milk_tea/core/lowering/calls.rb +1381 -1384
- data/lib/milk_tea/core/lowering/declarations.rb +180 -183
- data/lib/milk_tea/core/lowering/dyn.rb +48 -50
- data/lib/milk_tea/core/lowering/events.rb +983 -985
- data/lib/milk_tea/core/lowering/expressions.rb +1513 -1518
- data/lib/milk_tea/core/lowering/foreign_cstr.rb +181 -184
- data/lib/milk_tea/core/lowering/functions.rb +185 -188
- data/lib/milk_tea/core/lowering/loops.rb +1028 -1031
- data/lib/milk_tea/core/lowering/proc.rb +363 -366
- data/lib/milk_tea/core/lowering/resolve.rb +2133 -2135
- data/lib/milk_tea/core/lowering/scans.rb +179 -181
- data/lib/milk_tea/core/lowering/str_buffer.rb +102 -105
- data/lib/milk_tea/core/lowering/utils.rb +1040 -1044
- data/lib/milk_tea/core/lowering.rb +5 -7
- data/lib/milk_tea/core/module_binder.rb +0 -2
- data/lib/milk_tea/core/module_loader/errors.rb +4 -2
- data/lib/milk_tea/core/module_loader.rb +113 -20
- data/lib/milk_tea/core/module_path_resolver.rb +0 -2
- data/lib/milk_tea/core/module_roots.rb +6 -8
- data/lib/milk_tea/core/parser/attributes.rb +0 -2
- data/lib/milk_tea/core/parser/blocks.rb +0 -2
- data/lib/milk_tea/core/parser/declarations.rb +0 -2
- data/lib/milk_tea/core/parser/expressions.rb +31 -14
- data/lib/milk_tea/core/parser/recovery.rb +0 -2
- data/lib/milk_tea/core/parser/statements.rb +0 -2
- data/lib/milk_tea/core/parser/types.rb +8 -10
- data/lib/milk_tea/core/parser.rb +1 -3
- data/lib/milk_tea/core/pretty_printer/ast_formatter.rb +31 -33
- data/lib/milk_tea/core/pretty_printer/base_formatter.rb +0 -2
- data/lib/milk_tea/core/pretty_printer/ir_formatter.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/analysis_context.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/attributes.rb +14 -16
- data/lib/milk_tea/core/semantic_analyzer/calls.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/expressions.rb +1 -14
- data/lib/milk_tea/core/semantic_analyzer/flow_refinement.rb +18 -21
- data/lib/milk_tea/core/semantic_analyzer/foreign_functions.rb +0 -3
- data/lib/milk_tea/core/semantic_analyzer/function_binding.rb +12 -15
- data/lib/milk_tea/core/semantic_analyzer/generics.rb +57 -59
- data/lib/milk_tea/core/semantic_analyzer/interface_conformance.rb +0 -2
- data/lib/milk_tea/core/semantic_analyzer/name_resolution.rb +5 -9
- data/lib/milk_tea/core/semantic_analyzer/nullability.rb +0 -3
- data/lib/milk_tea/core/semantic_analyzer/statements.rb +35 -40
- data/lib/milk_tea/core/semantic_analyzer/top_level.rb +10 -12
- data/lib/milk_tea/core/semantic_analyzer/type_compatibility.rb +0 -6
- data/lib/milk_tea/core/semantic_analyzer/type_declaration.rb +102 -105
- data/lib/milk_tea/core/semantic_analyzer.rb +17 -8
- data/lib/milk_tea/core/types/layout.rb +21 -22
- data/lib/milk_tea/core/types/predicates.rb +0 -2
- data/lib/milk_tea/core/types/registry.rb +16 -18
- data/lib/milk_tea/core/types/visitor.rb +0 -4
- data/lib/milk_tea/dap/backends/lldb_dap.rb +0 -2
- data/lib/milk_tea/dap/protocol.rb +1 -1
- data/lib/milk_tea/dap/server/breakpoints.rb +0 -2
- data/lib/milk_tea/dap/server/debug_map.rb +0 -2
- data/lib/milk_tea/dap/server/handlers.rb +0 -2
- data/lib/milk_tea/dap/server/launch.rb +0 -2
- data/lib/milk_tea/dap/server/lldb_backend.rb +8 -10
- data/lib/milk_tea/dap/server/pause_diagnostics.rb +14 -16
- data/lib/milk_tea/dap/server/utilities.rb +0 -2
- data/lib/milk_tea/dap/server/wire.rb +0 -2
- data/lib/milk_tea/dap/server.rb +0 -2
- data/lib/milk_tea/lsp/diagnostics.rb +16 -16
- data/lib/milk_tea/lsp/server/call_hierarchy.rb +11 -12
- data/lib/milk_tea/lsp/server/code_actions.rb +355 -357
- data/lib/milk_tea/lsp/server/code_lens.rb +23 -25
- data/lib/milk_tea/lsp/server/completion.rb +873 -875
- data/lib/milk_tea/lsp/server/configuration.rb +129 -131
- data/lib/milk_tea/lsp/server/debug_info.rb +0 -2
- data/lib/milk_tea/lsp/server/definition.rb +564 -565
- data/lib/milk_tea/lsp/server/diagnostics_scheduling.rb +191 -193
- data/lib/milk_tea/lsp/server/execute_command.rb +0 -2
- data/lib/milk_tea/lsp/server/folding_range.rb +0 -4
- data/lib/milk_tea/lsp/server/formatting.rb +478 -480
- data/lib/milk_tea/lsp/server/hover.rb +1282 -1284
- data/lib/milk_tea/lsp/server/inlay_hints.rb +179 -181
- data/lib/milk_tea/lsp/server/lifecycle.rb +192 -194
- data/lib/milk_tea/lsp/server/linked_editing_range.rb +0 -2
- data/lib/milk_tea/lsp/server/on_type_formatting.rb +0 -2
- data/lib/milk_tea/lsp/server/progress.rb +0 -2
- data/lib/milk_tea/lsp/server/references.rb +316 -318
- data/lib/milk_tea/lsp/server/rename.rb +445 -447
- data/lib/milk_tea/lsp/server/selection_range.rb +0 -4
- data/lib/milk_tea/lsp/server/semantic_tokens.rb +1318 -1320
- data/lib/milk_tea/lsp/server/signature_help.rb +146 -148
- data/lib/milk_tea/lsp/server/text_documents.rb +99 -101
- data/lib/milk_tea/lsp/server/type_hierarchy.rb +2 -4
- data/lib/milk_tea/lsp/server/utilities.rb +397 -399
- data/lib/milk_tea/lsp/server.rb +0 -3
- data/lib/milk_tea/lsp/workspace/analysis.rb +40 -42
- data/lib/milk_tea/lsp/workspace/dependency_graph.rb +4 -10
- data/lib/milk_tea/lsp/workspace/utilities.rb +19 -19
- data/lib/milk_tea/packages/atomic_write.rb +4 -5
- data/lib/milk_tea/packages/dependency_solver.rb +15 -15
- data/lib/milk_tea/packages/lock.rb +4 -4
- data/lib/milk_tea/packages/manager_cli.rb +1 -1
- data/lib/milk_tea/packages/manifest.rb +3 -3
- data/lib/milk_tea/packages/registry_store.rb +4 -6
- data/lib/milk_tea/packages/services.rb +16 -16
- data/lib/milk_tea/packages/source_cache.rb +4 -6
- data/lib/milk_tea/packages/source_resolver.rb +44 -44
- data/lib/milk_tea/packages/version.rb +8 -8
- data/lib/milk_tea/tooling/build.rb +3 -7
- data/lib/milk_tea/tooling/build_cache.rb +0 -2
- data/lib/milk_tea/tooling/debug_info_formatter.rb +31 -33
- data/lib/milk_tea/tooling/debug_map.rb +8 -10
- data/lib/milk_tea/tooling/docs_app.rb +0 -2
- data/lib/milk_tea/tooling/linter/doc_tags.rb +25 -27
- data/lib/milk_tea/tooling/linter/fix_engine.rb +87 -24
- data/lib/milk_tea/tooling/linter/flow_rules.rb +44 -46
- data/lib/milk_tea/tooling/linter/imports_platform.rb +14 -16
- data/lib/milk_tea/tooling/linter/release_rules.rb +3 -5
- data/lib/milk_tea/tooling/linter/reserved_names.rb +23 -25
- data/lib/milk_tea/tooling/linter/rules.rb +87 -89
- data/lib/milk_tea/tooling/linter/source_helpers.rb +40 -42
- data/lib/milk_tea/tooling/linter/trailing_comma.rb +17 -19
- data/lib/milk_tea/tooling/linter/visitors.rb +162 -50
- data/lib/milk_tea/tooling/linter.rb +0 -2
- data/lib/milk_tea/tooling/project_scaffold.rb +0 -2
- data/lib/milk_tea/tooling/run.rb +0 -2
- data/lib/milk_tea/tooling/toolchain_cli.rb +0 -2
- data/std/cli.mt +13 -32
- data/std/color.mt +28 -28
- data/std/encoding.mt +4 -4
- data/std/graph.mt +1 -2
- data/std/hash.mt +14 -14
- data/std/http/server.mt +3 -3
- data/std/input.mt +1 -1
- data/std/json.mt +1 -1
- data/std/linear_algebra.mt +6 -6
- data/std/net/mux.mt +1 -1
- data/std/noise.mt +23 -23
- data/std/parse.mt +2 -2
- data/std/process.mt +3 -4
- data/std/sparse_set.mt +1 -1
- data/std/steering.mt +5 -5
- data/std/str.mt +1 -1
- data/std/tween.mt +4 -4
- data/std/utility.mt +3 -3
- metadata +2 -2
|
@@ -4,596 +4,594 @@ module MilkTea
|
|
|
4
4
|
module LSP
|
|
5
5
|
class Server
|
|
6
6
|
module ServerRename
|
|
7
|
-
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
15
|
-
|
|
26
|
+
def allow_hover_last_good_fallback?(uri)
|
|
27
|
+
return true unless @workspace.dependency_resolution_mode == :frozen
|
|
16
28
|
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
32
|
+
DependencyResolution.resolve(path, mode: @workspace.dependency_resolution_mode).ok?
|
|
33
|
+
rescue StandardError
|
|
34
|
+
true
|
|
35
|
+
end
|
|
27
36
|
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
32
|
-
|
|
43
|
+
token = @workspace.find_token_at(uri, lsp_line, lsp_char)
|
|
44
|
+
return nil unless name_like_token?(token)
|
|
33
45
|
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
46
|
-
|
|
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
|
-
|
|
49
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
60
|
-
|
|
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
|
-
|
|
63
|
-
|
|
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
|
-
|
|
67
|
-
|
|
74
|
+
nil
|
|
75
|
+
rescue StandardError => e
|
|
76
|
+
warn "Error in rename handler: #{e.message}"
|
|
77
|
+
nil
|
|
68
78
|
end
|
|
69
79
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
110
|
-
|
|
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
|
-
|
|
114
|
+
return nil if edits.empty?
|
|
115
|
+
|
|
116
|
+
{ uri => edits }
|
|
114
117
|
end
|
|
115
118
|
|
|
116
|
-
|
|
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
|
-
|
|
119
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
129
|
-
|
|
166
|
+
start = range[:start] || range['start']
|
|
167
|
+
return nil unless start
|
|
130
168
|
|
|
131
|
-
|
|
132
|
-
|
|
169
|
+
line = start[:line] || start['line']
|
|
170
|
+
character = start[:character] || start['character']
|
|
171
|
+
return nil if line.nil? || character.nil?
|
|
133
172
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
tokens = @workspace.get_tokens(related_uri) || []
|
|
137
|
-
next if tokens.empty?
|
|
173
|
+
[uri, line, character]
|
|
174
|
+
end
|
|
138
175
|
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
143
|
-
|
|
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:
|
|
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
|
-
|
|
195
|
+
{ uri => edits }
|
|
156
196
|
end
|
|
157
197
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
169
|
-
|
|
204
|
+
ast = @workspace.get_ast(uri)
|
|
205
|
+
return nil unless ast
|
|
170
206
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
176
|
-
|
|
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
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
|
|
185
|
-
return nil if ranges.empty?
|
|
222
|
+
edits = []
|
|
186
223
|
|
|
187
|
-
|
|
188
|
-
|
|
224
|
+
# Declaration site
|
|
225
|
+
decl_char = import_node.column - 1
|
|
226
|
+
edits << {
|
|
189
227
|
range: {
|
|
190
|
-
start: { line:
|
|
191
|
-
end:
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
|
|
207
|
-
|
|
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
|
-
|
|
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
|
-
|
|
215
|
-
|
|
251
|
+
def enum_member_rename_changes(uri, token, lsp_line, lsp_char, facts, new_name)
|
|
252
|
+
tokens = @workspace.get_tokens(uri) || []
|
|
216
253
|
|
|
217
|
-
|
|
218
|
-
|
|
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
|
-
|
|
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
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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
|
-
|
|
237
|
-
|
|
238
|
-
|
|
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
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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
|
-
|
|
260
|
-
|
|
261
|
-
return nil unless cursor_is_enum_member
|
|
278
|
+
changes[doc_uri] = edits unless edits.empty?
|
|
279
|
+
end
|
|
262
280
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
-
|
|
276
|
-
|
|
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
|
-
|
|
284
|
-
|
|
285
|
-
|
|
302
|
+
def local_binding_id?(uri, facts, binding_id)
|
|
303
|
+
ast = @workspace.get_ast(uri)
|
|
304
|
+
return false unless ast
|
|
286
305
|
|
|
287
|
-
|
|
288
|
-
|
|
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
|
-
|
|
293
|
-
|
|
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
|
-
|
|
305
|
-
|
|
306
|
-
return false unless ast
|
|
307
|
-
|
|
308
|
-
resolution = facts.binding_resolution
|
|
309
|
-
return false unless resolution
|
|
313
|
+
return true
|
|
314
|
+
end
|
|
310
315
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
next unless resolution.declaration_binding_ids[node.object_id] == binding_id
|
|
316
|
+
false
|
|
317
|
+
end
|
|
314
318
|
|
|
315
|
-
|
|
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
|
-
|
|
319
|
-
|
|
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
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
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
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
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
|
-
|
|
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
|
-
|
|
344
|
-
|
|
345
|
-
return
|
|
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
|
-
|
|
349
|
-
|
|
353
|
+
resolution = facts.binding_resolution
|
|
354
|
+
return [] unless resolution
|
|
350
355
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
return [] unless ast
|
|
356
|
+
ranges = []
|
|
357
|
+
seen = Set.new
|
|
354
358
|
|
|
355
|
-
|
|
356
|
-
|
|
359
|
+
each_ast_node(ast) do |node|
|
|
360
|
+
range = nil
|
|
357
361
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
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
|
-
|
|
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
|
-
|
|
370
|
+
key = [range[:line], range[:column], range[:length]]
|
|
371
|
+
next if seen.include?(key)
|
|
371
372
|
|
|
372
|
-
|
|
373
|
-
|
|
373
|
+
seen << key
|
|
374
|
+
ranges << range
|
|
375
|
+
end
|
|
374
376
|
|
|
375
|
-
|
|
376
|
-
ranges << range
|
|
377
|
+
ranges
|
|
377
378
|
end
|
|
378
379
|
|
|
379
|
-
|
|
380
|
-
|
|
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
|
-
|
|
383
|
-
|
|
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
|
-
|
|
390
|
+
nil
|
|
390
391
|
end
|
|
391
392
|
|
|
392
|
-
|
|
393
|
-
|
|
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
|
-
|
|
396
|
-
|
|
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
|
-
|
|
404
|
+
nil
|
|
404
405
|
end
|
|
405
406
|
|
|
406
|
-
|
|
407
|
-
|
|
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
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
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
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
-
|
|
426
|
-
|
|
423
|
+
ast_name_range(node.name, node.line, node.column)
|
|
424
|
+
end
|
|
427
425
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
426
|
+
def ast_name_range(name, line, column)
|
|
427
|
+
{ name: name, line: line, column: column, length: name.length }
|
|
428
|
+
end
|
|
431
429
|
|
|
432
|
-
|
|
433
|
-
|
|
430
|
+
def each_ast_node(node, &block)
|
|
431
|
+
return if node.nil?
|
|
434
432
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
433
|
+
if node.is_a?(Array)
|
|
434
|
+
node.each { |item| each_ast_node(item, &block) }
|
|
435
|
+
return
|
|
436
|
+
end
|
|
439
437
|
|
|
440
|
-
|
|
441
|
-
|
|
438
|
+
class_name = node.class.name
|
|
439
|
+
return unless class_name && class_name.start_with?('MilkTea::AST::')
|
|
442
440
|
|
|
443
|
-
|
|
441
|
+
yield node
|
|
444
442
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
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
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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
|
-
|
|
476
|
-
|
|
477
|
-
|
|
473
|
+
return nil if changes.empty?
|
|
474
|
+
changes
|
|
475
|
+
end
|
|
478
476
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
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
|
-
|
|
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
|
-
|
|
492
|
-
|
|
493
|
-
|
|
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
|
-
|
|
493
|
+
return true if index > 0 && tokens[index - 1].type == :dot
|
|
496
494
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
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
|
-
|
|
504
|
-
|
|
504
|
+
def struct_field_edit_context?(tokens, index, _struct_type)
|
|
505
|
+
return true if index > 0 && tokens[index - 1].type == :dot
|
|
505
506
|
|
|
506
|
-
|
|
507
|
-
|
|
507
|
+
next_nt = skip_trivia_forward(tokens, index + 1)
|
|
508
|
+
return true if next_nt && tokens[next_nt]&.type == :colon
|
|
508
509
|
|
|
509
|
-
|
|
510
|
-
|
|
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
|
-
|
|
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
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
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
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
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
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
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
|
-
|
|
550
|
-
|
|
547
|
+
receiver_type = method_info[:receiver_type]
|
|
548
|
+
return nil unless receiver_type
|
|
551
549
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
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
|
-
|
|
560
|
-
|
|
561
|
-
|
|
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
|
-
|
|
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
|
-
|
|
567
|
+
return nil if changes.empty?
|
|
568
|
+
changes
|
|
567
569
|
end
|
|
568
570
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
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
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
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
|
-
|
|
583
|
-
|
|
580
|
+
type_name = find_extending_type_for_method(ast, node)
|
|
581
|
+
next unless type_name
|
|
584
582
|
|
|
585
|
-
|
|
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
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
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
|