Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 8506f5e

Browse files
authored
Merge pull request rust-lang#3159 from scampi/issue-3132
The method trim_left_preserve_layout didn't handle tabs properly.
2 parents 2c471a5 + 2d718a3 commit 8506f5e

File tree

5 files changed

+147
-143
lines changed

5 files changed

+147
-143
lines changed

src/comment.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ fn identify_comment(
332332
let (first_group, rest) = orig.split_at(first_group_ending);
333333
let rewritten_first_group =
334334
if !config.normalize_comments() && has_bare_lines && style.is_block_comment() {
335-
trim_left_preserve_layout(first_group, &shape.indent, config)
335+
trim_left_preserve_layout(first_group, &shape.indent, config)?
336336
} else if !config.normalize_comments()
337337
&& !config.wrap_comments()
338338
&& !config.format_doc_comments()

src/macros.rs

Lines changed: 5 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,10 @@ use rewrite::{Rewrite, RewriteContext};
4040
use shape::{Indent, Shape};
4141
use source_map::SpanUtils;
4242
use spanned::Spanned;
43-
use utils::{format_visibility, mk_sp, remove_trailing_white_spaces, rewrite_ident, wrap_str};
43+
use utils::{
44+
format_visibility, is_empty_line, mk_sp, remove_trailing_white_spaces, rewrite_ident,
45+
trim_left_preserve_layout, wrap_str,
46+
};
4447
use visitor::FmtVisitor;
4548

4649
const FORCED_BRACKET_MACROS: &[&str] = &["vec!"];
@@ -373,7 +376,7 @@ pub fn rewrite_macro_inner(
373376
}
374377
DelimToken::Brace => {
375378
// Skip macro invocations with braces, for now.
376-
indent_macro_snippet(context, context.snippet(mac.span), shape.indent)
379+
trim_left_preserve_layout(context.snippet(mac.span), &shape.indent, &context.config)
377380
}
378381
_ => unreachable!(),
379382
}
@@ -1101,108 +1104,6 @@ fn macro_style(mac: &ast::Mac, context: &RewriteContext) -> DelimToken {
11011104
}
11021105
}
11031106

1104-
/// Indent each line according to the specified `indent`.
1105-
/// e.g.
1106-
///
1107-
/// ```rust,ignore
1108-
/// foo!{
1109-
/// x,
1110-
/// y,
1111-
/// foo(
1112-
/// a,
1113-
/// b,
1114-
/// c,
1115-
/// ),
1116-
/// }
1117-
/// ```
1118-
///
1119-
/// will become
1120-
///
1121-
/// ```rust,ignore
1122-
/// foo!{
1123-
/// x,
1124-
/// y,
1125-
/// foo(
1126-
/// a,
1127-
/// b,
1128-
/// c,
1129-
/// ),
1130-
/// }
1131-
/// ```
1132-
fn indent_macro_snippet(
1133-
context: &RewriteContext,
1134-
macro_str: &str,
1135-
indent: Indent,
1136-
) -> Option<String> {
1137-
let mut lines = LineClasses::new(macro_str);
1138-
let first_line = lines.next().map(|(_, s)| s.trim_right().to_owned())?;
1139-
let mut trimmed_lines = Vec::with_capacity(16);
1140-
1141-
let mut veto_trim = false;
1142-
let min_prefix_space_width = lines
1143-
.filter_map(|(kind, line)| {
1144-
let mut trimmed = true;
1145-
let prefix_space_width = if is_empty_line(&line) {
1146-
None
1147-
} else {
1148-
Some(get_prefix_space_width(context, &line))
1149-
};
1150-
1151-
let line = if veto_trim || (kind.is_string() && !line.ends_with('\\')) {
1152-
veto_trim = kind.is_string() && !line.ends_with('\\');
1153-
trimmed = false;
1154-
line
1155-
} else {
1156-
line.trim().to_owned()
1157-
};
1158-
trimmed_lines.push((trimmed, line, prefix_space_width));
1159-
1160-
// when computing the minimum, do not consider lines within a string
1161-
match kind {
1162-
FullCodeCharKind::InString | FullCodeCharKind::EndString => None,
1163-
_ => prefix_space_width,
1164-
}
1165-
})
1166-
.min()?;
1167-
1168-
Some(
1169-
first_line
1170-
+ "\n"
1171-
+ &trimmed_lines
1172-
.iter()
1173-
.map(
1174-
|&(trimmed, ref line, prefix_space_width)| match prefix_space_width {
1175-
_ if !trimmed => line.to_owned(),
1176-
Some(original_indent_width) => {
1177-
let new_indent_width = indent.width()
1178-
+ original_indent_width.saturating_sub(min_prefix_space_width);
1179-
let new_indent = Indent::from_width(context.config, new_indent_width);
1180-
format!("{}{}", new_indent.to_string(context.config), line)
1181-
}
1182-
None => String::new(),
1183-
},
1184-
)
1185-
.collect::<Vec<_>>()
1186-
.join("\n"),
1187-
)
1188-
}
1189-
1190-
fn get_prefix_space_width(context: &RewriteContext, s: &str) -> usize {
1191-
let mut width = 0;
1192-
for c in s.chars() {
1193-
match c {
1194-
' ' => width += 1,
1195-
'\t' => width += context.config.tab_spaces(),
1196-
_ => return width,
1197-
}
1198-
}
1199-
width
1200-
}
1201-
1202-
fn is_empty_line(s: &str) -> bool {
1203-
s.is_empty() || s.chars().all(char::is_whitespace)
1204-
}
1205-
12061107
// A very simple parser that just parses a macros 2.0 definition into its branches.
12071108
// Currently we do not attempt to parse any further than that.
12081109
#[derive(new)]

src/utils.rs

Lines changed: 115 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use syntax::ast::{
2020
use syntax::ptr;
2121
use syntax::source_map::{BytePos, Span, NO_EXPANSION};
2222

23-
use comment::{filter_normal_code, CharClasses, FullCodeCharKind};
23+
use comment::{filter_normal_code, CharClasses, FullCodeCharKind, LineClasses};
2424
use config::Config;
2525
use rewrite::RewriteContext;
2626
use shape::{Indent, Shape};
@@ -483,46 +483,123 @@ pub fn remove_trailing_white_spaces(text: &str) -> String {
483483
buffer
484484
}
485485

486-
/// Trims a minimum of leading whitespaces so that the content layout is kept and aligns to indent.
487-
pub fn trim_left_preserve_layout(orig: &str, indent: &Indent, config: &Config) -> String {
488-
let prefix_whitespace_min = orig
489-
.lines()
490-
// skip the line with the starting sigil since the leading whitespace is removed
491-
// otherwise, the minimum would always be zero
492-
.skip(1)
493-
.filter(|line| !line.is_empty())
494-
.map(|line| {
495-
let mut width = 0;
496-
for c in line.chars() {
497-
match c {
498-
' ' => width += 1,
499-
'\t' => width += config.tab_spaces(),
500-
_ => break,
501-
}
502-
}
503-
width
504-
})
505-
.min()
506-
.unwrap_or(0);
507-
508-
let indent_str = indent.to_string(config);
509-
let mut lines = orig.lines();
510-
let first_line = lines.next().unwrap();
511-
let rest = lines
512-
.map(|line| {
513-
if line.is_empty() {
514-
String::from("\n")
486+
/// Indent each line according to the specified `indent`.
487+
/// e.g.
488+
///
489+
/// ```rust,ignore
490+
/// foo!{
491+
/// x,
492+
/// y,
493+
/// foo(
494+
/// a,
495+
/// b,
496+
/// c,
497+
/// ),
498+
/// }
499+
/// ```
500+
///
501+
/// will become
502+
///
503+
/// ```rust,ignore
504+
/// foo!{
505+
/// x,
506+
/// y,
507+
/// foo(
508+
/// a,
509+
/// b,
510+
/// c,
511+
/// ),
512+
/// }
513+
/// ```
514+
pub fn trim_left_preserve_layout(orig: &str, indent: &Indent, config: &Config) -> Option<String> {
515+
let mut lines = LineClasses::new(orig);
516+
let first_line = lines.next().map(|(_, s)| s.trim_right().to_owned())?;
517+
let mut trimmed_lines = Vec::with_capacity(16);
518+
519+
let mut veto_trim = false;
520+
let min_prefix_space_width = lines
521+
.filter_map(|(kind, line)| {
522+
let mut trimmed = true;
523+
let prefix_space_width = if is_empty_line(&line) {
524+
None
515525
} else {
516-
format!("\n{}{}", indent_str, &line[prefix_whitespace_min..])
526+
Some(get_prefix_space_width(config, &line))
527+
};
528+
529+
let line = if veto_trim || (kind.is_string() && !line.ends_with('\\')) {
530+
veto_trim = kind.is_string() && !line.ends_with('\\');
531+
trimmed = false;
532+
line
533+
} else {
534+
line.trim().to_owned()
535+
};
536+
trimmed_lines.push((trimmed, line, prefix_space_width));
537+
538+
// When computing the minimum, do not consider lines within a string.
539+
// The reason is there is a veto against trimming and indenting such lines
540+
match kind {
541+
FullCodeCharKind::InString | FullCodeCharKind::EndString => None,
542+
_ => prefix_space_width,
517543
}
518544
})
519-
.collect::<Vec<String>>()
520-
.concat();
521-
format!("{}{}", first_line, rest)
545+
.min()?;
546+
547+
Some(
548+
first_line
549+
+ "\n"
550+
+ &trimmed_lines
551+
.iter()
552+
.map(
553+
|&(trimmed, ref line, prefix_space_width)| match prefix_space_width {
554+
_ if !trimmed => line.to_owned(),
555+
Some(original_indent_width) => {
556+
let new_indent_width = indent.width()
557+
+ original_indent_width.saturating_sub(min_prefix_space_width);
558+
let new_indent = Indent::from_width(config, new_indent_width);
559+
format!("{}{}", new_indent.to_string(config), line)
560+
}
561+
None => String::new(),
562+
},
563+
)
564+
.collect::<Vec<_>>()
565+
.join("\n"),
566+
)
567+
}
568+
569+
pub fn is_empty_line(s: &str) -> bool {
570+
s.is_empty() || s.chars().all(char::is_whitespace)
571+
}
572+
573+
fn get_prefix_space_width(config: &Config, s: &str) -> usize {
574+
let mut width = 0;
575+
for c in s.chars() {
576+
match c {
577+
' ' => width += 1,
578+
'\t' => width += config.tab_spaces(),
579+
_ => return width,
580+
}
581+
}
582+
width
522583
}
523584

524-
#[test]
525-
fn test_remove_trailing_white_spaces() {
526-
let s = " r#\"\n test\n \"#";
527-
assert_eq!(remove_trailing_white_spaces(&s), s);
585+
#[cfg(test)]
586+
mod test {
587+
use super::*;
588+
589+
#[test]
590+
fn test_remove_trailing_white_spaces() {
591+
let s = " r#\"\n test\n \"#";
592+
assert_eq!(remove_trailing_white_spaces(&s), s);
593+
}
594+
595+
#[test]
596+
fn test_trim_left_preserve_layout() {
597+
let s = "aaa\n\tbbb\n ccc";
598+
let config = Config::default();
599+
let indent = Indent::new(4, 0);
600+
assert_eq!(
601+
trim_left_preserve_layout(&s, &indent, &config),
602+
Some("aaa\n bbb\n ccc".to_string())
603+
);
604+
}
528605
}

tests/source/issue-3132.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
fn test() {
2+
/*
3+
a
4+
*/
5+
let x = 42;
6+
/*
7+
aaa
8+
"line 1
9+
line 2
10+
line 3"
11+
*/
12+
let x = 42;
13+
}

tests/target/issue-3132.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
fn test() {
2+
/*
3+
a
4+
*/
5+
let x = 42;
6+
/*
7+
aaa
8+
"line 1
9+
line 2
10+
line 3"
11+
*/
12+
let x = 42;
13+
}

0 commit comments

Comments
 (0)