rfmt 1.7.0-x86_64-linux → 2.0.0.beta1-x86_64-linux
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/CHANGELOG.md +25 -0
- data/README.md +32 -28
- data/ext/rfmt/tests/fixtures/parity/comments_mixed.rb +9 -0
- data/ext/rfmt/tests/fixtures/parity/constructs.rb +50 -0
- data/ext/rfmt/tests/fixtures/parity/embdoc.rb +12 -0
- data/ext/rfmt/tests/fixtures/parity/heredoc_assign.rb +15 -0
- data/ext/rfmt/tests/fixtures/parity/heredoc_call_args.rb +17 -0
- data/ext/rfmt/tests/fixtures/parity/metadata_classes.rb +30 -0
- data/ext/rfmt/tests/fixtures/parity/metadata_conditionals.rb +14 -0
- data/ext/rfmt/tests/fixtures/parity/metadata_defs.rb +33 -0
- data/ext/rfmt/tests/fixtures/parity/multibyte.rb +14 -0
- data/ext/rfmt/tests/fixtures/parity/numeric.rb +6 -0
- data/ext/rfmt/tests/fixtures/parity/plain.rb +31 -0
- data/lib/rfmt/3.3/rfmt.so +0 -0
- data/lib/rfmt/3.4/rfmt.so +0 -0
- data/lib/rfmt/cli.rb +37 -7
- data/lib/rfmt/configuration.rb +2 -20
- data/lib/rfmt/version.rb +1 -1
- data/lib/rfmt.rb +47 -19
- metadata +13 -18
- data/lib/rfmt/prism_bridge.rb +0 -529
- data/lib/rfmt/prism_node_extractor.rb +0 -115
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rfmt
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0.beta1
|
|
5
5
|
platform: x86_64-linux
|
|
6
6
|
authors:
|
|
7
7
|
- fujitani sora
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-07-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: diff-lcs
|
|
@@ -66,20 +66,6 @@ dependencies:
|
|
|
66
66
|
- - "~>"
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
68
|
version: '1.24'
|
|
69
|
-
- !ruby/object:Gem::Dependency
|
|
70
|
-
name: prism
|
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
|
72
|
-
requirements:
|
|
73
|
-
- - "~>"
|
|
74
|
-
- !ruby/object:Gem::Version
|
|
75
|
-
version: '1.6'
|
|
76
|
-
type: :runtime
|
|
77
|
-
prerelease: false
|
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
-
requirements:
|
|
80
|
-
- - "~>"
|
|
81
|
-
- !ruby/object:Gem::Version
|
|
82
|
-
version: '1.6'
|
|
83
69
|
- !ruby/object:Gem::Dependency
|
|
84
70
|
name: thor
|
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -109,6 +95,17 @@ files:
|
|
|
109
95
|
- exe/rfmt
|
|
110
96
|
- exe/rfmt-lsp
|
|
111
97
|
- exe/rfmt_fast
|
|
98
|
+
- ext/rfmt/tests/fixtures/parity/comments_mixed.rb
|
|
99
|
+
- ext/rfmt/tests/fixtures/parity/constructs.rb
|
|
100
|
+
- ext/rfmt/tests/fixtures/parity/embdoc.rb
|
|
101
|
+
- ext/rfmt/tests/fixtures/parity/heredoc_assign.rb
|
|
102
|
+
- ext/rfmt/tests/fixtures/parity/heredoc_call_args.rb
|
|
103
|
+
- ext/rfmt/tests/fixtures/parity/metadata_classes.rb
|
|
104
|
+
- ext/rfmt/tests/fixtures/parity/metadata_conditionals.rb
|
|
105
|
+
- ext/rfmt/tests/fixtures/parity/metadata_defs.rb
|
|
106
|
+
- ext/rfmt/tests/fixtures/parity/multibyte.rb
|
|
107
|
+
- ext/rfmt/tests/fixtures/parity/numeric.rb
|
|
108
|
+
- ext/rfmt/tests/fixtures/parity/plain.rb
|
|
112
109
|
- lib/rfmt.rb
|
|
113
110
|
- lib/rfmt/3.3/rfmt.so
|
|
114
111
|
- lib/rfmt/3.4/rfmt.so
|
|
@@ -123,8 +120,6 @@ files:
|
|
|
123
120
|
- lib/rfmt/lsp/uri.rb
|
|
124
121
|
- lib/rfmt/lsp/workspace.rb
|
|
125
122
|
- lib/rfmt/native_extension_loader.rb
|
|
126
|
-
- lib/rfmt/prism_bridge.rb
|
|
127
|
-
- lib/rfmt/prism_node_extractor.rb
|
|
128
123
|
- lib/rfmt/version.rb
|
|
129
124
|
- lib/ruby_lsp/rfmt/addon.rb
|
|
130
125
|
- lib/ruby_lsp/rfmt/formatter_runner.rb
|
data/lib/rfmt/prism_bridge.rb
DELETED
|
@@ -1,529 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'prism'
|
|
4
|
-
require 'json'
|
|
5
|
-
require_relative 'prism_node_extractor'
|
|
6
|
-
|
|
7
|
-
module Rfmt
|
|
8
|
-
# PrismBridge provides the Ruby-side integration with the Prism parser
|
|
9
|
-
# It parses Ruby source code and converts the AST to a JSON format
|
|
10
|
-
# that can be consumed by the Rust formatter
|
|
11
|
-
class PrismBridge
|
|
12
|
-
extend PrismNodeExtractor
|
|
13
|
-
|
|
14
|
-
class ParseError < StandardError; end
|
|
15
|
-
|
|
16
|
-
# Parse Ruby source code and return serialized AST
|
|
17
|
-
# @param source [String] Ruby source code to parse
|
|
18
|
-
# @return [String] JSON-serialized AST with comments
|
|
19
|
-
# @raise [ParseError] if parsing fails
|
|
20
|
-
def self.parse(source)
|
|
21
|
-
result = Prism.parse(source)
|
|
22
|
-
|
|
23
|
-
handle_parse_errors(result) if result.failure?
|
|
24
|
-
|
|
25
|
-
serialize_ast_with_comments(result)
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Parse Ruby source code from a file
|
|
29
|
-
# @param file_path [String] Path to Ruby file
|
|
30
|
-
# @return [String] JSON-serialized AST
|
|
31
|
-
# @raise [ParseError] if parsing fails
|
|
32
|
-
# @raise [Errno::ENOENT] if file doesn't exist
|
|
33
|
-
def self.parse_file(file_path)
|
|
34
|
-
source = File.read(file_path)
|
|
35
|
-
parse(source)
|
|
36
|
-
rescue Errno::ENOENT
|
|
37
|
-
raise ParseError, "File not found: #{file_path}"
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# Handle parsing errors from Prism
|
|
41
|
-
def self.handle_parse_errors(result)
|
|
42
|
-
errors = result.errors.map do |error|
|
|
43
|
-
{
|
|
44
|
-
line: error.location.start_line,
|
|
45
|
-
column: error.location.start_column,
|
|
46
|
-
message: error.message
|
|
47
|
-
}
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
error_messages = errors.map do |err|
|
|
51
|
-
"#{err[:line]}:#{err[:column]}: #{err[:message]}"
|
|
52
|
-
end.join("\n")
|
|
53
|
-
|
|
54
|
-
raise ParseError, "Parse errors:\n#{error_messages}"
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Serialize the Prism AST to JSON
|
|
58
|
-
def self.serialize_ast(node)
|
|
59
|
-
JSON.generate(convert_node(node), max_nesting: false)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# Serialize the Prism AST with comments to JSON
|
|
63
|
-
def self.serialize_ast_with_comments(result)
|
|
64
|
-
comments = result.comments.map do |comment|
|
|
65
|
-
loc = comment.location
|
|
66
|
-
end_line = loc.end_line
|
|
67
|
-
# Prism's `=begin ... =end` (EmbDocComment) location ends at
|
|
68
|
-
# `<=end_line>:0`, i.e. the column-0 start of the line AFTER the
|
|
69
|
-
# `=end` terminator. Reporting that larger end_line makes the
|
|
70
|
-
# comment appear to overlap with the next statement, so the
|
|
71
|
-
# comment gets misattributed to a deeper node (e.g. the first
|
|
72
|
-
# expression inside the next method body) and ends up emitted in
|
|
73
|
-
# the wrong place. Snap it back to the terminator line.
|
|
74
|
-
end_line = loc.end_line - 1 if loc.end_column.zero? && loc.end_line > loc.start_line
|
|
75
|
-
|
|
76
|
-
{
|
|
77
|
-
comment_type: comment.class.name.split('::').last.downcase.gsub('comment', ''),
|
|
78
|
-
location: {
|
|
79
|
-
start_line: loc.start_line,
|
|
80
|
-
start_column: loc.start_column,
|
|
81
|
-
end_line: end_line,
|
|
82
|
-
end_column: loc.end_column,
|
|
83
|
-
start_offset: loc.start_offset,
|
|
84
|
-
end_offset: loc.end_offset
|
|
85
|
-
},
|
|
86
|
-
text: loc.slice,
|
|
87
|
-
position: 'leading' # Default position, will be refined by Rust
|
|
88
|
-
}
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
JSON.generate({
|
|
92
|
-
ast: convert_node(result.value),
|
|
93
|
-
comments: comments
|
|
94
|
-
}, max_nesting: false)
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# Convert a Prism node to our internal representation
|
|
98
|
-
def self.convert_node(node)
|
|
99
|
-
return nil if node.nil?
|
|
100
|
-
|
|
101
|
-
{
|
|
102
|
-
node_type: node_type_name(node),
|
|
103
|
-
location: extract_location(node),
|
|
104
|
-
children: extract_children(node),
|
|
105
|
-
metadata: extract_metadata(node),
|
|
106
|
-
comments: extract_comments(node),
|
|
107
|
-
formatting: extract_formatting(node)
|
|
108
|
-
}
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
# Get the node type name from Prism node
|
|
112
|
-
def self.node_type_name(node)
|
|
113
|
-
# Prism node class names are like "Prism::ProgramNode"
|
|
114
|
-
# We want just "program_node" in snake_case
|
|
115
|
-
node.class.name.split('::').last.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
116
|
-
.gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
# Extract location information from node
|
|
120
|
-
def self.extract_location(node)
|
|
121
|
-
loc = node.location
|
|
122
|
-
|
|
123
|
-
# For heredoc nodes, the location only covers the opening tag (<<~CSV)
|
|
124
|
-
# We need to find the maximum end_offset including closing_loc
|
|
125
|
-
end_offset = loc.end_offset
|
|
126
|
-
end_line = loc.end_line
|
|
127
|
-
end_column = loc.end_column
|
|
128
|
-
|
|
129
|
-
# Check this node's closing_loc
|
|
130
|
-
if node.respond_to?(:closing_loc) && node.closing_loc
|
|
131
|
-
closing = node.closing_loc
|
|
132
|
-
if closing.end_offset > end_offset
|
|
133
|
-
end_offset = closing.end_offset
|
|
134
|
-
end_line = heredoc_terminator_line(closing)
|
|
135
|
-
end_column = closing.end_column
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
# Recursively check all descendant nodes for heredoc closing_loc
|
|
140
|
-
# Issue #74: handled direct children (e.g., LocalVariableWriteNode -> StringNode)
|
|
141
|
-
# Issue #86: handles deeper nesting (e.g., CallNode -> ArgumentsNode -> StringNode)
|
|
142
|
-
max_closing = find_max_closing_loc_recursive(node)
|
|
143
|
-
if max_closing && max_closing[:end_offset] > end_offset
|
|
144
|
-
end_offset = max_closing[:end_offset]
|
|
145
|
-
end_line = max_closing[:end_line]
|
|
146
|
-
end_column = max_closing[:end_column]
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
{
|
|
150
|
-
start_line: loc.start_line,
|
|
151
|
-
start_column: loc.start_column,
|
|
152
|
-
end_line: end_line,
|
|
153
|
-
end_column: end_column,
|
|
154
|
-
start_offset: loc.start_offset,
|
|
155
|
-
end_offset: end_offset
|
|
156
|
-
}
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
# Prism reports a heredoc's `closing_loc` as `<terminator_line>:0..(terminator_line+1):0`,
|
|
160
|
-
# i.e. its `end_line` is the LINE AFTER the terminator. Using that as the
|
|
161
|
-
# node's `end_line` makes blank-line preservation fail: a real blank line
|
|
162
|
-
# right after the terminator looks identical (diff == 1) to two adjacent
|
|
163
|
-
# statements with no separator. Use the terminator's own line instead.
|
|
164
|
-
def self.heredoc_terminator_line(closing_loc)
|
|
165
|
-
if closing_loc.end_column.zero? && closing_loc.end_line > closing_loc.start_line
|
|
166
|
-
closing_loc.start_line
|
|
167
|
-
else
|
|
168
|
-
closing_loc.end_line
|
|
169
|
-
end
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
# Recursively find the maximum closing_loc among all descendant nodes
|
|
173
|
-
# Returns nil if no closing_loc found, otherwise { end_offset:, end_line:, end_column: }
|
|
174
|
-
def self.find_max_closing_loc_recursive(node, depth: 0)
|
|
175
|
-
return nil if depth > 10
|
|
176
|
-
|
|
177
|
-
max_closing = nil
|
|
178
|
-
|
|
179
|
-
node.child_nodes.compact.each do |child|
|
|
180
|
-
if child.respond_to?(:closing_loc) && child.closing_loc
|
|
181
|
-
closing = child.closing_loc
|
|
182
|
-
if max_closing.nil? || closing.end_offset > max_closing[:end_offset]
|
|
183
|
-
max_closing = {
|
|
184
|
-
end_offset: closing.end_offset,
|
|
185
|
-
end_line: heredoc_terminator_line(closing),
|
|
186
|
-
end_column: closing.end_column
|
|
187
|
-
}
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
child_max = find_max_closing_loc_recursive(child, depth: depth + 1)
|
|
192
|
-
max_closing = child_max if child_max && (max_closing.nil? || child_max[:end_offset] > max_closing[:end_offset])
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
max_closing
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
# Extract child nodes
|
|
199
|
-
def self.extract_children(node)
|
|
200
|
-
children = []
|
|
201
|
-
|
|
202
|
-
begin
|
|
203
|
-
# Different node types have different child accessors
|
|
204
|
-
children = case node
|
|
205
|
-
when Prism::ProgramNode
|
|
206
|
-
node.statements ? node.statements.body : []
|
|
207
|
-
when Prism::StatementsNode
|
|
208
|
-
node.body || []
|
|
209
|
-
when Prism::ClassNode
|
|
210
|
-
[
|
|
211
|
-
node.constant_path,
|
|
212
|
-
node.superclass,
|
|
213
|
-
node.body
|
|
214
|
-
].compact
|
|
215
|
-
when Prism::ModuleNode
|
|
216
|
-
[
|
|
217
|
-
node.constant_path,
|
|
218
|
-
node.body
|
|
219
|
-
].compact
|
|
220
|
-
when Prism::DefNode
|
|
221
|
-
params = if node.parameters
|
|
222
|
-
node.parameters.child_nodes.compact
|
|
223
|
-
else
|
|
224
|
-
[]
|
|
225
|
-
end
|
|
226
|
-
params + [node.body].compact
|
|
227
|
-
when Prism::CallNode
|
|
228
|
-
result = []
|
|
229
|
-
result << node.receiver if node.receiver
|
|
230
|
-
result.concat(node.arguments.child_nodes.compact) if node.arguments
|
|
231
|
-
result << node.block if node.block
|
|
232
|
-
result
|
|
233
|
-
when Prism::IfNode, Prism::UnlessNode
|
|
234
|
-
[
|
|
235
|
-
node.predicate,
|
|
236
|
-
node.statements,
|
|
237
|
-
node.consequent
|
|
238
|
-
].compact
|
|
239
|
-
when Prism::ElseNode
|
|
240
|
-
[node.statements].compact
|
|
241
|
-
when Prism::ArrayNode
|
|
242
|
-
node.elements || []
|
|
243
|
-
when Prism::HashNode
|
|
244
|
-
node.elements || []
|
|
245
|
-
when Prism::BlockNode
|
|
246
|
-
params = if node.parameters
|
|
247
|
-
node.parameters.child_nodes.compact
|
|
248
|
-
else
|
|
249
|
-
[]
|
|
250
|
-
end
|
|
251
|
-
params + [node.body].compact
|
|
252
|
-
when Prism::BeginNode
|
|
253
|
-
[
|
|
254
|
-
node.statements,
|
|
255
|
-
node.rescue_clause,
|
|
256
|
-
node.else_clause,
|
|
257
|
-
node.ensure_clause
|
|
258
|
-
].compact
|
|
259
|
-
when Prism::EnsureNode
|
|
260
|
-
[node.statements].compact
|
|
261
|
-
when Prism::LambdaNode
|
|
262
|
-
params = if node.parameters
|
|
263
|
-
node.parameters.child_nodes.compact
|
|
264
|
-
else
|
|
265
|
-
[]
|
|
266
|
-
end
|
|
267
|
-
params + [node.body].compact
|
|
268
|
-
when Prism::RescueNode
|
|
269
|
-
result = []
|
|
270
|
-
result.concat(node.exceptions) if node.exceptions
|
|
271
|
-
result << node.reference if node.reference
|
|
272
|
-
result << node.statements if node.statements
|
|
273
|
-
result << node.subsequent if node.subsequent
|
|
274
|
-
result
|
|
275
|
-
when Prism::SymbolNode, Prism::LocalVariableReadNode, Prism::InstanceVariableReadNode
|
|
276
|
-
[]
|
|
277
|
-
when Prism::LocalVariableWriteNode, Prism::InstanceVariableWriteNode
|
|
278
|
-
[node.value].compact
|
|
279
|
-
when Prism::ReturnNode
|
|
280
|
-
node.arguments ? node.arguments.child_nodes.compact : []
|
|
281
|
-
when Prism::OrNode
|
|
282
|
-
[node.left, node.right].compact
|
|
283
|
-
when Prism::AssocNode
|
|
284
|
-
[node.key, node.value].compact
|
|
285
|
-
when Prism::KeywordHashNode
|
|
286
|
-
node.elements || []
|
|
287
|
-
when Prism::InterpolatedStringNode
|
|
288
|
-
node.parts || []
|
|
289
|
-
when Prism::EmbeddedStatementsNode
|
|
290
|
-
[node.statements].compact
|
|
291
|
-
when Prism::CaseNode
|
|
292
|
-
[node.predicate, *node.conditions, node.else_clause].compact
|
|
293
|
-
when Prism::WhenNode
|
|
294
|
-
[*node.conditions, node.statements].compact
|
|
295
|
-
when Prism::WhileNode, Prism::UntilNode
|
|
296
|
-
[node.predicate, node.statements].compact
|
|
297
|
-
when Prism::ForNode
|
|
298
|
-
[node.index, node.collection, node.statements].compact
|
|
299
|
-
when Prism::BreakNode, Prism::NextNode
|
|
300
|
-
node.arguments ? node.arguments.child_nodes.compact : []
|
|
301
|
-
when Prism::RedoNode, Prism::RetryNode
|
|
302
|
-
[]
|
|
303
|
-
when Prism::YieldNode
|
|
304
|
-
node.arguments ? node.arguments.child_nodes.compact : []
|
|
305
|
-
when Prism::SuperNode
|
|
306
|
-
result = []
|
|
307
|
-
result.concat(node.arguments.child_nodes.compact) if node.arguments
|
|
308
|
-
result << node.block if node.block
|
|
309
|
-
result
|
|
310
|
-
when Prism::ForwardingSuperNode
|
|
311
|
-
node.block ? [node.block] : []
|
|
312
|
-
when Prism::RescueModifierNode
|
|
313
|
-
[node.expression, node.rescue_expression].compact
|
|
314
|
-
when Prism::RangeNode
|
|
315
|
-
[node.left, node.right].compact
|
|
316
|
-
when Prism::RegularExpressionNode
|
|
317
|
-
[]
|
|
318
|
-
when Prism::SplatNode
|
|
319
|
-
[node.expression].compact
|
|
320
|
-
when Prism::AndNode
|
|
321
|
-
[node.left, node.right].compact
|
|
322
|
-
when Prism::InterpolatedRegularExpressionNode, Prism::InterpolatedSymbolNode,
|
|
323
|
-
Prism::InterpolatedXStringNode
|
|
324
|
-
node.parts || []
|
|
325
|
-
when Prism::XStringNode
|
|
326
|
-
[]
|
|
327
|
-
when Prism::ClassVariableReadNode, Prism::GlobalVariableReadNode, Prism::SelfNode
|
|
328
|
-
[]
|
|
329
|
-
when Prism::ClassVariableWriteNode, Prism::GlobalVariableWriteNode
|
|
330
|
-
[node.value].compact
|
|
331
|
-
when Prism::ClassVariableOrWriteNode, Prism::ClassVariableAndWriteNode,
|
|
332
|
-
Prism::GlobalVariableOrWriteNode, Prism::GlobalVariableAndWriteNode,
|
|
333
|
-
Prism::LocalVariableOrWriteNode, Prism::LocalVariableAndWriteNode,
|
|
334
|
-
Prism::InstanceVariableOrWriteNode, Prism::InstanceVariableAndWriteNode,
|
|
335
|
-
Prism::ConstantOrWriteNode, Prism::ConstantAndWriteNode
|
|
336
|
-
[node.value].compact
|
|
337
|
-
when Prism::ClassVariableOperatorWriteNode, Prism::GlobalVariableOperatorWriteNode,
|
|
338
|
-
Prism::LocalVariableOperatorWriteNode, Prism::InstanceVariableOperatorWriteNode,
|
|
339
|
-
Prism::ConstantOperatorWriteNode
|
|
340
|
-
[node.value].compact
|
|
341
|
-
when Prism::ConstantPathOrWriteNode, Prism::ConstantPathAndWriteNode,
|
|
342
|
-
Prism::ConstantPathOperatorWriteNode
|
|
343
|
-
[node.target, node.value].compact
|
|
344
|
-
when Prism::ConstantPathWriteNode
|
|
345
|
-
[node.target, node.value].compact
|
|
346
|
-
when Prism::CaseMatchNode
|
|
347
|
-
[node.predicate, *node.conditions, node.else_clause].compact
|
|
348
|
-
when Prism::InNode
|
|
349
|
-
[node.pattern, node.statements].compact
|
|
350
|
-
when Prism::MatchPredicateNode, Prism::MatchRequiredNode
|
|
351
|
-
[node.value, node.pattern].compact
|
|
352
|
-
when Prism::ParenthesesNode
|
|
353
|
-
[node.body].compact
|
|
354
|
-
when Prism::DefinedNode
|
|
355
|
-
[node.value].compact
|
|
356
|
-
when Prism::SingletonClassNode
|
|
357
|
-
[node.expression, node.body].compact
|
|
358
|
-
when Prism::AliasMethodNode
|
|
359
|
-
[node.new_name, node.old_name].compact
|
|
360
|
-
when Prism::AliasGlobalVariableNode
|
|
361
|
-
[node.new_name, node.old_name].compact
|
|
362
|
-
when Prism::UndefNode
|
|
363
|
-
node.names || []
|
|
364
|
-
when Prism::AssocSplatNode
|
|
365
|
-
[node.value].compact
|
|
366
|
-
when Prism::BlockArgumentNode
|
|
367
|
-
[node.expression].compact
|
|
368
|
-
when Prism::MultiWriteNode
|
|
369
|
-
[*node.lefts, node.rest, *node.rights, node.value].compact
|
|
370
|
-
when Prism::MultiTargetNode
|
|
371
|
-
[*node.lefts, node.rest, *node.rights].compact
|
|
372
|
-
when Prism::SourceFileNode, Prism::SourceLineNode, Prism::SourceEncodingNode
|
|
373
|
-
[]
|
|
374
|
-
when Prism::PreExecutionNode, Prism::PostExecutionNode
|
|
375
|
-
[node.statements].compact
|
|
376
|
-
# Numeric literals
|
|
377
|
-
when Prism::RationalNode, Prism::ImaginaryNode
|
|
378
|
-
[node.numeric].compact
|
|
379
|
-
# String interpolation
|
|
380
|
-
when Prism::EmbeddedVariableNode
|
|
381
|
-
[node.variable].compact
|
|
382
|
-
# Pattern matching patterns
|
|
383
|
-
when Prism::ArrayPatternNode
|
|
384
|
-
[*node.requireds, node.rest, *node.posts].compact
|
|
385
|
-
when Prism::HashPatternNode
|
|
386
|
-
[*node.elements, node.rest].compact
|
|
387
|
-
when Prism::FindPatternNode
|
|
388
|
-
[node.left, *node.requireds, node.right].compact
|
|
389
|
-
when Prism::CapturePatternNode
|
|
390
|
-
[node.value, node.target].compact
|
|
391
|
-
when Prism::AlternationPatternNode
|
|
392
|
-
[node.left, node.right].compact
|
|
393
|
-
when Prism::PinnedExpressionNode
|
|
394
|
-
[node.expression].compact
|
|
395
|
-
when Prism::PinnedVariableNode
|
|
396
|
-
[node.variable].compact
|
|
397
|
-
# Forwarding and special parameters
|
|
398
|
-
when Prism::ForwardingArgumentsNode, Prism::ForwardingParameterNode,
|
|
399
|
-
Prism::NoKeywordsParameterNode
|
|
400
|
-
[]
|
|
401
|
-
# References
|
|
402
|
-
when Prism::BackReferenceReadNode, Prism::NumberedReferenceReadNode
|
|
403
|
-
[]
|
|
404
|
-
# Call/Index compound assignment
|
|
405
|
-
when Prism::CallAndWriteNode, Prism::CallOrWriteNode, Prism::CallOperatorWriteNode
|
|
406
|
-
[node.receiver, node.value].compact
|
|
407
|
-
when Prism::IndexAndWriteNode, Prism::IndexOrWriteNode, Prism::IndexOperatorWriteNode
|
|
408
|
-
[node.receiver, node.arguments, node.value].compact
|
|
409
|
-
# Match
|
|
410
|
-
when Prism::MatchWriteNode
|
|
411
|
-
[node.call, *node.targets].compact
|
|
412
|
-
when Prism::MatchLastLineNode, Prism::InterpolatedMatchLastLineNode
|
|
413
|
-
[]
|
|
414
|
-
# Other
|
|
415
|
-
when Prism::FlipFlopNode
|
|
416
|
-
[node.left, node.right].compact
|
|
417
|
-
when Prism::ImplicitNode
|
|
418
|
-
[node.value].compact
|
|
419
|
-
when Prism::ImplicitRestNode
|
|
420
|
-
[]
|
|
421
|
-
else
|
|
422
|
-
# For unknown types, try to get child nodes if they exist
|
|
423
|
-
[]
|
|
424
|
-
end
|
|
425
|
-
rescue StandardError => e
|
|
426
|
-
# Log warning in debug mode but continue processing
|
|
427
|
-
warn "Warning: Failed to extract children from #{node.class}: #{e.message}" if $DEBUG
|
|
428
|
-
children = []
|
|
429
|
-
end
|
|
430
|
-
|
|
431
|
-
children.compact.map { |child| convert_node(child) }
|
|
432
|
-
end
|
|
433
|
-
|
|
434
|
-
# Extract metadata specific to node type
|
|
435
|
-
def self.extract_metadata(node)
|
|
436
|
-
metadata = {}
|
|
437
|
-
|
|
438
|
-
case node
|
|
439
|
-
when Prism::ClassNode
|
|
440
|
-
if (name = extract_class_or_module_name(node))
|
|
441
|
-
metadata['name'] = name
|
|
442
|
-
end
|
|
443
|
-
if (superclass = extract_superclass_name(node))
|
|
444
|
-
metadata['superclass'] = superclass
|
|
445
|
-
end
|
|
446
|
-
when Prism::ModuleNode
|
|
447
|
-
if (name = extract_class_or_module_name(node))
|
|
448
|
-
metadata['name'] = name
|
|
449
|
-
end
|
|
450
|
-
when Prism::DefNode
|
|
451
|
-
# Prefer `name_loc.slice` over `name.to_s` so unary operator suffixes
|
|
452
|
-
# (`def !@`, `def +@`, `def -@`) survive the round-trip. Prism
|
|
453
|
-
# normalizes `name` to the symbol with the `@` stripped for `!@`,
|
|
454
|
-
# which would otherwise rewrite `def !@` to `def !` silently.
|
|
455
|
-
name = if node.respond_to?(:name_loc) && node.name_loc
|
|
456
|
-
node.name_loc.slice
|
|
457
|
-
else
|
|
458
|
-
extract_node_name(node)
|
|
459
|
-
end
|
|
460
|
-
metadata['name'] = name if name
|
|
461
|
-
metadata['parameters_count'] = extract_parameter_count(node).to_s
|
|
462
|
-
# Extract parameters text directly from source
|
|
463
|
-
if node.parameters
|
|
464
|
-
metadata['parameters_text'] = node.parameters.location.slice
|
|
465
|
-
metadata['has_parens'] = (!node.lparen_loc.nil?).to_s
|
|
466
|
-
end
|
|
467
|
-
# Check if this is a class method (def self.method_name)
|
|
468
|
-
if node.respond_to?(:receiver) && node.receiver
|
|
469
|
-
receiver = node.receiver
|
|
470
|
-
if receiver.is_a?(Prism::SelfNode)
|
|
471
|
-
metadata['receiver'] = 'self'
|
|
472
|
-
elsif receiver.respond_to?(:slice)
|
|
473
|
-
metadata['receiver'] = receiver.slice
|
|
474
|
-
end
|
|
475
|
-
end
|
|
476
|
-
when Prism::CallNode
|
|
477
|
-
if (name = extract_node_name(node))
|
|
478
|
-
metadata['name'] = name
|
|
479
|
-
end
|
|
480
|
-
if (message = extract_message_name(node))
|
|
481
|
-
metadata['message'] = message
|
|
482
|
-
end
|
|
483
|
-
when Prism::StringNode
|
|
484
|
-
if (content = extract_string_content(node))
|
|
485
|
-
metadata['content'] = content
|
|
486
|
-
end
|
|
487
|
-
when Prism::IntegerNode
|
|
488
|
-
if (value = extract_literal_value(node))
|
|
489
|
-
metadata['value'] = value
|
|
490
|
-
end
|
|
491
|
-
when Prism::FloatNode
|
|
492
|
-
if (value = extract_literal_value(node))
|
|
493
|
-
metadata['value'] = value
|
|
494
|
-
end
|
|
495
|
-
when Prism::SymbolNode
|
|
496
|
-
if (value = extract_literal_value(node))
|
|
497
|
-
metadata['value'] = value
|
|
498
|
-
end
|
|
499
|
-
when Prism::LocalVariableWriteNode, Prism::InstanceVariableWriteNode
|
|
500
|
-
metadata['name'] = node.name.to_s
|
|
501
|
-
when Prism::IfNode, Prism::UnlessNode
|
|
502
|
-
# Detect ternary operator: if_keyword_loc is nil for ternary
|
|
503
|
-
metadata['is_ternary'] = node.if_keyword_loc.nil?.to_s if node.respond_to?(:if_keyword_loc)
|
|
504
|
-
end
|
|
505
|
-
|
|
506
|
-
metadata
|
|
507
|
-
end
|
|
508
|
-
|
|
509
|
-
# Extract comments associated with the node
|
|
510
|
-
def self.extract_comments(_node)
|
|
511
|
-
# Prism attaches comments to the parse result, not individual nodes
|
|
512
|
-
# For Phase 1, we'll return empty array and implement in Phase 2
|
|
513
|
-
[]
|
|
514
|
-
end
|
|
515
|
-
|
|
516
|
-
# Extract formatting information
|
|
517
|
-
def self.extract_formatting(node)
|
|
518
|
-
loc = node.location
|
|
519
|
-
{
|
|
520
|
-
indent_level: 0, # Will be calculated during formatting
|
|
521
|
-
needs_blank_line_before: false,
|
|
522
|
-
needs_blank_line_after: false,
|
|
523
|
-
preserve_newlines: false,
|
|
524
|
-
multiline: loc.start_line != loc.end_line,
|
|
525
|
-
original_formatting: nil # Can store original text if needed
|
|
526
|
-
}
|
|
527
|
-
end
|
|
528
|
-
end
|
|
529
|
-
end
|