Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
34 changes: 31 additions & 3 deletions crates/fmt/src/state/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,36 @@ use solar::parse::{
};
use std::{borrow::Cow, fmt::Debug};

pub(crate) trait LitExt<'ast> {
fn is_str_concatenation(&self) -> bool;
}

impl<'ast> LitExt<'ast> for ast::Lit<'ast> {
/// Checks if a the input literal is a string literal with comma-separated parts.
///
/// This heuristic is a strong indicator of string concatenation, such as:
/// `string memory s = "a," "b," "c";`
fn is_str_concatenation(&self) -> bool {
if !matches!(self.kind, ast::LitKind::Str(..)) {
return false;
}

let mut has_parts = false;
let mut iter = self.literals().peekable();
while let Some((_span, symbol)) = iter.next() {
if iter.peek().is_some() {
has_parts = true;

if !symbol.as_str().contains(',') {
return false;
}
}
}

has_parts
}
}

/// Language-specific pretty printing. Common for both: Solidity + Yul.
impl<'ast> State<'_, 'ast> {
pub(super) fn print_lit(&mut self, lit: &'ast ast::Lit<'ast>) {
Expand All @@ -20,9 +50,8 @@ impl<'ast> State<'_, 'ast> {

match *kind {
ast::LitKind::Str(kind, ..) => {
self.cbox(0);
self.s.ibox(if lit.is_str_concatenation() { self.ind } else { 0 });
for (pos, (span, symbol)) in lit.literals().delimited() {
self.ibox(0);
if !self.handle_span(span, false) {
let quote_pos = span.lo() + kind.prefix().len() as u32;
self.print_str_lit(kind, quote_pos, symbol.as_str());
Expand All @@ -34,7 +63,6 @@ impl<'ast> State<'_, 'ast> {
} else {
self.neverbreak();
}
self.end();
}
self.end();
}
Expand Down
15 changes: 11 additions & 4 deletions crates/fmt/src/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub(super) struct State<'sess, 'ast> {
binary_expr: bool,
member_expr: bool,
var_init: bool,
fn_body: bool,
}

impl std::ops::Deref for State<'_, '_> {
Expand Down Expand Up @@ -121,6 +122,7 @@ impl<'sess> State<'sess, '_> {
binary_expr: false,
member_expr: false,
var_init: false,
fn_body: false,
}
}

Expand Down Expand Up @@ -320,7 +322,11 @@ impl<'sess> State<'sess, '_> {
if cmnt.style.is_blank() {
match config.skip_blanks {
Some(Skip::All) => continue,
Some(Skip::Leading) if is_leading => continue,
Some(Skip::Leading { resetable })
if is_leading || (!resetable && last_style.is_none()) =>
{
continue;
}
Some(Skip::Trailing) => {
buffered_blank = Some(cmnt);
continue;
Expand Down Expand Up @@ -647,8 +653,9 @@ impl<'sess> State<'sess, '_> {
#[derive(Clone, Copy)]
enum Skip {
All,
Leading,
Leading { resetable: bool },
Trailing,
LeadingNoReset,
}

#[derive(Default, Clone, Copy)]
Expand All @@ -670,8 +677,8 @@ impl CommentConfig {
Self { skip_blanks: Some(Skip::All), ..Default::default() }
}

pub(crate) fn skip_leading_ws() -> Self {
Self { skip_blanks: Some(Skip::Leading), ..Default::default() }
pub(crate) fn skip_leading_ws(resetable: bool) -> Self {
Self { skip_blanks: Some(Skip::Leading { resetable }), ..Default::default() }
}

pub(crate) fn skip_trailing_ws() -> Self {
Expand Down
Loading
Loading