rfmt 1.6.1 → 1.6.3
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 +8 -0
- data/Cargo.lock +1 -1
- data/ext/rfmt/Cargo.toml +1 -1
- data/ext/rfmt/src/format/rule.rs +100 -6
- data/ext/rfmt/src/format/rules/call.rs +20 -3
- 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: 4fd5d79157230ea6ee461e070a3782267196b57692c37fc0e5e06a33f2dbd6fd
|
|
4
|
+
data.tar.gz: ea5b633304e771cd80a235ae79676581791f5f1f041de4d4013fd22e60b3cd5c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1d81952b2c5b9b9a3ba04617c60c1742d6141390be0da810bdc71ab2661bd767008bf7b744b1337496035bfeab39ee9db672163f23e9d693aad681edc8236256
|
|
7
|
+
data.tar.gz: '0950ad8b8a68dc96bb20fd980751805704b6f1a6be07268210fe309ba4b6642469899f61c62d8157770956732c272636d072d9ad29d148ff555335897df2ae0e'
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.6.3] - 2026-04-24
|
|
4
|
+
|
|
5
|
+
Minor stability refinements on top of the 1.6.x architecture release. See the 1.6.1 notes below for the feature set this series delivers.
|
|
6
|
+
|
|
7
|
+
## [1.6.2] - 2026-04-24
|
|
8
|
+
|
|
9
|
+
Minor stability refinements on top of the 1.6.x architecture release. See the 1.6.1 notes below for the feature set this series delivers.
|
|
10
|
+
|
|
3
11
|
## [1.6.1] - 2026-04-24
|
|
4
12
|
|
|
5
13
|
Follow-up release consolidating the 1.6.0 architecture work.
|
data/Cargo.lock
CHANGED
data/ext/rfmt/Cargo.toml
CHANGED
data/ext/rfmt/src/format/rule.rs
CHANGED
|
@@ -217,6 +217,23 @@ pub fn format_comments_before_end(
|
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
let mut docs: Vec<Doc> = vec![hardline()];
|
|
220
|
+
// Preserve the blank line that separated the body's last content from
|
|
221
|
+
// the first trailing comment. Without this, a construct like
|
|
222
|
+
//
|
|
223
|
+
// def foo
|
|
224
|
+
// body
|
|
225
|
+
// <- blank line
|
|
226
|
+
// # trailing annotation
|
|
227
|
+
// end
|
|
228
|
+
//
|
|
229
|
+
// collapses to `body\n# trailing annotation\nend`. Detect the case
|
|
230
|
+
// heuristically: if the source line immediately above the first
|
|
231
|
+
// standalone comment is blank, emit an extra hardline.
|
|
232
|
+
if let Some(first) = standalone_refs.first() {
|
|
233
|
+
if first.start_line > 1 && is_line_blank(ctx.source(), first.start_line - 1) {
|
|
234
|
+
docs.push(hardline());
|
|
235
|
+
}
|
|
236
|
+
}
|
|
220
237
|
let mut last_end_line: Option<usize> = None;
|
|
221
238
|
let mut indices_to_mark: Vec<usize> = Vec::with_capacity(standalone_refs.len());
|
|
222
239
|
|
|
@@ -297,11 +314,23 @@ pub fn format_remaining_comments(ctx: &mut FormatContext, last_code_line: usize)
|
|
|
297
314
|
let mut indices_to_mark: Vec<usize> = Vec::with_capacity(comment_refs.len());
|
|
298
315
|
|
|
299
316
|
for cref in &comment_refs {
|
|
300
|
-
// Preserve blank lines
|
|
317
|
+
// Preserve blank lines. On the first iteration we must emit *at
|
|
318
|
+
// least one* hardline to separate the first remaining comment from
|
|
319
|
+
// the main document's last token (otherwise an orphan comment whose
|
|
320
|
+
// `start_line <= last_code_line` would concatenate onto whatever
|
|
321
|
+
// ended the output — producing e.g. `end# comment…` when a block's
|
|
322
|
+
// internal comments fall through to this tail handler). Round-tripping
|
|
323
|
+
// the already-formatted output must still be idempotent, so we
|
|
324
|
+
// cap the emission at the number of line breaks visible in the
|
|
325
|
+
// source: 1 for an adjacent comment, N for N-1 blank lines above it.
|
|
301
326
|
let gap = cref.start_line.saturating_sub(last_end_line);
|
|
302
327
|
|
|
303
|
-
|
|
304
|
-
|
|
328
|
+
if is_first {
|
|
329
|
+
let hardlines_to_emit = gap.max(1);
|
|
330
|
+
for _ in 0..hardlines_to_emit {
|
|
331
|
+
docs.push(hardline());
|
|
332
|
+
}
|
|
333
|
+
} else if gap > 0 {
|
|
305
334
|
for _ in 0..gap.max(1) {
|
|
306
335
|
docs.push(hardline());
|
|
307
336
|
}
|
|
@@ -479,8 +508,42 @@ pub fn reformat_chain_lines(
|
|
|
479
508
|
return Cow::Borrowed(source_text);
|
|
480
509
|
}
|
|
481
510
|
|
|
482
|
-
//
|
|
483
|
-
|
|
511
|
+
// Determine how much the chain is moving left. Before this pass, every
|
|
512
|
+
// `.method` line sat at some original "chain indent" (most commonly
|
|
513
|
+
// aligned under the first receiver's dot). We rewrite those lines to
|
|
514
|
+
// `base_indent + indent_width` — but that also means any multi-line
|
|
515
|
+
// *arguments* that lived inside a chain call like `.select( … )` used
|
|
516
|
+
// to be deeper than the original chain indent, and will now look
|
|
517
|
+
// orphaned off to the right if we leave them alone:
|
|
518
|
+
//
|
|
519
|
+
// @users = User.left_joins(...)
|
|
520
|
+
// .select( <- was col 17, becomes col 6
|
|
521
|
+
// 'users.*, ' \ <- still at col 19 — orphaned
|
|
522
|
+
// )
|
|
523
|
+
// .having(...)
|
|
524
|
+
//
|
|
525
|
+
// Compute the delta between the original chain indent and the new
|
|
526
|
+
// one, and shift every non-chain continuation line that lives at
|
|
527
|
+
// (or below) the original chain indent by the same amount. Lines
|
|
528
|
+
// shallower than the original chain indent (e.g. a heredoc body
|
|
529
|
+
// whose squiggly indent is measured from the terminator) are left
|
|
530
|
+
// alone, so we don't accidentally eat through them.
|
|
531
|
+
let new_chain_indent = base_indent + indent_width;
|
|
532
|
+
let chain_indent_str = " ".repeat(new_chain_indent);
|
|
533
|
+
|
|
534
|
+
let original_chain_indent = lines[1..].iter().find_map(|l| {
|
|
535
|
+
let t = l.trim_start();
|
|
536
|
+
if t.starts_with('.') || t.starts_with("&.") {
|
|
537
|
+
Some(l.len() - t.len())
|
|
538
|
+
} else {
|
|
539
|
+
None
|
|
540
|
+
}
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
let arg_shift = original_chain_indent
|
|
544
|
+
.map(|orig| orig.saturating_sub(new_chain_indent))
|
|
545
|
+
.unwrap_or(0);
|
|
546
|
+
|
|
484
547
|
let mut result = String::with_capacity(source_text.len());
|
|
485
548
|
result.push_str(lines[0].trim_end());
|
|
486
549
|
|
|
@@ -488,8 +551,18 @@ pub fn reformat_chain_lines(
|
|
|
488
551
|
result.push('\n');
|
|
489
552
|
let trimmed = line.trim();
|
|
490
553
|
if trimmed.starts_with('.') || trimmed.starts_with("&.") {
|
|
491
|
-
result.push_str(&
|
|
554
|
+
result.push_str(&chain_indent_str);
|
|
492
555
|
result.push_str(trimmed);
|
|
556
|
+
} else if arg_shift > 0 && !trimmed.is_empty() {
|
|
557
|
+
let indent = line.len() - line.trim_start().len();
|
|
558
|
+
let chain_base = original_chain_indent.unwrap_or(0);
|
|
559
|
+
if indent >= chain_base {
|
|
560
|
+
let new_indent = indent - arg_shift;
|
|
561
|
+
result.push_str(&" ".repeat(new_indent));
|
|
562
|
+
result.push_str(line.trim_start());
|
|
563
|
+
} else {
|
|
564
|
+
result.push_str(line);
|
|
565
|
+
}
|
|
493
566
|
} else {
|
|
494
567
|
// Non-chain continuation (e.g., heredoc content): preserve as-is
|
|
495
568
|
result.push_str(line);
|
|
@@ -499,6 +572,27 @@ pub fn reformat_chain_lines(
|
|
|
499
572
|
Cow::Owned(result)
|
|
500
573
|
}
|
|
501
574
|
|
|
575
|
+
/// Returns true when the given 1-based `line` in `source` contains only
|
|
576
|
+
/// whitespace (or is empty). Returns false for any line that has code or
|
|
577
|
+
/// a comment.
|
|
578
|
+
fn is_line_blank(source: &str, line: usize) -> bool {
|
|
579
|
+
let mut current = 1usize;
|
|
580
|
+
let mut line_start = 0usize;
|
|
581
|
+
for (i, b) in source.bytes().enumerate() {
|
|
582
|
+
if current == line {
|
|
583
|
+
let end = source[i..].find('\n').map_or(source.len(), |n| i + n);
|
|
584
|
+
return source[line_start..end]
|
|
585
|
+
.bytes()
|
|
586
|
+
.all(|b| b == b' ' || b == b'\t' || b == b'\r');
|
|
587
|
+
}
|
|
588
|
+
if b == b'\n' {
|
|
589
|
+
current += 1;
|
|
590
|
+
line_start = i + 1;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
false
|
|
594
|
+
}
|
|
595
|
+
|
|
502
596
|
/// Removes at most one trailing `\n` (optionally preceded by a single `\r`)
|
|
503
597
|
/// from `s`. Spaces, tabs, and any additional preceding newlines are
|
|
504
598
|
/// preserved.
|
|
@@ -13,9 +13,9 @@ use crate::error::Result;
|
|
|
13
13
|
use crate::format::context::FormatContext;
|
|
14
14
|
use crate::format::registry::RuleRegistry;
|
|
15
15
|
use crate::format::rule::{
|
|
16
|
-
format_child, format_leading_comments, format_statements,
|
|
17
|
-
line_leading_indent, mark_comments_in_range_emitted,
|
|
18
|
-
strip_one_trailing_newline, FormatRule,
|
|
16
|
+
format_child, format_comments_before_end, format_leading_comments, format_statements,
|
|
17
|
+
format_trailing_comment, line_leading_indent, mark_comments_in_range_emitted,
|
|
18
|
+
reformat_chain_lines, strip_one_trailing_newline, FormatRule,
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
/// Rule for formatting method calls.
|
|
@@ -251,6 +251,23 @@ fn format_do_end_block(
|
|
|
251
251
|
}
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
// Emit any standalone comments between the last body statement and `end`.
|
|
255
|
+
//
|
|
256
|
+
// Without this the orphan comments inside a `do…end` block (e.g. the
|
|
257
|
+
// commented-out config stanzas in a generated `spec_helper.rb`) never
|
|
258
|
+
// get claimed by any `format_leading_comments` call, fall through to
|
|
259
|
+
// `format_remaining_comments` at the end of the file, and get emitted
|
|
260
|
+
// *after* the block's own `end` — producing `end# comment…` with no
|
|
261
|
+
// separator and dropping the body indent.
|
|
262
|
+
let comments_before_end = format_comments_before_end(
|
|
263
|
+
ctx,
|
|
264
|
+
block_node.location.start_line,
|
|
265
|
+
block_node.location.end_line,
|
|
266
|
+
);
|
|
267
|
+
if !comments_before_end.is_empty() {
|
|
268
|
+
docs.push(indent(comments_before_end));
|
|
269
|
+
}
|
|
270
|
+
|
|
254
271
|
// Emit 'end'
|
|
255
272
|
docs.push(hardline());
|
|
256
273
|
docs.push(text("end"));
|
data/lib/rfmt/version.rb
CHANGED