rfmt 1.6.1 → 1.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 327f57f105df77a0d6b77bda9ab87c4f234f03f23fb918d74679942456c5a8ec
4
- data.tar.gz: 482a5406275e422fb306970ffb47708f37be0c3115bc92485996d2adde6c9463
3
+ metadata.gz: 5f1d0fa5ba56d10568e9dd3625974abc815350c4c6c2ff03d5114d8c38aae9a1
4
+ data.tar.gz: 4b6256a168e7be4a70c162a26bea0983fd6397397224cd1953abbe9cc1cbd01b
5
5
  SHA512:
6
- metadata.gz: 52982745650ed439f2ac7a5a1a9a26ae2dcf9e9df8015526278f58508929e15a1d72425d2d3c5963c0a4008968e20fcb6c374881122a3e99618d2cef15434eaf
7
- data.tar.gz: 10e8282f05caacdffcbb2d7042e9f95a08c5d08c9b081307a12752840549fbb08b6bc557a256a9a46fd012986369fd7cfd0442a46d1d81146c2fb234d0b936f7
6
+ metadata.gz: a90b0b5f699c21e3e5aebd5f33ce71c152694a3cc51c812d2ffda946d2250a7714ae572ab9e7538531386e09b91210aa50f5e4de49e1d11234afd6803525ede4
7
+ data.tar.gz: ab5588dfe3b5035e444219ad218756b475d964f18b47fdb78311593ba964c7c4436856abca58a13fc2e799eaa7d85815e6e70be39e6d0e083d9c7951c3867553
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.6.2] - 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
+
3
7
  ## [1.6.1] - 2026-04-24
4
8
 
5
9
  Follow-up release consolidating the 1.6.0 architecture work.
data/Cargo.lock CHANGED
@@ -1251,7 +1251,7 @@ checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c"
1251
1251
 
1252
1252
  [[package]]
1253
1253
  name = "rfmt"
1254
- version = "1.6.1"
1254
+ version = "1.6.2"
1255
1255
  dependencies = [
1256
1256
  "anyhow",
1257
1257
  "clap",
data/ext/rfmt/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "rfmt"
3
- version = "1.6.1"
3
+ version = "1.6.2"
4
4
  edition = "2021"
5
5
  authors = ["fujitani sora <fujitanisora0414@gmail.com>"]
6
6
  license = "MIT"
@@ -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
- // Only add newlines if not the first comment or if there's a gap
304
- if !is_first || gap > 0 {
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
  }
@@ -499,6 +528,27 @@ pub fn reformat_chain_lines(
499
528
  Cow::Owned(result)
500
529
  }
501
530
 
531
+ /// Returns true when the given 1-based `line` in `source` contains only
532
+ /// whitespace (or is empty). Returns false for any line that has code or
533
+ /// a comment.
534
+ fn is_line_blank(source: &str, line: usize) -> bool {
535
+ let mut current = 1usize;
536
+ let mut line_start = 0usize;
537
+ for (i, b) in source.bytes().enumerate() {
538
+ if current == line {
539
+ let end = source[i..].find('\n').map_or(source.len(), |n| i + n);
540
+ return source[line_start..end]
541
+ .bytes()
542
+ .all(|b| b == b' ' || b == b'\t' || b == b'\r');
543
+ }
544
+ if b == b'\n' {
545
+ current += 1;
546
+ line_start = i + 1;
547
+ }
548
+ }
549
+ false
550
+ }
551
+
502
552
  /// Removes at most one trailing `\n` (optionally preceded by a single `\r`)
503
553
  /// from `s`. Spaces, tabs, and any additional preceding newlines are
504
554
  /// 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, format_trailing_comment,
17
- line_leading_indent, mark_comments_in_range_emitted, reformat_chain_lines,
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rfmt
4
- VERSION = '1.6.1'
4
+ VERSION = '1.6.2'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rfmt
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.1
4
+ version: 1.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - fujitani sora