Skip to content

Commit 54b7d19

Browse files
committed
fix some performance issue
1 parent 5d4bcf7 commit 54b7d19

6 files changed

Lines changed: 655 additions & 196 deletions

File tree

crates/emmylua_formatter/src/formatter/expr.rs

Lines changed: 132 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -297,9 +297,12 @@ fn format_index_expr(
297297
.map(|prefix| format_expr(ctx, plan, &prefix))
298298
.unwrap_or_default();
299299
let access_docs = format_index_access_ir(ctx, plan, expr);
300-
let indent_tail = expr
301-
.get_index_token()
302-
.is_some_and(|token| token.is_dot() || token.is_colon());
300+
let indent_tail = matches!(
301+
access_docs.first(),
302+
Some(DocIR::SyntaxToken(
303+
LuaTokenKind::TkDot | LuaTokenKind::TkColon
304+
))
305+
);
303306

304307
let bridge = analyze_expr_bridge(expr.syntax());
305308
if bridge.has_line_break || !bridge.comment_fragments.is_empty() {
@@ -349,6 +352,12 @@ pub fn format_param_list_ir(
349352
)
350353
}
351354

355+
enum CompactCallArgListAttempt {
356+
Formatted(Vec<DocIR>),
357+
ReuseDocs(Vec<Vec<DocIR>>),
358+
CommentsPresent,
359+
}
360+
352361
#[derive(Default)]
353362
struct CollectedParamEntries {
354363
entries: Vec<ParamEntry>,
@@ -665,10 +674,6 @@ fn format_call_arg_list(
665674
}
666675

667676
let preserve_multiline_args = args_list.syntax().text().contains_char('\n');
668-
let first_arg_is_multiline_table = matches!(
669-
args.first(),
670-
Some(LuaExpr::TableExpr(table)) if table.syntax().text().contains_char('\n')
671-
);
672677
let attach_first_arg = preserve_multiline_args && should_attach_first_call_arg(&args);
673678
let arg_docs: Vec<Vec<DocIR>> = args
674679
.iter()
@@ -684,6 +689,22 @@ fn format_call_arg_list(
684689
)
685690
})
686691
.collect();
692+
693+
format_call_arg_list_from_docs(ctx, plan, args_list, &args, attach_first_arg, arg_docs)
694+
}
695+
696+
fn format_call_arg_list_from_docs(
697+
ctx: &FormatContext,
698+
plan: &RootFormatPlan,
699+
args_list: &LuaCallArgList,
700+
args: &[LuaExpr],
701+
attach_first_arg: bool,
702+
arg_docs: Vec<Vec<DocIR>>,
703+
) -> Vec<DocIR> {
704+
let first_arg_is_multiline_table = matches!(
705+
args.first(),
706+
Some(LuaExpr::TableExpr(table)) if table.syntax().text().contains_char('\n')
707+
);
687708
let (open, close) = paren_tokens(args_list.syntax());
688709
let comma = first_direct_token(args_list.syntax(), LuaTokenKind::TkComma);
689710
let layout_plan = expr_sequence_layout_plan(plan, args_list.syntax());
@@ -770,22 +791,23 @@ fn format_call_args_with_inline_block_item(
770791
close: DocIR,
771792
comma: Option<&LuaSyntaxToken>,
772793
) -> Vec<DocIR> {
773-
let prefix_items = arg_docs[..block_index].to_vec();
774-
let block_item = arg_docs[block_index].clone();
775-
let tail_items = arg_docs[block_index + 1..].to_vec();
794+
let prefix_items = &arg_docs[..block_index];
795+
let block_item = &arg_docs[block_index];
796+
let tail_items = &arg_docs[block_index + 1..];
776797

777798
let mut anchored_docs = vec![open.clone()];
778799
if !prefix_items.is_empty() {
779-
anchored_docs.extend(ir::intersperse(
800+
append_docs_with_separator(
801+
&mut anchored_docs,
780802
prefix_items,
781-
comma_flat_separator(plan, comma),
782-
));
803+
&comma_flat_separator(plan, comma),
804+
);
783805
anchored_docs.extend(comma_flat_separator(plan, comma));
784806
}
785-
anchored_docs.extend(block_item);
807+
anchored_docs.extend(block_item.clone());
786808
if !tail_items.is_empty() {
787809
anchored_docs.push(ir::indent(vec![ir::fill(
788-
build_fill_parts_with_leading_separator(&tail_items, &comma_fill_separator(comma)),
810+
build_fill_parts_with_leading_separator(tail_items, &comma_fill_separator(comma)),
789811
)]));
790812
anchored_docs.push(ir::hard_line());
791813
anchored_docs.push(close.clone());
@@ -794,22 +816,7 @@ fn format_call_args_with_inline_block_item(
794816
}
795817

796818
let anchored = vec![ir::group_break(anchored_docs)];
797-
798-
let mut one_per_line_inner = Vec::new();
799-
for (index, item_docs) in arg_docs.iter().enumerate() {
800-
one_per_line_inner.push(ir::hard_line());
801-
one_per_line_inner.extend(item_docs.clone());
802-
if index + 1 < arg_docs.len() {
803-
one_per_line_inner.extend(comma_token_docs(comma));
804-
}
805-
}
806-
807-
let one_per_line = vec![ir::group_break(vec![
808-
open,
809-
ir::indent(one_per_line_inner),
810-
ir::hard_line(),
811-
close,
812-
])];
819+
let one_per_line = build_one_per_line_call_args(&arg_docs, open, close, comma);
813820

814821
choose_sequence_layout(
815822
ctx,
@@ -875,22 +882,7 @@ fn format_call_args_with_block_items(
875882
ir::hard_line(),
876883
close.clone(),
877884
])];
878-
879-
let mut one_per_line_inner = Vec::new();
880-
for (index, item_docs) in arg_docs.iter().enumerate() {
881-
one_per_line_inner.push(ir::hard_line());
882-
one_per_line_inner.extend(item_docs.clone());
883-
if index + 1 < arg_docs.len() {
884-
one_per_line_inner.extend(comma_token_docs(comma));
885-
}
886-
}
887-
888-
let one_per_line = vec![ir::group_break(vec![
889-
open,
890-
ir::indent(one_per_line_inner),
891-
ir::hard_line(),
892-
close,
893-
])];
885+
let one_per_line = build_one_per_line_call_args(&arg_docs, open, close, comma);
894886

895887
choose_sequence_layout(
896888
ctx,
@@ -924,6 +916,30 @@ fn render_blocked_call_arg_chunk(items_with_trailing: Vec<Vec<DocIR>>) -> Vec<Do
924916
vec![ir::fill(parts)]
925917
}
926918

919+
fn build_one_per_line_call_args(
920+
arg_docs: &[Vec<DocIR>],
921+
open: DocIR,
922+
close: DocIR,
923+
comma: Option<&LuaSyntaxToken>,
924+
) -> Vec<DocIR> {
925+
let item_count = arg_docs.len();
926+
let mut one_per_line_inner = Vec::new();
927+
for (index, item_docs) in arg_docs.iter().enumerate() {
928+
one_per_line_inner.push(ir::hard_line());
929+
one_per_line_inner.extend(item_docs.clone());
930+
if index + 1 < item_count {
931+
one_per_line_inner.extend(comma_token_docs(comma));
932+
}
933+
}
934+
935+
vec![ir::group_break(vec![
936+
open,
937+
ir::indent(one_per_line_inner),
938+
ir::hard_line(),
939+
close,
940+
])]
941+
}
942+
927943
fn should_attach_first_call_arg(args: &[LuaExpr]) -> bool {
928944
matches!(
929945
args.first(),
@@ -976,8 +992,9 @@ fn format_call_args_with_attached_first_arg(
976992
return docs;
977993
}
978994

979-
let first_arg = arg_docs[0].clone();
980-
let tail_items = arg_docs[1..].to_vec();
995+
let Some((first_arg, tail_items)) = arg_docs.split_first() else {
996+
return vec![open, close];
997+
};
981998
let tail_is_single_line = allow_inline_tail_after_first_arg
982999
&& tail_items
9831000
.iter()
@@ -987,10 +1004,7 @@ fn format_call_args_with_attached_first_arg(
9871004
let mut docs = vec![open.clone()];
9881005
docs.extend(first_arg.clone());
9891006
docs.extend(comma_flat_separator(plan, comma));
990-
docs.extend(ir::intersperse(
991-
tail_items.clone(),
992-
comma_flat_separator(plan, comma),
993-
));
1007+
append_docs_with_separator(&mut docs, tail_items, &comma_flat_separator(plan, comma));
9941008
docs.push(close.clone());
9951009
docs
9961010
});
@@ -1000,31 +1014,18 @@ fn format_call_args_with_attached_first_arg(
10001014
fill_docs.extend(comma_token_docs(comma));
10011015
fill_docs.push(ir::indent(vec![
10021016
ir::soft_line(),
1003-
ir::fill(build_fill_parts(&tail_items, &comma_fill_separator(comma))),
1017+
ir::fill(build_fill_parts(tail_items, &comma_fill_separator(comma))),
10041018
]));
10051019
fill_docs.push(close.clone());
10061020

1007-
let mut one_per_line_docs = vec![open];
1008-
one_per_line_docs.extend(first_arg);
1009-
one_per_line_docs.extend(comma_token_docs(comma));
1010-
let mut rest = Vec::new();
1011-
for (index, item_docs) in tail_items.iter().enumerate() {
1012-
rest.push(ir::hard_line());
1013-
rest.extend(item_docs.clone());
1014-
if index + 1 < tail_items.len() {
1015-
rest.extend(comma_token_docs(comma));
1016-
}
1017-
}
1018-
one_per_line_docs.push(ir::indent(rest));
1019-
one_per_line_docs.push(ir::hard_line());
1020-
one_per_line_docs.push(close);
1021-
10221021
choose_sequence_layout(
10231022
ctx,
10241023
SequenceLayoutCandidates {
10251024
flat,
10261025
fill: Some(vec![ir::group(fill_docs)]),
1027-
one_per_line: Some(vec![ir::group_break(one_per_line_docs)]),
1026+
one_per_line: Some(build_attached_first_arg_one_per_line(
1027+
&arg_docs, open, close, comma,
1028+
)),
10281029
..Default::default()
10291030
},
10301031
SequenceLayoutPolicy {
@@ -1036,6 +1037,45 @@ fn format_call_args_with_attached_first_arg(
10361037
)
10371038
}
10381039

1040+
fn build_attached_first_arg_one_per_line(
1041+
arg_docs: &[Vec<DocIR>],
1042+
open: DocIR,
1043+
close: DocIR,
1044+
comma: Option<&LuaSyntaxToken>,
1045+
) -> Vec<DocIR> {
1046+
let mut one_per_line_docs = vec![open];
1047+
let Some((first_arg, rest_args)) = arg_docs.split_first() else {
1048+
one_per_line_docs.push(close);
1049+
return vec![ir::group_break(one_per_line_docs)];
1050+
};
1051+
one_per_line_docs.extend(first_arg.clone());
1052+
one_per_line_docs.extend(comma_token_docs(comma));
1053+
1054+
let rest_count = rest_args.len();
1055+
let mut rest = Vec::new();
1056+
for (index, item_docs) in rest_args.iter().enumerate() {
1057+
rest.push(ir::hard_line());
1058+
rest.extend(item_docs.clone());
1059+
if index + 1 < rest_count {
1060+
rest.extend(comma_token_docs(comma));
1061+
}
1062+
}
1063+
1064+
one_per_line_docs.push(ir::indent(rest));
1065+
one_per_line_docs.push(ir::hard_line());
1066+
one_per_line_docs.push(close);
1067+
vec![ir::group_break(one_per_line_docs)]
1068+
}
1069+
1070+
fn append_docs_with_separator(docs: &mut Vec<DocIR>, items: &[Vec<DocIR>], separator: &[DocIR]) {
1071+
for (index, item_docs) in items.iter().enumerate() {
1072+
if index > 0 {
1073+
docs.extend(separator.to_vec());
1074+
}
1075+
docs.extend(item_docs.clone());
1076+
}
1077+
}
1078+
10391079
#[derive(Default)]
10401080
struct CollectedCallArgEntries {
10411081
entries: Vec<CallArgEntry>,
@@ -2438,8 +2478,23 @@ fn format_call_suffix_ir(
24382478
}
24392479

24402480
let docs = if !args_list.syntax().text().contains_char('\n') {
2441-
format_compact_call_arg_list(ctx, plan, &args_list)
2442-
.unwrap_or_else(|| format_call_arg_list(ctx, plan, &args_list))
2481+
match format_compact_call_arg_list(ctx, plan, &args_list, &args) {
2482+
CompactCallArgListAttempt::Formatted(docs) => docs,
2483+
CompactCallArgListAttempt::ReuseDocs(arg_docs) => {
2484+
let attach_first_arg = false;
2485+
format_call_arg_list_from_docs(
2486+
ctx,
2487+
plan,
2488+
&args_list,
2489+
&args,
2490+
attach_first_arg,
2491+
arg_docs,
2492+
)
2493+
}
2494+
CompactCallArgListAttempt::CommentsPresent => {
2495+
format_call_arg_list(ctx, plan, &args_list)
2496+
}
2497+
}
24432498
} else {
24442499
format_call_arg_list(ctx, plan, &args_list)
24452500
};
@@ -2466,24 +2521,24 @@ fn format_compact_call_arg_list(
24662521
ctx: &FormatContext,
24672522
plan: &RootFormatPlan,
24682523
args_list: &LuaCallArgList,
2469-
) -> Option<Vec<DocIR>> {
2470-
let args: Vec<_> = args_list.get_args().collect();
2524+
args: &[LuaExpr],
2525+
) -> CompactCallArgListAttempt {
24712526
let collected = collect_call_arg_entries(ctx, plan, args_list);
24722527
if collected.has_comments {
2473-
return None;
2528+
return CompactCallArgListAttempt::CommentsPresent;
24742529
}
24752530

24762531
let arg_docs: Vec<Vec<DocIR>> = args.iter().map(|arg| format_expr(ctx, plan, arg)).collect();
24772532
if arg_docs
24782533
.iter()
24792534
.any(|docs| ir::ir_has_forced_line_break(docs))
24802535
{
2481-
return None;
2536+
return CompactCallArgListAttempt::ReuseDocs(arg_docs);
24822537
}
24832538

24842539
let (open, close) = paren_tokens(args_list.syntax());
24852540
let comma = first_direct_token(args_list.syntax(), LuaTokenKind::TkComma);
2486-
Some(format_delimited_sequence(
2541+
CompactCallArgListAttempt::Formatted(format_delimited_sequence(
24872542
ctx,
24882543
DelimitedSequenceLayout {
24892544
open: token_or_kind_doc(open.as_ref(), LuaTokenKind::TkLeftParen),

crates/emmylua_formatter/src/formatter/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@ impl<'a> FormatContext<'a> {
2323
pub fn format_chunk(ctx: &FormatContext, chunk: &LuaChunk) -> Vec<DocIR> {
2424
let spacing_plan = spacing::analyze_root_spacing(ctx, chunk);
2525
let layout_plan = layout::analyze_root_layout(ctx, chunk, spacing_plan);
26+
2627
render::render_root(ctx, chunk, &layout_plan)
2728
}

0 commit comments

Comments
 (0)