Skip to content

Commit

Permalink
feat: format simple commented func-calls
Browse files Browse the repository at this point in the history
  • Loading branch information
QuadnucYard authored and Enter-tainer committed Dec 3, 2024
1 parent 501ef18 commit e6fee45
Show file tree
Hide file tree
Showing 59 changed files with 1,199 additions and 337 deletions.
6 changes: 2 additions & 4 deletions src/pretty/code_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,7 @@ impl<'a> PrettyPrinter<'a> {
.process_list(destructuring.to_untyped(), |node| {
self.convert_destructuring_item(node)
})
.always_fold_if(|| {
only_one_pattern && !has_comment_children(destructuring.to_untyped())
})
.always_fold_if(|| only_one_pattern)
.print_doc(ListStyle {
add_trailing_sep_single: only_one_pattern,
..Default::default()
Expand All @@ -121,7 +119,7 @@ impl<'a> PrettyPrinter<'a> {

ListStylist::new(self)
.process_list(params.to_untyped(), |node| self.convert_param(node))
.always_fold_if(|| is_single_simple && !has_comment_children(params.to_untyped()))
.always_fold_if(|| is_single_simple)
.print_doc(ListStyle {
omit_delim_single: is_single_simple,
..Default::default()
Expand Down
199 changes: 54 additions & 145 deletions src/pretty/func_call.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use itertools::Itertools;
use pretty::DocAllocator;
use typst_syntax::{ast::*, SyntaxKind, SyntaxNode};

use super::doc_ext::DocExt;
use super::{style::FoldStyle, PrettyPrinter};
use super::list::{ListStyle, ListStylist};
use super::mode::Mode;
use super::util::is_only_one_and;
use super::PrettyPrinter;

use super::{
table,
Expand All @@ -12,7 +14,7 @@ use super::{
};

#[derive(Debug)]
pub(super) enum ParenthesizedFuncCallArg<'a> {
enum ParenthesizedFuncCallArg<'a> {
Argument(Arg<'a>),
Comma,
Space,
Expand All @@ -21,45 +23,6 @@ pub(super) enum ParenthesizedFuncCallArg<'a> {
BlockComment(&'a SyntaxNode),
}

impl ParenthesizedFuncCallArg<'_> {
#[allow(unused)]
pub fn is_comment(&self) -> bool {
matches!(
self,
ParenthesizedFuncCallArg::LineComment(_) | ParenthesizedFuncCallArg::BlockComment(_)
)
}

pub fn is_function_call(&self) -> bool {
if let ParenthesizedFuncCallArg::Argument(arg) = self {
let inner = match arg {
Arg::Pos(p) => *p,
Arg::Named(n) => n.expr(),
Arg::Spread(s) => s.expr(),
};
matches!(inner, Expr::FuncCall(_))
} else {
false
}
}

#[allow(unused)]
pub fn is_newline(&self) -> Option<usize> {
if let ParenthesizedFuncCallArg::Newline(count) = self {
Some(*count)
} else {
None
}
}

pub fn is_trivial(&self) -> bool {
matches!(
self,
ParenthesizedFuncCallArg::Space | ParenthesizedFuncCallArg::Comma
)
}
}

impl<'a> ParenthesizedFuncCallArg<'a> {
pub fn into_doc(
self,
Expand All @@ -82,21 +45,26 @@ impl<'a> ParenthesizedFuncCallArg<'a> {

impl<'a> PrettyPrinter<'a> {
pub(super) fn convert_func_call(&'a self, func_call: FuncCall<'a>) -> ArenaDoc<'a> {
let mut doc = self.convert_expr(func_call.callee());
if let Some(res) = self.check_unformattable(func_call.args().to_untyped()) {
return doc + res;
self.convert_expr(func_call.callee())
+ self.convert_func_call_args(func_call, func_call.args())
}

fn convert_func_call_args(&'a self, func_call: FuncCall<'a>, args: Args<'a>) -> ArenaDoc<'a> {
if self.current_mode() == Mode::Math {
return self.format_disabled(args.to_untyped());
}
let has_parenthesized_args = has_parenthesized_args(func_call.args());
let mut doc = self.arena.nil();
let has_parenthesized_args = has_parenthesized_args(args);
if table::is_table(func_call) {
if let Some(cols) = table::is_formatable_table(func_call) {
doc += self.convert_table(func_call, cols);
} else if has_parenthesized_args {
doc += self.convert_parenthesized_args_as_is(func_call.args());
doc += self.convert_parenthesized_args_as_is(args);
}
} else if has_parenthesized_args {
doc += self.convert_parenthesized_args(func_call.args());
doc += self.convert_parenthesized_args(args);
};
doc + self.convert_additional_args(func_call.args(), has_parenthesized_args)
doc + self.convert_additional_args(args, has_parenthesized_args)
}

pub(super) fn convert_args(&'a self, args: Args<'a>) -> ArenaDoc<'a> {
Expand All @@ -110,55 +78,39 @@ impl<'a> PrettyPrinter<'a> {
}

pub(super) fn convert_parenthesized_args(&'a self, args: Args<'a>) -> ArenaDoc<'a> {
if let Some(res) = self.check_unformattable(args.to_untyped()) {
return res;
}
let args = parse_args(args).collect_vec();
let is_multiline = {
let mut is_multiline = false;
for arg in &args {
if let ParenthesizedFuncCallArg::Space = arg {
break;
}
if let ParenthesizedFuncCallArg::Newline(_) = arg {
is_multiline = true;
break;
// let always_fold = is_only_one_and(args.items(), |item| {
// matches!(item, Arg::Pos(Expr::Code(_)) | Arg::Pos(Expr::Content(_)))
// });
let arg_count = args
.to_untyped()
.children()
.take_while(|it| it.kind() != SyntaxKind::RightParen)
.filter(|it| it.is::<Arg>())
.count();
let always_fold = is_only_one_and(args.items().take(arg_count), |arg| {
let inner = match arg {
Arg::Pos(p) => *p,
Arg::Named(n) => n.expr(),
Arg::Spread(s) => s.expr(),
};
!matches!(inner, Expr::FuncCall(_))
});
let mut closed = false;
ListStylist::new(self)
.keep_linebreak(self.config.blank_lines_upper_bound)
.process_list_impl(args.to_untyped(), |child| {
// We should ignore additional args here.
if child.kind() == SyntaxKind::RightParen {
closed = true;
} else if !closed {
return child.cast().map(|arg| self.convert_arg(arg));
}
}
is_multiline
};
let prefer_tighter = {
let real_args = args
.iter()
.filter(|x| matches!(x, ParenthesizedFuncCallArg::Argument(_)))
.collect_vec();
real_args.is_empty() || (real_args.len() == 1 && !real_args[0].is_function_call())
};
let non_trivial_args = args.into_iter().filter(|x| !x.is_trivial());
let doc = if prefer_tighter {
non_trivial_args
.into_iter()
.find(|x| matches!(x, ParenthesizedFuncCallArg::Argument(_)))
.map(|x| x.into_doc(self, None))
.unwrap_or_else(|| self.arena.nil())
.parens()
} else {
// remove trailing newlines
let mut args = non_trivial_args.collect_vec();
while let Some(ParenthesizedFuncCallArg::Newline(_)) = args.last() {
args.pop();
}
comma_seprated_args(
self,
args.into_iter(),
if is_multiline {
FoldStyle::Never
} else {
FoldStyle::Fit
},
)
};
doc
Option::None
})
.always_fold_if(|| always_fold)
.print_doc(ListStyle {
..Default::default()
})
}

pub(super) fn convert_parenthesized_args_as_is(&'a self, args: Args<'a>) -> ArenaDoc<'a> {
Expand All @@ -167,9 +119,10 @@ impl<'a> PrettyPrinter<'a> {
inner.nest(2).parens()
}

/// Handle additional content blocks
fn convert_additional_args(&'a self, args: Args<'a>, has_paren: bool) -> ArenaDoc<'a> {
let node = args.to_untyped();
let args = node
let args = args
.to_untyped()
.children()
.skip_while(|node| {
if has_paren {
Expand All @@ -178,9 +131,9 @@ impl<'a> PrettyPrinter<'a> {
node.kind() != SyntaxKind::ContentBlock
}
})
.filter_map(|node| node.cast::<Arg>());
.filter_map(|node| node.cast::<ContentBlock>());
self.arena
.concat(args.map(|arg| self.convert_arg(arg)))
.concat(args.map(|arg| self.convert_content_block(arg)))
.group()
}

Expand Down Expand Up @@ -209,47 +162,3 @@ fn parse_args(args: Args<'_>) -> impl Iterator<Item = ParenthesizedFuncCallArg<'
_ => ParenthesizedFuncCallArg::Argument(node.cast::<Arg>().unwrap()),
})
}

fn comma_seprated_args<'a, I>(
pp: &'a PrettyPrinter<'a>,
args: I,
fold_style: FoldStyle,
) -> ArenaDoc<'a>
where
I: Iterator<Item = ParenthesizedFuncCallArg<'a>> + ExactSizeIterator,
{
if args.len() == 0 {
return pp.arena.nil().parens();
}
let format_inner = |sep: ArenaDoc<'a>, comma_: ArenaDoc<'a>| {
let mut inner = pp.arena.nil();
for (pos, arg) in args.with_position() {
let need_sep = matches!(arg, ParenthesizedFuncCallArg::Argument(_));
inner += arg.into_doc(pp, Some(1));
if matches!(
pos,
itertools::Position::First | itertools::Position::Middle
) && need_sep
{
inner += sep.clone();
}
}
inner + comma_
};
match fold_style {
FoldStyle::Fit | FoldStyle::Always => {
let comma_ = pp.arena.text(",").flat_alt(pp.arena.nil());
let sep = pp.arena.text(",") + pp.arena.line();
let inner = format_inner(sep, comma_);
((pp.arena.line_() + inner).nest(2) + pp.arena.line_())
.group()
.parens()
}
FoldStyle::Never => {
let sep = pp.arena.text(",") + pp.arena.hardline();
let comma_ = pp.arena.text(",");
let inner = format_inner(sep, comma_);
((pp.arena.hardline() + inner).nest(2) + pp.arena.hardline()).parens()
}
}
}
14 changes: 9 additions & 5 deletions src/pretty/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub struct ListStylist<'a> {
free_comments: Vec<ArenaDoc<'a>>,
items: Vec<Item<'a>>,
item_count: usize,
has_comment: bool,
has_line_comment: bool,
fold_style: FoldStyle,
disallow_front_comment: bool,
Expand Down Expand Up @@ -69,6 +70,7 @@ impl<'a> ListStylist<'a> {
free_comments: Default::default(),
items: Default::default(),
item_count: 0,
has_comment: false,
has_line_comment: false,
fold_style: FoldStyle::Fit,
disallow_front_comment: false,
Expand All @@ -91,8 +93,9 @@ impl<'a> ListStylist<'a> {
self
}

/// Force to fold if the predicate is true. Has no effect the list contains any comment.
pub fn always_fold_if(mut self, pred: impl FnOnce() -> bool) -> Self {
if pred() {
if !self.has_comment && pred() {
self.fold_style = FoldStyle::Always;
}
self
Expand Down Expand Up @@ -142,11 +145,11 @@ impl<'a> ListStylist<'a> {
if let Some(item_body) = item_checker(node) {
self.add_item(item_body);
} else {
self.meet_trivia(node);
self.process_trivia(node);
}
}

self.windup();
self.process_windup();

self
}
Expand All @@ -171,8 +174,9 @@ impl<'a> ListStylist<'a> {
}

/// Handle non-items.
fn meet_trivia(&mut self, node: &'a SyntaxNode) {
fn process_trivia(&mut self, node: &'a SyntaxNode) {
if is_comment_node(node) {
self.has_comment = true;
// Line comment cannot appear in single line block
if node.kind() == SyntaxKind::LineComment {
self.has_line_comment = true;
Expand All @@ -196,7 +200,7 @@ impl<'a> ListStylist<'a> {
}

/// Process remaining free comments and trailing lines.
fn windup(&mut self) {
fn process_windup(&mut self) {
self.attach_or_detach_comments();
while let Some(Item::Linebreak(_)) = self.items.last() {
self.items.pop();
Expand Down
1 change: 1 addition & 0 deletions src/pretty/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub(super) fn func_name(node: FuncCall<'_>) -> EcoString {
node.callee().to_untyped().clone().into_text()
}

/// Like `f()`, `f(x, y)`, not `f[]`
pub(super) fn has_parenthesized_args(node: Args<'_>) -> bool {
node.to_untyped()
.children()
Expand Down
13 changes: 13 additions & 0 deletions tests/assets/unit/code/func-call-last-arg.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#fun(aaaaaaaa, bbbbbbbbbb, () => {

})

#fun(aaaaaaaa, bbbbbbbbbb, () => {})

#fun(aaaaaaaa, bbbbbbbbbb, () => {
// something
})

#fun(aaaaaaaa, bbbbbbbbbb, {
// something
})
2 changes: 0 additions & 2 deletions tests/assets/unit/comment/comment-in-closure.typ
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
doc // all comments will be kept by typstyle
)={doc}

#set heading( /* 1 */ numbering /* 2 */:/* 3 */ (/* 4 */../* 5 */num/* 6 */) /* 1 */ => none)

#let f()/* 0 */=/* 1 */ ()=> /* 2 */none
#let g(..)/* 0 */ = /* 1 */ ()=> /* 2 */none
#let h(..)/* 0 */ =/* 1 */ ()=>/* 2 */ { none}
Expand Down
Loading

0 comments on commit e6fee45

Please sign in to comment.