rfmt 1.5.2 → 1.6.0
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 +36 -0
- data/Cargo.lock +266 -92
- data/README.md +22 -18
- data/ext/rfmt/Cargo.toml +4 -1
- data/ext/rfmt/src/doc/builders.rs +528 -0
- data/ext/rfmt/src/doc/mod.rs +220 -0
- data/ext/rfmt/src/doc/printer.rs +684 -0
- data/ext/rfmt/src/format/context.rs +448 -0
- data/ext/rfmt/src/format/formatter.rs +226 -0
- data/ext/rfmt/src/format/mod.rs +35 -0
- data/ext/rfmt/src/format/registry.rs +195 -0
- data/ext/rfmt/src/format/rule.rs +555 -0
- data/ext/rfmt/src/format/rules/begin.rs +295 -0
- data/ext/rfmt/src/format/rules/body_end.rs +109 -0
- data/ext/rfmt/src/format/rules/call.rs +409 -0
- data/ext/rfmt/src/format/rules/case.rs +359 -0
- data/ext/rfmt/src/format/rules/class.rs +160 -0
- data/ext/rfmt/src/format/rules/def.rs +216 -0
- data/ext/rfmt/src/format/rules/fallback.rs +116 -0
- data/ext/rfmt/src/format/rules/if_unless.rs +407 -0
- data/ext/rfmt/src/format/rules/loops.rs +325 -0
- data/ext/rfmt/src/format/rules/mod.rs +31 -0
- data/ext/rfmt/src/format/rules/module.rs +150 -0
- data/ext/rfmt/src/format/rules/singleton_class.rs +202 -0
- data/ext/rfmt/src/format/rules/statements.rs +122 -0
- data/ext/rfmt/src/format/rules/variable_write.rs +296 -0
- data/ext/rfmt/src/lib.rs +8 -5
- data/ext/rfmt/src/parser/prism_adapter.rs +157 -2
- data/lib/rfmt/version.rb +1 -1
- data/lib/ruby_lsp/rfmt/formatter_runner.rb +2 -0
- metadata +23 -2
- data/ext/rfmt/src/emitter/mod.rs +0 -1760
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//! Rule-based formatting system for rfmt
|
|
2
|
+
//!
|
|
3
|
+
//! This module provides a Prettier-inspired architecture for formatting Ruby code:
|
|
4
|
+
//!
|
|
5
|
+
//! - **FormatContext**: Manages state during formatting (comments, source, config)
|
|
6
|
+
//! - **FormatRule**: Trait for implementing formatting rules for specific node types
|
|
7
|
+
//! - **RuleRegistry**: Maps node types to their formatting rules
|
|
8
|
+
//! - **Formatter**: Main entry point that coordinates the formatting process
|
|
9
|
+
//!
|
|
10
|
+
//! # Architecture
|
|
11
|
+
//!
|
|
12
|
+
//! ```text
|
|
13
|
+
//! AST Node → RuleRegistry.get_rule() → FormatRule.format() → Doc IR → Printer → String
|
|
14
|
+
//! ```
|
|
15
|
+
//!
|
|
16
|
+
//! # Example
|
|
17
|
+
//!
|
|
18
|
+
//! ```rust,ignore
|
|
19
|
+
//! use rfmt::format::Formatter;
|
|
20
|
+
//! use rfmt::config::Config;
|
|
21
|
+
//!
|
|
22
|
+
//! let formatter = Formatter::new(Config::default());
|
|
23
|
+
//! let result = formatter.format(source, &ast)?;
|
|
24
|
+
//! ```
|
|
25
|
+
|
|
26
|
+
pub mod context;
|
|
27
|
+
pub mod formatter;
|
|
28
|
+
pub mod registry;
|
|
29
|
+
pub mod rule;
|
|
30
|
+
pub mod rules;
|
|
31
|
+
|
|
32
|
+
pub use context::FormatContext;
|
|
33
|
+
pub use formatter::Formatter;
|
|
34
|
+
pub use registry::RuleRegistry;
|
|
35
|
+
pub use rule::{BoxedRule, FormatRule};
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
//! RuleRegistry - Dispatches nodes to appropriate formatting rules
|
|
2
|
+
//!
|
|
3
|
+
//! The registry maps NodeType to FormatRule implementations, allowing
|
|
4
|
+
//! the formatter to dispatch nodes to the correct rule.
|
|
5
|
+
|
|
6
|
+
use crate::ast::NodeType;
|
|
7
|
+
use std::borrow::Cow;
|
|
8
|
+
use std::collections::HashMap;
|
|
9
|
+
|
|
10
|
+
use super::rule::{BoxedRule, FormatRule};
|
|
11
|
+
use super::rules::{
|
|
12
|
+
BeginRule, BlockRule, CallRule, CaseMatchRule, CaseRule, ClassRule, DefRule, EnsureRule,
|
|
13
|
+
FallbackRule, ForRule, IfRule, InRule, InstanceVariableWriteRule, LambdaRule,
|
|
14
|
+
LocalVariableWriteRule, ModuleRule, RescueRule, SingletonClassRule, StatementsRule, UnlessRule,
|
|
15
|
+
UntilRule, WhenRule, WhileRule,
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/// Key type for the registry, derived from NodeType.
|
|
19
|
+
///
|
|
20
|
+
/// Uses `Cow<'static, str>` to avoid allocation for known node types
|
|
21
|
+
/// while still supporting dynamic Unknown types.
|
|
22
|
+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
23
|
+
pub struct NodeTypeKey(Cow<'static, str>);
|
|
24
|
+
|
|
25
|
+
impl NodeTypeKey {
|
|
26
|
+
#[inline]
|
|
27
|
+
const fn from_static(s: &'static str) -> Self {
|
|
28
|
+
Self(Cow::Borrowed(s))
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
fn from_owned(s: String) -> Self {
|
|
32
|
+
Self(Cow::Owned(s))
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
impl From<&NodeType> for NodeTypeKey {
|
|
37
|
+
#[inline]
|
|
38
|
+
fn from(node_type: &NodeType) -> Self {
|
|
39
|
+
match node_type {
|
|
40
|
+
NodeType::ProgramNode => Self::from_static("program_node"),
|
|
41
|
+
NodeType::StatementsNode => Self::from_static("statements_node"),
|
|
42
|
+
NodeType::ClassNode => Self::from_static("class_node"),
|
|
43
|
+
NodeType::ModuleNode => Self::from_static("module_node"),
|
|
44
|
+
NodeType::SingletonClassNode => Self::from_static("singleton_class_node"),
|
|
45
|
+
NodeType::DefNode => Self::from_static("def_node"),
|
|
46
|
+
NodeType::CallNode => Self::from_static("call_node"),
|
|
47
|
+
NodeType::IfNode => Self::from_static("if_node"),
|
|
48
|
+
NodeType::UnlessNode => Self::from_static("unless_node"),
|
|
49
|
+
NodeType::BeginNode => Self::from_static("begin_node"),
|
|
50
|
+
NodeType::RescueNode => Self::from_static("rescue_node"),
|
|
51
|
+
NodeType::EnsureNode => Self::from_static("ensure_node"),
|
|
52
|
+
NodeType::CaseNode => Self::from_static("case_node"),
|
|
53
|
+
NodeType::WhenNode => Self::from_static("when_node"),
|
|
54
|
+
NodeType::CaseMatchNode => Self::from_static("case_match_node"),
|
|
55
|
+
NodeType::InNode => Self::from_static("in_node"),
|
|
56
|
+
NodeType::WhileNode => Self::from_static("while_node"),
|
|
57
|
+
NodeType::UntilNode => Self::from_static("until_node"),
|
|
58
|
+
NodeType::ForNode => Self::from_static("for_node"),
|
|
59
|
+
NodeType::BlockNode => Self::from_static("block_node"),
|
|
60
|
+
NodeType::LambdaNode => Self::from_static("lambda_node"),
|
|
61
|
+
NodeType::LocalVariableWriteNode => Self::from_static("local_variable_write_node"),
|
|
62
|
+
NodeType::InstanceVariableWriteNode => {
|
|
63
|
+
Self::from_static("instance_variable_write_node")
|
|
64
|
+
}
|
|
65
|
+
NodeType::Unknown(s) => Self::from_owned(s.clone()),
|
|
66
|
+
// Default for unhandled types
|
|
67
|
+
_ => Self::from_static("unknown"),
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/// Registry that maps NodeType to FormatRule.
|
|
73
|
+
pub struct RuleRegistry {
|
|
74
|
+
rules: HashMap<NodeTypeKey, BoxedRule>,
|
|
75
|
+
fallback: BoxedRule,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
impl RuleRegistry {
|
|
79
|
+
pub fn new() -> Self {
|
|
80
|
+
Self {
|
|
81
|
+
rules: HashMap::new(),
|
|
82
|
+
fallback: Box::new(FallbackRule),
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
#[inline]
|
|
87
|
+
pub fn add<R: FormatRule + 'static>(mut self, node_type: NodeType, rule: R) -> Self {
|
|
88
|
+
let key = NodeTypeKey::from(&node_type);
|
|
89
|
+
self.rules.insert(key, Box::new(rule));
|
|
90
|
+
self
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
pub fn add_rule<R: FormatRule + 'static>(&mut self, node_type: NodeType, rule: R) {
|
|
94
|
+
let key = NodeTypeKey::from(&node_type);
|
|
95
|
+
self.rules.insert(key, Box::new(rule));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
#[inline]
|
|
99
|
+
pub fn get_rule(&self, node_type: &NodeType) -> &dyn FormatRule {
|
|
100
|
+
let key = NodeTypeKey::from(node_type);
|
|
101
|
+
self.rules
|
|
102
|
+
.get(&key)
|
|
103
|
+
.map(|r| r.as_ref())
|
|
104
|
+
.unwrap_or(self.fallback.as_ref())
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
pub fn default_registry() -> Self {
|
|
108
|
+
Self::new()
|
|
109
|
+
.add(NodeType::StatementsNode, StatementsRule)
|
|
110
|
+
.add(NodeType::ClassNode, ClassRule)
|
|
111
|
+
.add(NodeType::ModuleNode, ModuleRule)
|
|
112
|
+
.add(NodeType::SingletonClassNode, SingletonClassRule)
|
|
113
|
+
.add(NodeType::DefNode, DefRule)
|
|
114
|
+
.add(NodeType::IfNode, IfRule)
|
|
115
|
+
.add(NodeType::UnlessNode, UnlessRule)
|
|
116
|
+
.add(NodeType::CaseNode, CaseRule)
|
|
117
|
+
.add(NodeType::WhenNode, WhenRule)
|
|
118
|
+
.add(NodeType::CaseMatchNode, CaseMatchRule)
|
|
119
|
+
.add(NodeType::InNode, InRule)
|
|
120
|
+
.add(NodeType::BeginNode, BeginRule)
|
|
121
|
+
.add(NodeType::RescueNode, RescueRule)
|
|
122
|
+
.add(NodeType::EnsureNode, EnsureRule)
|
|
123
|
+
.add(NodeType::CallNode, CallRule)
|
|
124
|
+
.add(NodeType::BlockNode, BlockRule)
|
|
125
|
+
.add(NodeType::LambdaNode, LambdaRule)
|
|
126
|
+
.add(NodeType::WhileNode, WhileRule)
|
|
127
|
+
.add(NodeType::UntilNode, UntilRule)
|
|
128
|
+
.add(NodeType::ForNode, ForRule)
|
|
129
|
+
.add(NodeType::LocalVariableWriteNode, LocalVariableWriteRule)
|
|
130
|
+
.add(
|
|
131
|
+
NodeType::InstanceVariableWriteNode,
|
|
132
|
+
InstanceVariableWriteRule,
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
impl Default for RuleRegistry {
|
|
138
|
+
fn default() -> Self {
|
|
139
|
+
Self::default_registry()
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
#[cfg(test)]
|
|
144
|
+
mod tests {
|
|
145
|
+
use super::*;
|
|
146
|
+
|
|
147
|
+
#[test]
|
|
148
|
+
fn test_node_type_key_from_node_type() {
|
|
149
|
+
let key = NodeTypeKey::from(&NodeType::ClassNode);
|
|
150
|
+
assert_eq!(key.0, "class_node");
|
|
151
|
+
|
|
152
|
+
let key = NodeTypeKey::from(&NodeType::DefNode);
|
|
153
|
+
assert_eq!(key.0, "def_node");
|
|
154
|
+
|
|
155
|
+
let key = NodeTypeKey::from(&NodeType::Unknown("custom".to_string()));
|
|
156
|
+
assert_eq!(key.0, "custom");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
#[test]
|
|
160
|
+
fn test_registry_get_registered_rule() {
|
|
161
|
+
let registry = RuleRegistry::default_registry();
|
|
162
|
+
|
|
163
|
+
// ClassRule should be registered
|
|
164
|
+
let _rule = registry.get_rule(&NodeType::ClassNode);
|
|
165
|
+
// If we get here, the rule was found
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
#[test]
|
|
169
|
+
fn test_registry_get_fallback_rule() {
|
|
170
|
+
let registry = RuleRegistry::default_registry();
|
|
171
|
+
|
|
172
|
+
// IntegerNode doesn't have a specific rule, should use fallback
|
|
173
|
+
let _rule = registry.get_rule(&NodeType::IntegerNode);
|
|
174
|
+
// If we get here, the fallback was used
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
#[test]
|
|
178
|
+
fn test_registry_custom_registration() {
|
|
179
|
+
// Using builder pattern with method chaining
|
|
180
|
+
let registry = RuleRegistry::new().add(NodeType::IfNode, FallbackRule);
|
|
181
|
+
|
|
182
|
+
// Should find the registered rule
|
|
183
|
+
let _rule = registry.get_rule(&NodeType::IfNode);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
#[test]
|
|
187
|
+
fn test_registry_add_rule_mutable() {
|
|
188
|
+
// Using mutable reference variant
|
|
189
|
+
let mut registry = RuleRegistry::new();
|
|
190
|
+
registry.add_rule(NodeType::DefNode, FallbackRule);
|
|
191
|
+
|
|
192
|
+
// Should find the registered rule
|
|
193
|
+
let _rule = registry.get_rule(&NodeType::DefNode);
|
|
194
|
+
}
|
|
195
|
+
}
|