Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions crates/common/src/comments/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,9 +235,9 @@ impl<'ast> CommentGatherer<'ast> {
let line_begin_pos = (line_begin_in_file - self.start_bpos).to_usize();
let mut col = CharPos(self.text[line_begin_pos..self.pos].chars().count());

// To preserve alignment in non-doc comments, normalize the block based on its
// least-indented line.
if !is_doc {
// To preserve alignment in multi-line non-doc comments, normalize the block based
// on its least-indented line.
if !is_doc && token_text.contains('\n') {
col = token_text.lines().skip(1).fold(col, |min, line| {
if line.is_empty() {
return min;
Expand Down
95 changes: 63 additions & 32 deletions crates/fmt/src/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,52 +584,83 @@ impl<'sess> State<'sess, '_> {
return;
}

let post_break_prefix = |prefix: &'static str, line_len: usize| -> &'static str {
fn post_break_prefix(prefix: &'static str, has_content: bool) -> &'static str {
if !has_content {
return prefix;
}
match prefix {
"///" if line_len > 3 => "/// ",
"//" if line_len > 2 => "// ",
"/*" if line_len > 2 => "/* ",
" *" if line_len > 2 => " * ",
"///" => "/// ",
"//" => "// ",
"/*" => "/* ",
" *" => " * ",
_ => prefix,
}
};
}

self.ibox(0);
let (prefix, content) = if is_doc {
// Doc comments preserve leading whitespaces (right after the prefix).
self.word(prefix);
let content = &line[prefix.len()..];
let (leading_ws, rest) =
content.split_at(content.chars().take_while(|&c| c.is_whitespace()).count());
self.word(prefix);

let content = &line[prefix.len()..];
let content = if is_doc {
// Doc comments preserve leading whitespaces (right after the prefix) as nbps.
let ws_len = content
.char_indices()
.take_while(|(_, c)| c.is_whitespace())
.last()
.map_or(0, |(idx, c)| idx + c.len_utf8());
let (leading_ws, rest) = content.split_at(ws_len);
if !leading_ws.is_empty() {
self.word(leading_ws.to_owned());
}
let prefix = post_break_prefix(prefix, rest.len());
(prefix, rest)
rest
} else {
let content = line[prefix.len()..].trim();
let prefix = post_break_prefix(prefix, content.len());
self.word(prefix);
(prefix, content)
};

// Split the rest of the content into words.
let mut words = content.split_whitespace().peekable();
while let Some(word) = words.next() {
self.word(word.to_owned());
if let Some(next_word) = words.peek() {
if *next_word == "*/" {
// Non-doc comments: replace first whitespace with nbsp, rest of content continues
if let Some(first_char) = content.chars().next() {
if first_char.is_whitespace() {
self.nbsp();
&content[first_char.len_utf8()..]
} else {
self.s.scan_break(BreakToken {
offset: break_offset,
blank_space: 1,
post_break: if matches!(prefix, "/* ") { None } else { Some(prefix) },
..Default::default()
});
content
}
} else {
""
}
};

let post_break = post_break_prefix(prefix, !content.is_empty());

// Process content character by character to preserve consecutive whitespaces
let (mut chars, mut current_word) = (content.chars().peekable(), String::new());
while let Some(ch) = chars.next() {
if ch.is_whitespace() {
// Print current word
if !current_word.is_empty() {
self.word(std::mem::take(&mut current_word));
}

// Preserve multiple spaces while adding a single break
let mut ws_count = 1;
while chars.peek().is_some_and(|c| c.is_whitespace()) {
ws_count += 1;
chars.next();
}
self.s.scan_break(BreakToken {
offset: break_offset,
blank_space: ws_count,
post_break: if post_break.starts_with("/*") { None } else { Some(post_break) },
..Default::default()
});
continue;
}

current_word.push(ch);
}

// Print final word
if !current_word.is_empty() {
self.word(current_word);
}

self.end();
}

Expand Down
8 changes: 8 additions & 0 deletions crates/fmt/testdata/SimpleComments/fmt.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
contract SimpleComments {
//´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:
// VARIABLES
//.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•

mapping(address /* asset */ => address /* router */) public router;

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

constructor() {
// TODO: do this and that

Expand Down
7 changes: 7 additions & 0 deletions crates/fmt/testdata/SimpleComments/original.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
contract SimpleComments {
//´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:
// VARIABLES
//.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•

mapping(address /* asset */ => address /* router */) public router;

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* FUNCTIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

constructor() {
// TODO: do this and that
Expand Down
9 changes: 9 additions & 0 deletions crates/fmt/testdata/SimpleComments/wrap-comments.fmt.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
// config: line_length = 60
// config: wrap_comments = true
contract SimpleComments {
//´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:
// VARIABLES
//.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•

mapping(address /* asset */ => address /* router */)
public router;

/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* FUNCTIONS
*/
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

constructor() {
// TODO: do this and that

Expand Down
Loading