rfmt 1.5.3 → 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 +31 -0
- data/Cargo.lock +1 -1
- 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 -1844
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
//! CaseRule, WhenRule, CaseMatchRule, InRule - Formats Ruby case expressions
|
|
2
|
+
//!
|
|
3
|
+
//! Handles:
|
|
4
|
+
//! - case/when: `case x when ... end`
|
|
5
|
+
//! - case/in: `case x in ... end` (pattern matching)
|
|
6
|
+
|
|
7
|
+
use crate::ast::{Node, NodeType};
|
|
8
|
+
use crate::doc::{concat, hardline, indent, text, Doc};
|
|
9
|
+
use crate::error::Result;
|
|
10
|
+
use crate::format::context::FormatContext;
|
|
11
|
+
use crate::format::registry::RuleRegistry;
|
|
12
|
+
use crate::format::rule::{
|
|
13
|
+
format_leading_comments, format_statements, format_trailing_comment, FormatRule,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/// Rule for formatting case expressions.
|
|
17
|
+
pub struct CaseRule;
|
|
18
|
+
|
|
19
|
+
/// Rule for formatting when clauses.
|
|
20
|
+
pub struct WhenRule;
|
|
21
|
+
|
|
22
|
+
/// Rule for formatting case match expressions (Ruby 3.0+ pattern matching).
|
|
23
|
+
pub struct CaseMatchRule;
|
|
24
|
+
|
|
25
|
+
/// Rule for formatting in clauses (pattern matching).
|
|
26
|
+
pub struct InRule;
|
|
27
|
+
|
|
28
|
+
impl FormatRule for CaseRule {
|
|
29
|
+
fn format(&self, node: &Node, ctx: &mut FormatContext, registry: &RuleRegistry) -> Result<Doc> {
|
|
30
|
+
format_case(node, ctx, registry)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
impl FormatRule for WhenRule {
|
|
35
|
+
fn format(&self, node: &Node, ctx: &mut FormatContext, registry: &RuleRegistry) -> Result<Doc> {
|
|
36
|
+
format_when(node, ctx, registry)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
impl FormatRule for CaseMatchRule {
|
|
41
|
+
fn format(&self, node: &Node, ctx: &mut FormatContext, registry: &RuleRegistry) -> Result<Doc> {
|
|
42
|
+
format_case_match(node, ctx, registry)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
impl FormatRule for InRule {
|
|
47
|
+
fn format(&self, node: &Node, ctx: &mut FormatContext, registry: &RuleRegistry) -> Result<Doc> {
|
|
48
|
+
format_in(node, ctx, registry)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/// Formats case expression
|
|
53
|
+
fn format_case(node: &Node, ctx: &mut FormatContext, registry: &RuleRegistry) -> Result<Doc> {
|
|
54
|
+
let mut docs: Vec<Doc> = Vec::with_capacity(8);
|
|
55
|
+
|
|
56
|
+
// Leading comments
|
|
57
|
+
let leading = format_leading_comments(ctx, node.location.start_line);
|
|
58
|
+
if !leading.is_empty() {
|
|
59
|
+
docs.push(leading);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// "case" keyword
|
|
63
|
+
docs.push(text("case"));
|
|
64
|
+
|
|
65
|
+
// Find predicate (first child that isn't WhenNode or ElseNode)
|
|
66
|
+
let mut when_start_idx = 0;
|
|
67
|
+
if let Some(first_child) = node.children.first() {
|
|
68
|
+
if !matches!(
|
|
69
|
+
first_child.node_type,
|
|
70
|
+
NodeType::WhenNode | NodeType::ElseNode
|
|
71
|
+
) {
|
|
72
|
+
// This is the predicate
|
|
73
|
+
if let Some(source_text) = ctx.extract_source(first_child) {
|
|
74
|
+
docs.push(text(" "));
|
|
75
|
+
docs.push(text(source_text));
|
|
76
|
+
}
|
|
77
|
+
when_start_idx = 1;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Emit when clauses and else
|
|
82
|
+
for child in node.children.iter().skip(when_start_idx) {
|
|
83
|
+
match &child.node_type {
|
|
84
|
+
NodeType::WhenNode => {
|
|
85
|
+
docs.push(hardline());
|
|
86
|
+
let when_doc = format_when(child, ctx, registry)?;
|
|
87
|
+
docs.push(when_doc);
|
|
88
|
+
}
|
|
89
|
+
NodeType::ElseNode => {
|
|
90
|
+
docs.push(hardline());
|
|
91
|
+
docs.push(text("else"));
|
|
92
|
+
|
|
93
|
+
// Emit else body
|
|
94
|
+
for else_child in &child.children {
|
|
95
|
+
if matches!(else_child.node_type, NodeType::StatementsNode) {
|
|
96
|
+
let body_doc = format_statements(else_child, ctx, registry)?;
|
|
97
|
+
docs.push(indent(concat(vec![hardline(), body_doc])));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
_ => {}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// End keyword
|
|
106
|
+
docs.push(hardline());
|
|
107
|
+
docs.push(text("end"));
|
|
108
|
+
|
|
109
|
+
// Trailing comment on end line
|
|
110
|
+
let trailing = format_trailing_comment(ctx, node.location.end_line);
|
|
111
|
+
if !trailing.is_empty() {
|
|
112
|
+
docs.push(trailing);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
Ok(concat(docs))
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/// Formats when clause
|
|
119
|
+
fn format_when(node: &Node, ctx: &mut FormatContext, registry: &RuleRegistry) -> Result<Doc> {
|
|
120
|
+
let mut docs: Vec<Doc> = Vec::with_capacity(6);
|
|
121
|
+
|
|
122
|
+
// Leading comments
|
|
123
|
+
let leading = format_leading_comments(ctx, node.location.start_line);
|
|
124
|
+
if !leading.is_empty() {
|
|
125
|
+
docs.push(leading);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
docs.push(text("when "));
|
|
129
|
+
|
|
130
|
+
// Collect conditions (all children except StatementsNode)
|
|
131
|
+
let conditions: Vec<_> = node
|
|
132
|
+
.children
|
|
133
|
+
.iter()
|
|
134
|
+
.filter(|c| !matches!(c.node_type, NodeType::StatementsNode))
|
|
135
|
+
.collect();
|
|
136
|
+
|
|
137
|
+
// Emit conditions with comma separator
|
|
138
|
+
for (i, cond) in conditions.iter().enumerate() {
|
|
139
|
+
if let Some(source_text) = ctx.extract_source(cond) {
|
|
140
|
+
docs.push(text(source_text));
|
|
141
|
+
if i < conditions.len() - 1 {
|
|
142
|
+
docs.push(text(", "));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
let statements = node
|
|
148
|
+
.children
|
|
149
|
+
.iter()
|
|
150
|
+
.find(|c| matches!(c.node_type, NodeType::StatementsNode));
|
|
151
|
+
|
|
152
|
+
let is_single_line = node.location.start_line == node.location.end_line;
|
|
153
|
+
|
|
154
|
+
if is_single_line {
|
|
155
|
+
// Inline style: when X then Y
|
|
156
|
+
if let Some(statements) = statements {
|
|
157
|
+
if let Some(source_text) = ctx.extract_source(statements) {
|
|
158
|
+
docs.push(text(" then "));
|
|
159
|
+
docs.push(text(source_text));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
// Multi-line style: when X\n Y
|
|
164
|
+
if let Some(statements) = statements {
|
|
165
|
+
let body_doc = format_statements(statements, ctx, registry)?;
|
|
166
|
+
docs.push(indent(concat(vec![hardline(), body_doc])));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
Ok(concat(docs))
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/// Formats case match expression (Ruby 3.0+ pattern matching)
|
|
174
|
+
fn format_case_match(node: &Node, ctx: &mut FormatContext, registry: &RuleRegistry) -> Result<Doc> {
|
|
175
|
+
let mut docs: Vec<Doc> = Vec::with_capacity(8);
|
|
176
|
+
|
|
177
|
+
// Leading comments
|
|
178
|
+
let leading = format_leading_comments(ctx, node.location.start_line);
|
|
179
|
+
if !leading.is_empty() {
|
|
180
|
+
docs.push(leading);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// "case" keyword
|
|
184
|
+
docs.push(text("case"));
|
|
185
|
+
|
|
186
|
+
// Find predicate (first child that isn't InNode or ElseNode)
|
|
187
|
+
let mut in_start_idx = 0;
|
|
188
|
+
if let Some(first_child) = node.children.first() {
|
|
189
|
+
if !matches!(first_child.node_type, NodeType::InNode | NodeType::ElseNode) {
|
|
190
|
+
// This is the predicate
|
|
191
|
+
if let Some(source_text) = ctx.extract_source(first_child) {
|
|
192
|
+
docs.push(text(" "));
|
|
193
|
+
docs.push(text(source_text));
|
|
194
|
+
}
|
|
195
|
+
in_start_idx = 1;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Emit in clauses and else
|
|
200
|
+
for child in node.children.iter().skip(in_start_idx) {
|
|
201
|
+
match &child.node_type {
|
|
202
|
+
NodeType::InNode => {
|
|
203
|
+
docs.push(hardline());
|
|
204
|
+
let in_doc = format_in(child, ctx, registry)?;
|
|
205
|
+
docs.push(in_doc);
|
|
206
|
+
}
|
|
207
|
+
NodeType::ElseNode => {
|
|
208
|
+
docs.push(hardline());
|
|
209
|
+
docs.push(text("else"));
|
|
210
|
+
|
|
211
|
+
// Emit else body
|
|
212
|
+
for else_child in &child.children {
|
|
213
|
+
if matches!(else_child.node_type, NodeType::StatementsNode) {
|
|
214
|
+
let body_doc = format_statements(else_child, ctx, registry)?;
|
|
215
|
+
docs.push(indent(concat(vec![hardline(), body_doc])));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
_ => {}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// End keyword
|
|
224
|
+
docs.push(hardline());
|
|
225
|
+
docs.push(text("end"));
|
|
226
|
+
|
|
227
|
+
// Trailing comment on end line
|
|
228
|
+
let trailing = format_trailing_comment(ctx, node.location.end_line);
|
|
229
|
+
if !trailing.is_empty() {
|
|
230
|
+
docs.push(trailing);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
Ok(concat(docs))
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/// Formats in clause (pattern matching)
|
|
237
|
+
fn format_in(node: &Node, ctx: &mut FormatContext, registry: &RuleRegistry) -> Result<Doc> {
|
|
238
|
+
let mut docs: Vec<Doc> = Vec::with_capacity(6);
|
|
239
|
+
|
|
240
|
+
// Leading comments
|
|
241
|
+
let leading = format_leading_comments(ctx, node.location.start_line);
|
|
242
|
+
if !leading.is_empty() {
|
|
243
|
+
docs.push(leading);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
docs.push(text("in "));
|
|
247
|
+
|
|
248
|
+
// First child is the pattern
|
|
249
|
+
if let Some(pattern) = node.children.first() {
|
|
250
|
+
if let Some(source_text) = ctx.extract_source(pattern) {
|
|
251
|
+
docs.push(text(source_text));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
let is_single_line = node.location.start_line == node.location.end_line;
|
|
256
|
+
|
|
257
|
+
if is_single_line {
|
|
258
|
+
// Inline style: in X then Y
|
|
259
|
+
if let Some(statements) = node.children.get(1) {
|
|
260
|
+
if let Some(source_text) = ctx.extract_source(statements) {
|
|
261
|
+
docs.push(text(" then "));
|
|
262
|
+
docs.push(text(source_text));
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
} else {
|
|
266
|
+
// Multi-line style: in X\n Y
|
|
267
|
+
if let Some(statements) = node.children.get(1) {
|
|
268
|
+
if matches!(statements.node_type, NodeType::StatementsNode) {
|
|
269
|
+
let body_doc = format_statements(statements, ctx, registry)?;
|
|
270
|
+
docs.push(indent(concat(vec![hardline(), body_doc])));
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
Ok(concat(docs))
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
#[cfg(test)]
|
|
279
|
+
mod tests {
|
|
280
|
+
use super::*;
|
|
281
|
+
use crate::ast::{FormattingInfo, Location};
|
|
282
|
+
use crate::config::Config;
|
|
283
|
+
use crate::doc::Printer;
|
|
284
|
+
use std::collections::HashMap;
|
|
285
|
+
|
|
286
|
+
fn make_case_node(children: Vec<Node>, start_line: usize, end_line: usize) -> Node {
|
|
287
|
+
Node {
|
|
288
|
+
node_type: NodeType::CaseNode,
|
|
289
|
+
location: Location::new(start_line, 0, end_line, 3, 0, 50),
|
|
290
|
+
children,
|
|
291
|
+
metadata: HashMap::new(),
|
|
292
|
+
comments: Vec::new(),
|
|
293
|
+
formatting: FormattingInfo::default(),
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
fn make_when_node(children: Vec<Node>, start_line: usize, end_line: usize) -> Node {
|
|
298
|
+
Node {
|
|
299
|
+
node_type: NodeType::WhenNode,
|
|
300
|
+
location: Location::new(start_line, 0, end_line, 0, 0, 30),
|
|
301
|
+
children,
|
|
302
|
+
metadata: HashMap::new(),
|
|
303
|
+
comments: Vec::new(),
|
|
304
|
+
formatting: FormattingInfo::default(),
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
#[test]
|
|
309
|
+
fn test_simple_case() {
|
|
310
|
+
let config = Config::default();
|
|
311
|
+
let source = "case x\nwhen 1\n puts 'one'\nend";
|
|
312
|
+
let mut ctx = FormatContext::new(&config, source);
|
|
313
|
+
let registry = RuleRegistry::default_registry();
|
|
314
|
+
|
|
315
|
+
// predicate node
|
|
316
|
+
let predicate = Node {
|
|
317
|
+
node_type: NodeType::CallNode,
|
|
318
|
+
location: Location::new(1, 5, 1, 6, 5, 6),
|
|
319
|
+
children: Vec::new(),
|
|
320
|
+
metadata: HashMap::new(),
|
|
321
|
+
comments: Vec::new(),
|
|
322
|
+
formatting: FormattingInfo::default(),
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
// when node
|
|
326
|
+
let when_cond = Node {
|
|
327
|
+
node_type: NodeType::IntegerNode,
|
|
328
|
+
location: Location::new(2, 5, 2, 6, 12, 13),
|
|
329
|
+
children: Vec::new(),
|
|
330
|
+
metadata: HashMap::new(),
|
|
331
|
+
comments: Vec::new(),
|
|
332
|
+
formatting: FormattingInfo::default(),
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
let when_body = Node {
|
|
336
|
+
node_type: NodeType::StatementsNode,
|
|
337
|
+
location: Location::new(3, 2, 3, 13, 16, 27),
|
|
338
|
+
children: Vec::new(),
|
|
339
|
+
metadata: HashMap::new(),
|
|
340
|
+
comments: Vec::new(),
|
|
341
|
+
formatting: FormattingInfo::default(),
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
let when_node = make_when_node(vec![when_cond, when_body], 2, 3);
|
|
345
|
+
|
|
346
|
+
let node = make_case_node(vec![predicate, when_node], 1, 4);
|
|
347
|
+
ctx.collect_comments(&node);
|
|
348
|
+
|
|
349
|
+
let rule = CaseRule;
|
|
350
|
+
let doc = rule.format(&node, &mut ctx, ®istry).unwrap();
|
|
351
|
+
|
|
352
|
+
let mut printer = Printer::new(&config);
|
|
353
|
+
let result = printer.print(&doc);
|
|
354
|
+
|
|
355
|
+
assert!(result.contains("case x"));
|
|
356
|
+
assert!(result.contains("when 1"));
|
|
357
|
+
assert!(result.contains("end"));
|
|
358
|
+
}
|
|
359
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
//! ClassRule - Formats Ruby class definitions
|
|
2
|
+
//!
|
|
3
|
+
//! Handles class definitions including:
|
|
4
|
+
//! - Simple classes: `class Foo`
|
|
5
|
+
//! - Classes with inheritance: `class Foo < Bar`
|
|
6
|
+
//! - Class bodies with methods and other declarations
|
|
7
|
+
//! - Leading and trailing comments
|
|
8
|
+
|
|
9
|
+
use crate::ast::Node;
|
|
10
|
+
use crate::doc::{text, Doc};
|
|
11
|
+
use crate::error::Result;
|
|
12
|
+
use crate::format::context::FormatContext;
|
|
13
|
+
use crate::format::registry::RuleRegistry;
|
|
14
|
+
use crate::format::rule::FormatRule;
|
|
15
|
+
|
|
16
|
+
use super::body_end::{format_body_end, BodyEndConfig};
|
|
17
|
+
|
|
18
|
+
/// Rule for formatting class definitions.
|
|
19
|
+
pub struct ClassRule;
|
|
20
|
+
|
|
21
|
+
impl FormatRule for ClassRule {
|
|
22
|
+
fn format(&self, node: &Node, ctx: &mut FormatContext, registry: &RuleRegistry) -> Result<Doc> {
|
|
23
|
+
format_body_end(
|
|
24
|
+
ctx,
|
|
25
|
+
registry,
|
|
26
|
+
BodyEndConfig {
|
|
27
|
+
keyword: "class",
|
|
28
|
+
node,
|
|
29
|
+
header_builder: Box::new(build_class_header),
|
|
30
|
+
skip_same_line_children: true,
|
|
31
|
+
},
|
|
32
|
+
)
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// Builds the header portion for a class definition.
|
|
37
|
+
///
|
|
38
|
+
/// Returns: `ClassName` or `ClassName < Superclass`
|
|
39
|
+
fn build_class_header(node: &Node) -> Vec<Doc> {
|
|
40
|
+
let mut parts: Vec<Doc> = Vec::with_capacity(4);
|
|
41
|
+
|
|
42
|
+
// Get class name from metadata
|
|
43
|
+
if let Some(name) = node.metadata.get("name") {
|
|
44
|
+
parts.push(text(name));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Get superclass from metadata if present
|
|
48
|
+
if let Some(superclass) = node.metadata.get("superclass") {
|
|
49
|
+
parts.push(text(" < "));
|
|
50
|
+
parts.push(text(superclass));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
parts
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
#[cfg(test)]
|
|
57
|
+
mod tests {
|
|
58
|
+
use super::*;
|
|
59
|
+
use crate::ast::{FormattingInfo, Location, NodeType};
|
|
60
|
+
use crate::config::Config;
|
|
61
|
+
use crate::doc::Printer;
|
|
62
|
+
use std::collections::HashMap;
|
|
63
|
+
|
|
64
|
+
fn make_class_node(
|
|
65
|
+
name: &str,
|
|
66
|
+
superclass: Option<&str>,
|
|
67
|
+
children: Vec<Node>,
|
|
68
|
+
start_line: usize,
|
|
69
|
+
end_line: usize,
|
|
70
|
+
) -> Node {
|
|
71
|
+
let mut metadata = HashMap::new();
|
|
72
|
+
metadata.insert("name".to_string(), name.to_string());
|
|
73
|
+
if let Some(sc) = superclass {
|
|
74
|
+
metadata.insert("superclass".to_string(), sc.to_string());
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
Node {
|
|
78
|
+
node_type: NodeType::ClassNode,
|
|
79
|
+
location: Location::new(start_line, 0, end_line, 3, 0, 50),
|
|
80
|
+
children,
|
|
81
|
+
metadata,
|
|
82
|
+
comments: Vec::new(),
|
|
83
|
+
formatting: FormattingInfo::default(),
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
#[test]
|
|
88
|
+
fn test_simple_class() {
|
|
89
|
+
let config = Config::default();
|
|
90
|
+
let source = "class Foo\nend";
|
|
91
|
+
let mut ctx = FormatContext::new(&config, source);
|
|
92
|
+
let registry = RuleRegistry::default_registry();
|
|
93
|
+
|
|
94
|
+
let node = make_class_node("Foo", None, Vec::new(), 1, 2);
|
|
95
|
+
ctx.collect_comments(&node);
|
|
96
|
+
|
|
97
|
+
let rule = ClassRule;
|
|
98
|
+
let doc = rule.format(&node, &mut ctx, ®istry).unwrap();
|
|
99
|
+
|
|
100
|
+
let mut printer = Printer::new(&config);
|
|
101
|
+
let result = printer.print(&doc);
|
|
102
|
+
|
|
103
|
+
assert_eq!(result.trim(), "class Foo\nend");
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
#[test]
|
|
107
|
+
fn test_class_with_inheritance() {
|
|
108
|
+
let config = Config::default();
|
|
109
|
+
let source = "class Foo < Bar\nend";
|
|
110
|
+
let mut ctx = FormatContext::new(&config, source);
|
|
111
|
+
let registry = RuleRegistry::default_registry();
|
|
112
|
+
|
|
113
|
+
let node = make_class_node("Foo", Some("Bar"), Vec::new(), 1, 2);
|
|
114
|
+
ctx.collect_comments(&node);
|
|
115
|
+
|
|
116
|
+
let rule = ClassRule;
|
|
117
|
+
let doc = rule.format(&node, &mut ctx, ®istry).unwrap();
|
|
118
|
+
|
|
119
|
+
let mut printer = Printer::new(&config);
|
|
120
|
+
let result = printer.print(&doc);
|
|
121
|
+
|
|
122
|
+
assert_eq!(result.trim(), "class Foo < Bar\nend");
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
#[test]
|
|
126
|
+
fn test_class_with_body() {
|
|
127
|
+
let config = Config::default();
|
|
128
|
+
let source = "class Foo\n def bar\n end\nend";
|
|
129
|
+
let mut ctx = FormatContext::new(&config, source);
|
|
130
|
+
let registry = RuleRegistry::default_registry();
|
|
131
|
+
|
|
132
|
+
// Create a method node as child
|
|
133
|
+
let method_node = Node {
|
|
134
|
+
node_type: NodeType::DefNode,
|
|
135
|
+
location: Location::new(2, 2, 3, 5, 12, 24),
|
|
136
|
+
children: Vec::new(),
|
|
137
|
+
metadata: {
|
|
138
|
+
let mut m = HashMap::new();
|
|
139
|
+
m.insert("name".to_string(), "bar".to_string());
|
|
140
|
+
m
|
|
141
|
+
},
|
|
142
|
+
comments: Vec::new(),
|
|
143
|
+
formatting: FormattingInfo::default(),
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
let node = make_class_node("Foo", None, vec![method_node], 1, 4);
|
|
147
|
+
ctx.collect_comments(&node);
|
|
148
|
+
|
|
149
|
+
let rule = ClassRule;
|
|
150
|
+
let doc = rule.format(&node, &mut ctx, ®istry).unwrap();
|
|
151
|
+
|
|
152
|
+
let mut printer = Printer::new(&config);
|
|
153
|
+
let result = printer.print(&doc);
|
|
154
|
+
|
|
155
|
+
// Should have the class, body, and end
|
|
156
|
+
assert!(result.contains("class Foo"));
|
|
157
|
+
assert!(result.contains("def bar"));
|
|
158
|
+
assert!(result.contains("end"));
|
|
159
|
+
}
|
|
160
|
+
}
|