rfmt 1.5.0 → 1.5.2
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 +27 -0
- data/Cargo.lock +1 -1
- data/README.md +1 -0
- data/ext/rfmt/Cargo.toml +1 -1
- data/ext/rfmt/src/emitter/mod.rs +57 -0
- data/ext/rfmt/src/parser/prism_adapter.rs +1 -1
- data/lib/rfmt/prism_bridge.rb +36 -10
- data/lib/rfmt/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b17e8b19308af7368b77ac8b7a89bab1b7c321e16de03f1aae0b8d3df27f678c
|
|
4
|
+
data.tar.gz: 9929e63f33a0e116ce70441e86d6c525157517b37e2f83f4e9cb931d53eab8af
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f2c375826007f03f24163fa83b1b51dffa3ef241ad4e38c800145b8387b108368807cc221b4d453860861b2dea8fcfbea33ad7391b61c24f2d973da004078aaa
|
|
7
|
+
data.tar.gz: 7305b6778b6dcecb116a7e9df522d5085eee28f763fa88283ecc70a1a0d71e70eace61e7be736eb010a8cf8aacf556b34a9226d05cf83f9216c3e385d43ce720
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.5.2] - 2026-02-21
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Instance variable write node emission support (#92)
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- Nix dev environment optimization (build caching, direnv support, devShell splitting)
|
|
10
|
+
- Code formatting improvements (clippy, rustfmt)
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- Fix Nix bundler version conflict (remove pkgs.bundler, use Ruby built-in)
|
|
14
|
+
|
|
15
|
+
## [1.5.1] - 2026-02-21
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- Fix inline modifier `if`/`unless` formatting (#87)
|
|
19
|
+
- Fix heredoc command incorrectly removed (#90, #86)
|
|
20
|
+
- Fix method chain command dependency handling (#89, #85)
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- Heredoc comment deletion support
|
|
24
|
+
- Place block loop emission support
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- Update README.md
|
|
28
|
+
- Code formatting improvements (RuboCop compliance)
|
|
29
|
+
|
|
3
30
|
## [1.5.0] - 2026-01-25
|
|
4
31
|
|
|
5
32
|
### Added
|
data/Cargo.lock
CHANGED
data/README.md
CHANGED
|
@@ -23,6 +23,7 @@ A Ruby code formatter written in Rust
|
|
|
23
23
|
## What is rfmt?
|
|
24
24
|
|
|
25
25
|
[RubyGems reference](https://rubygems.org/gems/rfmt)
|
|
26
|
+
[DeepWiki rfmt](https://deepwiki.com/fs0414/rfmt)
|
|
26
27
|
|
|
27
28
|
**rfmt** is a Ruby code formatter that enforces consistent style across your codebase. Key characteristics:
|
|
28
29
|
|
data/ext/rfmt/Cargo.toml
CHANGED
data/ext/rfmt/src/emitter/mod.rs
CHANGED
|
@@ -463,6 +463,9 @@ impl Emitter {
|
|
|
463
463
|
NodeType::SingletonClassNode => self.emit_singleton_class(node, indent_level)?,
|
|
464
464
|
NodeType::CaseMatchNode => self.emit_case_match(node, indent_level)?,
|
|
465
465
|
NodeType::InNode => self.emit_in(node, indent_level)?,
|
|
466
|
+
NodeType::LocalVariableWriteNode | NodeType::InstanceVariableWriteNode => {
|
|
467
|
+
self.emit_variable_write(node, indent_level)?
|
|
468
|
+
}
|
|
466
469
|
_ => self.emit_generic(node, indent_level)?,
|
|
467
470
|
}
|
|
468
471
|
Ok(())
|
|
@@ -1000,6 +1003,7 @@ impl Emitter {
|
|
|
1000
1003
|
self.write_source_text(predicate)?;
|
|
1001
1004
|
}
|
|
1002
1005
|
|
|
1006
|
+
self.emit_trailing_comments(node.location.end_line)?;
|
|
1003
1007
|
return Ok(());
|
|
1004
1008
|
}
|
|
1005
1009
|
|
|
@@ -1035,6 +1039,7 @@ impl Emitter {
|
|
|
1035
1039
|
}
|
|
1036
1040
|
}
|
|
1037
1041
|
|
|
1042
|
+
self.emit_trailing_comments(node.location.end_line)?;
|
|
1038
1043
|
return Ok(());
|
|
1039
1044
|
}
|
|
1040
1045
|
|
|
@@ -1196,6 +1201,16 @@ impl Emitter {
|
|
|
1196
1201
|
if let Some(text) = self.source.get(start..end) {
|
|
1197
1202
|
// Trim trailing whitespace but preserve the content
|
|
1198
1203
|
write!(self.buffer, "{}", text.trim_end())?;
|
|
1204
|
+
|
|
1205
|
+
// Mark comments within the extracted range as emitted
|
|
1206
|
+
for (idx, comment) in self.all_comments.iter().enumerate() {
|
|
1207
|
+
if !self.emitted_comment_indices.contains(&idx)
|
|
1208
|
+
&& comment.location.start_offset >= start
|
|
1209
|
+
&& comment.location.end_offset <= end
|
|
1210
|
+
{
|
|
1211
|
+
self.emitted_comment_indices.insert(idx);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1199
1214
|
}
|
|
1200
1215
|
}
|
|
1201
1216
|
|
|
@@ -1368,6 +1383,48 @@ impl Emitter {
|
|
|
1368
1383
|
Ok(())
|
|
1369
1384
|
}
|
|
1370
1385
|
|
|
1386
|
+
/// Emit variable write node (LocalVariableWriteNode, InstanceVariableWriteNode)
|
|
1387
|
+
/// Handles `x = value` and `@x = value` patterns
|
|
1388
|
+
fn emit_variable_write(&mut self, node: &Node, indent_level: usize) -> Result<()> {
|
|
1389
|
+
self.emit_comments_before(node.location.start_line, indent_level)?;
|
|
1390
|
+
|
|
1391
|
+
let name = node.metadata.get("name").map(|s| s.as_str()).unwrap_or("_");
|
|
1392
|
+
|
|
1393
|
+
// Get value node (first child)
|
|
1394
|
+
let value = match node.children.first() {
|
|
1395
|
+
Some(v) => v,
|
|
1396
|
+
None => {
|
|
1397
|
+
// No value: fallback to source extraction
|
|
1398
|
+
return self.emit_generic(node, indent_level);
|
|
1399
|
+
}
|
|
1400
|
+
};
|
|
1401
|
+
|
|
1402
|
+
let is_block_value = matches!(
|
|
1403
|
+
value.node_type,
|
|
1404
|
+
NodeType::IfNode
|
|
1405
|
+
| NodeType::UnlessNode
|
|
1406
|
+
| NodeType::CaseNode
|
|
1407
|
+
| NodeType::CaseMatchNode
|
|
1408
|
+
| NodeType::BeginNode
|
|
1409
|
+
| NodeType::WhileNode
|
|
1410
|
+
| NodeType::UntilNode
|
|
1411
|
+
| NodeType::ForNode
|
|
1412
|
+
);
|
|
1413
|
+
|
|
1414
|
+
self.emit_indent(indent_level)?;
|
|
1415
|
+
if is_block_value {
|
|
1416
|
+
writeln!(self.buffer, "{} =", name)?;
|
|
1417
|
+
self.emit_node(value, indent_level + 1)?;
|
|
1418
|
+
} else {
|
|
1419
|
+
write!(self.buffer, "{} = ", name)?;
|
|
1420
|
+
self.write_source_text_trimmed(value)?;
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
self.emit_trailing_comments(node.location.end_line)?;
|
|
1424
|
+
|
|
1425
|
+
Ok(())
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1371
1428
|
/// Emit generic node by extracting from source
|
|
1372
1429
|
fn emit_generic(&mut self, node: &Node, indent_level: usize) -> Result<()> {
|
|
1373
1430
|
self.emit_comments_before(node.location.start_line, indent_level)?;
|
|
@@ -368,7 +368,7 @@ mod tests {
|
|
|
368
368
|
|
|
369
369
|
let node = result.unwrap();
|
|
370
370
|
assert_eq!(node.node_type, NodeType::ClassNode);
|
|
371
|
-
|
|
371
|
+
assert!(node.formatting.multiline);
|
|
372
372
|
assert!(node.is_multiline());
|
|
373
373
|
assert_eq!(node.line_count(), 3);
|
|
374
374
|
}
|
data/lib/rfmt/prism_bridge.rb
CHANGED
|
@@ -125,16 +125,14 @@ module Rfmt
|
|
|
125
125
|
end
|
|
126
126
|
end
|
|
127
127
|
|
|
128
|
-
#
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
end_line = closing.end_line
|
|
137
|
-
end_column = closing.end_column
|
|
128
|
+
# Recursively check all descendant nodes for heredoc closing_loc
|
|
129
|
+
# Issue #74: handled direct children (e.g., LocalVariableWriteNode -> StringNode)
|
|
130
|
+
# Issue #86: handles deeper nesting (e.g., CallNode -> ArgumentsNode -> StringNode)
|
|
131
|
+
max_closing = find_max_closing_loc_recursive(node)
|
|
132
|
+
if max_closing && max_closing[:end_offset] > end_offset
|
|
133
|
+
end_offset = max_closing[:end_offset]
|
|
134
|
+
end_line = max_closing[:end_line]
|
|
135
|
+
end_column = max_closing[:end_column]
|
|
138
136
|
end
|
|
139
137
|
|
|
140
138
|
{
|
|
@@ -147,6 +145,32 @@ module Rfmt
|
|
|
147
145
|
}
|
|
148
146
|
end
|
|
149
147
|
|
|
148
|
+
# Recursively find the maximum closing_loc among all descendant nodes
|
|
149
|
+
# Returns nil if no closing_loc found, otherwise { end_offset:, end_line:, end_column: }
|
|
150
|
+
def self.find_max_closing_loc_recursive(node, depth: 0)
|
|
151
|
+
return nil if depth > 10
|
|
152
|
+
|
|
153
|
+
max_closing = nil
|
|
154
|
+
|
|
155
|
+
node.child_nodes.compact.each do |child|
|
|
156
|
+
if child.respond_to?(:closing_loc) && child.closing_loc
|
|
157
|
+
closing = child.closing_loc
|
|
158
|
+
if max_closing.nil? || closing.end_offset > max_closing[:end_offset]
|
|
159
|
+
max_closing = {
|
|
160
|
+
end_offset: closing.end_offset,
|
|
161
|
+
end_line: closing.end_line,
|
|
162
|
+
end_column: closing.end_column
|
|
163
|
+
}
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
child_max = find_max_closing_loc_recursive(child, depth: depth + 1)
|
|
168
|
+
max_closing = child_max if child_max && (max_closing.nil? || child_max[:end_offset] > max_closing[:end_offset])
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
max_closing
|
|
172
|
+
end
|
|
173
|
+
|
|
150
174
|
# Extract child nodes
|
|
151
175
|
def self.extract_children(node)
|
|
152
176
|
children = []
|
|
@@ -441,6 +465,8 @@ module Rfmt
|
|
|
441
465
|
if (value = extract_literal_value(node))
|
|
442
466
|
metadata['value'] = value
|
|
443
467
|
end
|
|
468
|
+
when Prism::LocalVariableWriteNode, Prism::InstanceVariableWriteNode
|
|
469
|
+
metadata['name'] = node.name.to_s
|
|
444
470
|
when Prism::IfNode, Prism::UnlessNode
|
|
445
471
|
# Detect ternary operator: if_keyword_loc is nil for ternary
|
|
446
472
|
metadata['is_ternary'] = node.if_keyword_loc.nil?.to_s if node.respond_to?(:if_keyword_loc)
|
data/lib/rfmt/version.rb
CHANGED