1 use full_moon::{
2     ast::{
3         span::ContainedSpan, BinOp, Call, Expression, Index, Prefix, Suffix, UnOp, Value, Var,
4         VarExpression,
5     },
6     node::Node,
7     tokenizer::{Symbol, Token, TokenReference, TokenType},
8 };
9 use std::boxed::Box;
10 
11 #[cfg(feature = "luau")]
12 use crate::formatters::luau::format_type_assertion;
13 use crate::{
14     context::{create_indent_trivia, create_newline_trivia, Context},
15     fmt_symbol,
16     formatters::{
17         functions::{
18             format_anonymous_function, format_call, format_function_call, FunctionCallNextNode,
19         },
20         general::{format_contained_span, format_token_reference},
21         table::format_table_constructor,
22         trivia::{
23             strip_leading_trivia, strip_trivia, FormatTriviaType, UpdateLeadingTrivia,
24             UpdateTrailingTrivia, UpdateTrivia,
25         },
26         trivia_util::{
27             self, contains_comments, expression_leading_comments, get_expression_trailing_trivia,
28             trivia_is_newline,
29         },
30     },
31     shape::Shape,
32 };
33 
34 #[macro_export]
35 macro_rules! fmt_op {
36     ($ctx:expr, $enum:ident, $value:ident, $shape:expr, { $($operator:ident = $output:expr,)+ }) => {
37         match $value {
38             $(
39                 $enum::$operator(token) => $enum::$operator(fmt_symbol!($ctx, token, $output, $shape)),
40             )+
41             other => panic!("unknown node {:?}", other),
42         }
43     };
44 }
45 
46 enum ExpressionContext {
47     /// Standard expression, with no special context
48     Standard,
49     /// The expression originates from a [`Prefix`] node. The special context here is that the expression will
50     /// always be wrapped in parentheses.
51     Prefix,
52 }
53 
format_binop(ctx: &Context, binop: &BinOp, shape: Shape) -> BinOp54 pub fn format_binop(ctx: &Context, binop: &BinOp, shape: Shape) -> BinOp {
55     fmt_op!(ctx, BinOp, binop, shape, {
56         And = " and ",
57         Caret = " ^ ",
58         GreaterThan = " > ",
59         GreaterThanEqual = " >= ",
60         LessThan = " < ",
61         LessThanEqual = " <= ",
62         Minus = " - ",
63         Or = " or ",
64         Percent = " % ",
65         Plus = " + ",
66         Slash = " / ",
67         Star = " * ",
68         TildeEqual = " ~= ",
69         TwoDots = " .. ",
70         TwoEqual = " == ",
71     })
72 }
73 
74 /// Check to determine whether expression parentheses are required, depending on the provided
75 /// internal expression contained within the parentheses
check_excess_parentheses(internal_expression: &Expression) -> bool76 fn check_excess_parentheses(internal_expression: &Expression) -> bool {
77     match internal_expression {
78         // Parentheses inside parentheses, not necessary
79         Expression::Parentheses { .. } => true,
80         // Check whether the expression relating to the UnOp is safe
81         Expression::UnaryOperator { expression, .. } => check_excess_parentheses(expression),
82         // Don't bother removing them if there is a binop, as they may be needed. TODO: can we be more intelligent here?
83         Expression::BinaryOperator { .. } => false,
84         Expression::Value {
85             value,
86             #[cfg(feature = "luau")]
87             type_assertion,
88         } => {
89             // If we have a type assertion, we should always keep parentheses
90             #[cfg(feature = "luau")]
91             if type_assertion.is_some() {
92                 return false;
93             }
94 
95             match &**value {
96                 // Internal expression is a function call
97                 // We could potentially be culling values, so we should not remove parentheses
98                 Value::FunctionCall(_) => false,
99                 Value::Symbol(token_ref) => {
100                     match token_ref.token_type() {
101                         // If we have an ellipse inside of parentheses, we may also be culling values
102                         // Therefore, we don't remove parentheses
103                         TokenType::Symbol { symbol } => !matches!(symbol, Symbol::Ellipse),
104                         _ => true,
105                     }
106                 }
107                 _ => true,
108             }
109         }
110         other => panic!("unknown node {:?}", other),
111     }
112 }
113 
114 /// Formats an Expression node
format_expression(ctx: &Context, expression: &Expression, shape: Shape) -> Expression115 pub fn format_expression(ctx: &Context, expression: &Expression, shape: Shape) -> Expression {
116     format_expression_internal(ctx, expression, ExpressionContext::Standard, shape)
117 }
118 
119 /// Internal expression formatter, with access to expression context
format_expression_internal( ctx: &Context, expression: &Expression, context: ExpressionContext, shape: Shape, ) -> Expression120 fn format_expression_internal(
121     ctx: &Context,
122     expression: &Expression,
123     context: ExpressionContext,
124     shape: Shape,
125 ) -> Expression {
126     match expression {
127         Expression::Value {
128             value,
129             #[cfg(feature = "luau")]
130             type_assertion,
131         } => Expression::Value {
132             value: Box::new(format_value(ctx, value, shape)),
133             #[cfg(feature = "luau")]
134             type_assertion: type_assertion
135                 .as_ref()
136                 .map(|assertion| format_type_assertion(ctx, assertion, shape)),
137         },
138         Expression::Parentheses {
139             contained,
140             expression,
141         } => {
142             // Examine whether the internal expression requires parentheses
143             // If not, just format and return the internal expression. Otherwise, format the parentheses
144             let use_internal_expression = check_excess_parentheses(expression);
145 
146             // If the context is for a prefix, we should always keep the parentheses, as they are always required
147             if use_internal_expression && !matches!(context, ExpressionContext::Prefix) {
148                 // Get the trailing comments from contained span and append them onto the expression
149                 let trailing_comments = contained
150                     .tokens()
151                     .1
152                     .trailing_trivia()
153                     .filter(|token| trivia_util::trivia_is_comment(token))
154                     .flat_map(|x| {
155                         // Prepend a single space beforehand
156                         vec![Token::new(TokenType::spaces(1)), x.to_owned()]
157                     })
158                     .collect();
159                 format_expression(ctx, expression, shape)
160                     .update_trailing_trivia(FormatTriviaType::Append(trailing_comments))
161             } else {
162                 Expression::Parentheses {
163                     contained: format_contained_span(ctx, contained, shape),
164                     expression: Box::new(format_expression(ctx, expression, shape + 1)), // 1 = opening parentheses
165                 }
166             }
167         }
168         Expression::UnaryOperator { unop, expression } => {
169             let unop = format_unop(ctx, unop, shape);
170             let shape = shape + strip_leading_trivia(&unop).to_string().len();
171             let mut expression = format_expression(ctx, expression, shape);
172 
173             // Special case: if we have `- -foo`, or `-(-foo)` where we have already removed the parentheses, then
174             // it will lead to `--foo`, which is invalid syntax. We must explicitly add/keep the parentheses `-(-foo)`.
175             if let UnOp::Minus(_) = unop {
176                 let require_parentheses = match expression {
177                     Expression::UnaryOperator {
178                         unop: UnOp::Minus(_),
179                         ..
180                     } => true,
181 
182                     Expression::Value { ref value, .. } => matches!(
183                         **value,
184                         Value::ParenthesesExpression(Expression::UnaryOperator {
185                             unop: UnOp::Minus(_),
186                             ..
187                         })
188                     ),
189 
190                     _ => false,
191                 };
192 
193                 if require_parentheses {
194                     let (new_expression, trailing_comments) =
195                         trivia_util::take_expression_trailing_comments(&expression);
196                     expression = Expression::Parentheses {
197                         contained: ContainedSpan::new(
198                             TokenReference::symbol("(").unwrap(),
199                             TokenReference::symbol(")").unwrap(),
200                         )
201                         .update_trailing_trivia(FormatTriviaType::Append(trailing_comments)),
202                         expression: Box::new(new_expression),
203                     }
204                 }
205             }
206 
207             Expression::UnaryOperator {
208                 unop,
209                 expression: Box::new(expression),
210             }
211         }
212         Expression::BinaryOperator { lhs, binop, rhs } => {
213             let lhs = format_expression(ctx, lhs, shape);
214             let binop = format_binop(ctx, binop, shape);
215             let shape = shape.take_last_line(&lhs) + binop.to_string().len();
216             Expression::BinaryOperator {
217                 lhs: Box::new(lhs),
218                 binop,
219                 rhs: Box::new(format_expression(ctx, rhs, shape)),
220             }
221         }
222         other => panic!("unknown node {:?}", other),
223     }
224 }
225 
226 /// Formats an Index Node
format_index(ctx: &Context, index: &Index, shape: Shape) -> Index227 pub fn format_index(ctx: &Context, index: &Index, shape: Shape) -> Index {
228     match index {
229         Index::Brackets {
230             brackets,
231             expression,
232         } => Index::Brackets {
233             brackets: format_contained_span(ctx, brackets, shape),
234             expression: format_expression(ctx, expression, shape + 1), // 1 = opening bracket
235         },
236 
237         Index::Dot { dot, name } => Index::Dot {
238             dot: format_token_reference(ctx, dot, shape),
239             name: format_token_reference(ctx, name, shape),
240         },
241         other => panic!("unknown node {:?}", other),
242     }
243 }
244 
245 /// Formats a Prefix Node
format_prefix(ctx: &Context, prefix: &Prefix, shape: Shape) -> Prefix246 pub fn format_prefix(ctx: &Context, prefix: &Prefix, shape: Shape) -> Prefix {
247     match prefix {
248         Prefix::Expression(expression) => {
249             let singleline_format =
250                 format_expression_internal(ctx, expression, ExpressionContext::Prefix, shape);
251             let singeline_shape = shape.take_first_line(&strip_trivia(&singleline_format));
252 
253             if singeline_shape.over_budget() {
254                 Prefix::Expression(format_hanging_expression_(
255                     ctx,
256                     expression,
257                     shape,
258                     ExpressionContext::Prefix,
259                     None,
260                 ))
261             } else {
262                 Prefix::Expression(singleline_format)
263             }
264         }
265         Prefix::Name(token_reference) => {
266             Prefix::Name(format_token_reference(ctx, token_reference, shape))
267         }
268         other => panic!("unknown node {:?}", other),
269     }
270 }
271 
272 /// Formats a Suffix Node
format_suffix( ctx: &Context, suffix: &Suffix, shape: Shape, call_next_node: FunctionCallNextNode, ) -> Suffix273 pub fn format_suffix(
274     ctx: &Context,
275     suffix: &Suffix,
276     shape: Shape,
277     call_next_node: FunctionCallNextNode,
278 ) -> Suffix {
279     match suffix {
280         Suffix::Call(call) => Suffix::Call(format_call(ctx, call, shape, call_next_node)),
281         Suffix::Index(index) => Suffix::Index(format_index(ctx, index, shape)),
282         other => panic!("unknown node {:?}", other),
283     }
284 }
285 
286 /// Formats a Value Node
format_value(ctx: &Context, value: &Value, shape: Shape) -> Value287 pub fn format_value(ctx: &Context, value: &Value, shape: Shape) -> Value {
288     match value {
289         Value::Function((token_reference, function_body)) => Value::Function(
290             format_anonymous_function(ctx, token_reference, function_body, shape),
291         ),
292         Value::FunctionCall(function_call) => {
293             Value::FunctionCall(format_function_call(ctx, function_call, shape))
294         }
295         Value::Number(token_reference) => {
296             Value::Number(format_token_reference(ctx, token_reference, shape))
297         }
298         Value::ParenthesesExpression(expression) => {
299             Value::ParenthesesExpression(format_expression(ctx, expression, shape))
300         }
301         Value::String(token_reference) => {
302             Value::String(format_token_reference(ctx, token_reference, shape))
303         }
304         Value::Symbol(token_reference) => {
305             Value::Symbol(format_token_reference(ctx, token_reference, shape))
306         }
307         Value::TableConstructor(table_constructor) => {
308             Value::TableConstructor(format_table_constructor(ctx, table_constructor, shape))
309         }
310         Value::Var(var) => Value::Var(format_var(ctx, var, shape)),
311         other => panic!("unknown node {:?}", other),
312     }
313 }
314 
315 /// Formats a Var Node
format_var(ctx: &Context, var: &Var, shape: Shape) -> Var316 pub fn format_var(ctx: &Context, var: &Var, shape: Shape) -> Var {
317     match var {
318         Var::Name(token_reference) => {
319             Var::Name(format_token_reference(ctx, token_reference, shape))
320         }
321         Var::Expression(var_expression) => {
322             Var::Expression(format_var_expression(ctx, var_expression, shape))
323         }
324         other => panic!("unknown node {:?}", other),
325     }
326 }
327 
format_var_expression( ctx: &Context, var_expression: &VarExpression, shape: Shape, ) -> VarExpression328 pub fn format_var_expression(
329     ctx: &Context,
330     var_expression: &VarExpression,
331     shape: Shape,
332 ) -> VarExpression {
333     let formatted_prefix = format_prefix(ctx, var_expression.prefix(), shape);
334     let mut shape = shape + strip_leading_trivia(&formatted_prefix).to_string().len();
335 
336     let mut formatted_suffixes = Vec::new();
337     let mut suffixes = var_expression.suffixes().peekable();
338 
339     while let Some(suffix) = suffixes.next() {
340         // If the suffix after this one is something like `.foo` or `:foo` - this affects removing parentheses
341         let ambiguous_next_suffix = if matches!(
342             suffixes.peek(),
343             Some(Suffix::Index(_)) | Some(Suffix::Call(Call::MethodCall(_)))
344         ) {
345             FunctionCallNextNode::ObscureWithoutParens
346         } else {
347             FunctionCallNextNode::None
348         };
349 
350         let suffix = format_suffix(ctx, suffix, shape, ambiguous_next_suffix);
351         shape = shape + suffix.to_string().len();
352         formatted_suffixes.push(suffix);
353     }
354 
355     VarExpression::new(formatted_prefix).with_suffixes(formatted_suffixes)
356 }
357 
358 /// Formats an UnOp Node
format_unop(ctx: &Context, unop: &UnOp, shape: Shape) -> UnOp359 pub fn format_unop(ctx: &Context, unop: &UnOp, shape: Shape) -> UnOp {
360     fmt_op!(ctx, UnOp, unop, shape, {
361         Minus = "-",
362         Not = "not ",
363         Hash = "#",
364     })
365 }
366 
367 /// Pushes a [`BinOp`] onto a newline, and indent its depending on indent_level.
368 /// Preserves any leading comments, and moves trailing comments to before the BinOp.
369 /// Also takes in the [`Expression`] present on the RHS of the BinOp - this is needed so that we can take any
370 /// leading comments from the expression, and place them before the BinOp.
hang_binop(ctx: &Context, binop: BinOp, shape: Shape, rhs: &Expression) -> BinOp371 fn hang_binop(ctx: &Context, binop: BinOp, shape: Shape, rhs: &Expression) -> BinOp {
372     // Get the leading comments of a binop, as we need to preserve them
373     // Intersperse a newline and indent trivia between them
374     // iter_intersperse is currently not available, so we need to do something different. Tracking issue: https://github.com/rust-lang/rust/issues/79524
375     let mut leading_comments = trivia_util::binop_leading_comments(&binop)
376         .iter()
377         .flat_map(|x| {
378             vec![
379                 create_newline_trivia(ctx),
380                 create_indent_trivia(ctx, shape),
381                 x.to_owned(),
382             ]
383         })
384         .collect::<Vec<_>>();
385 
386     // If there are any comments trailing the BinOp, we need to move them to before the BinOp
387     let mut trailing_comments = trivia_util::binop_trailing_comments(&binop);
388     leading_comments.append(&mut trailing_comments);
389 
390     // If there are any leading comments to the RHS expression, we need to move them to before the BinOp
391     let mut expression_leading_comments = trivia_util::expression_leading_comments(rhs)
392         .iter()
393         .flat_map(|x| {
394             vec![
395                 create_newline_trivia(ctx),
396                 create_indent_trivia(ctx, shape),
397                 x.to_owned(),
398             ]
399         })
400         .collect::<Vec<_>>();
401     leading_comments.append(&mut expression_leading_comments);
402 
403     // Create a newline just before the BinOp, and preserve the indentation
404     leading_comments.push(create_newline_trivia(ctx));
405     leading_comments.push(create_indent_trivia(ctx, shape));
406 
407     binop.update_trivia(
408         FormatTriviaType::Replace(leading_comments),
409         FormatTriviaType::Replace(vec![Token::new(TokenType::spaces(1))]),
410     )
411 }
412 
413 /// Finds the length of the expression which matches the precedence level of the provided binop
binop_expression_length(expression: &Expression, top_binop: &BinOp) -> usize414 fn binop_expression_length(expression: &Expression, top_binop: &BinOp) -> usize {
415     match expression {
416         Expression::BinaryOperator { lhs, binop, rhs } => {
417             if binop.precedence() >= top_binop.precedence()
418                 && binop.is_right_associative() == top_binop.is_right_associative()
419             {
420                 if binop.is_right_associative() {
421                     binop_expression_length(rhs, top_binop)
422                         + strip_trivia(binop).to_string().len() + 2 // 2 = space before and after binop
423                         + lhs.to_string().len()
424                 } else {
425                     binop_expression_length(lhs, top_binop)
426                         + strip_trivia(binop).to_string().len() + 2 // 2 = space before and after binop
427                         + rhs.to_string().len()
428                 }
429             } else {
430                 0
431             }
432         }
433         _ => expression.to_string().len(),
434     }
435 }
436 
binop_expression_contains_comments(expression: &Expression, top_binop: &BinOp) -> bool437 fn binop_expression_contains_comments(expression: &Expression, top_binop: &BinOp) -> bool {
438     match expression {
439         Expression::BinaryOperator { lhs, binop, rhs } => {
440             if binop.precedence() == top_binop.precedence() {
441                 contains_comments(binop)
442                     || !expression_leading_comments(rhs).is_empty()
443                     || get_expression_trailing_trivia(lhs)
444                         .iter()
445                         .any(trivia_util::trivia_is_comment)
446                     || binop_expression_contains_comments(lhs, top_binop)
447                     || binop_expression_contains_comments(rhs, top_binop)
448             } else {
449                 false
450             }
451         }
452         _ => false,
453     }
454 }
455 
456 /// Converts an item to a range
457 trait ToRange {
to_range(&self) -> (usize, usize)458     fn to_range(&self) -> (usize, usize);
459 }
460 
461 impl ToRange for (usize, usize) {
to_range(&self) -> (usize, usize)462     fn to_range(&self) -> (usize, usize) {
463         *self
464     }
465 }
466 
467 impl ToRange for Expression {
to_range(&self) -> (usize, usize)468     fn to_range(&self) -> (usize, usize) {
469         let (start, end) = self.range().unwrap();
470         (start.bytes(), end.bytes())
471     }
472 }
473 
474 /// This struct encompasses information about the leftmost-expression in a BinaryExpression tree.
475 /// It holds the range of the leftmost binary expression, and the original additional indent level of this range.
476 /// This struct is only used when the hanging binary expression involves a hang level, for example:
477 /// ```lua
478 /// foooo
479 ///    + bar
480 ///    + baz
481 /// ```
482 /// or in a larger context:
483 /// ```lua
484 /// local someVariable = foooo
485 ///    + bar
486 ///    + baz
487 /// ```
488 /// As seen, the first item (`foooo`) is inlined, and has an indent level one lower than the rest of the binary
489 /// expressions. We want to ensure that whenever we have `foooo` in our expression, we use the original indentation level
490 /// because the expression is (at this current point in time) inlined - otherwise, it will be over-indented.
491 /// We hold the original indentation level incase we are deep down in the recursivecalls:
492 /// ```lua
493 /// local ratio = (minAxis - minAxisSize) / delta * (self.props.maxScaleRatio - self.props.minScaleRatio)
494 ///     + self.props.minScaleRatio
495 /// ```
496 /// Since the first line contains binary operators at a different precedence level to the `+`, then the indentation
497 /// level has been increased even further. But we want to use the original indentation level, because as it stands,
498 /// the expression is currently inlined on the original line.
499 #[derive(Clone, Copy, Debug)]
500 struct LeftmostRangeHang {
501     range: (usize, usize),
502     original_additional_indent_level: usize,
503 }
504 
505 impl LeftmostRangeHang {
506     /// Finds the leftmost expression from the given (full) expression, and then creates a [`LeftmostRangeHang`]
507     /// to represent it
find(expression: &Expression, original_additional_indent_level: usize) -> Self508     fn find(expression: &Expression, original_additional_indent_level: usize) -> Self {
509         match expression {
510             Expression::BinaryOperator { lhs, .. } => {
511                 Self::find(lhs, original_additional_indent_level)
512             }
513             _ => Self {
514                 range: expression.to_range(),
515                 original_additional_indent_level,
516             },
517         }
518     }
519 
520     /// Given an [`Expression`], returns the [`Shape`] to use for this expression.
521     /// This function checks the provided expression to see if the LeftmostRange falls inside of it.
522     /// If so, then we need to use the original indentation level shape, as (so far) the expression is inlined.
required_shape<T: ToRange>(&self, shape: Shape, item: &T) -> Shape523     fn required_shape<T: ToRange>(&self, shape: Shape, item: &T) -> Shape {
524         let (expression_start, expression_end) = item.to_range();
525         let (lhs_start, lhs_end) = self.range;
526 
527         if lhs_start >= expression_start && lhs_end <= expression_end {
528             shape.with_indent(
529                 shape
530                     .indent()
531                     .with_additional_indent(self.original_additional_indent_level),
532             )
533         } else {
534             shape
535         }
536     }
537 }
538 
is_hang_binop_over_width( shape: Shape, expression: &Expression, top_binop: &BinOp, lhs_range: Option<LeftmostRangeHang>, ) -> bool539 fn is_hang_binop_over_width(
540     shape: Shape,
541     expression: &Expression,
542     top_binop: &BinOp,
543     lhs_range: Option<LeftmostRangeHang>,
544 ) -> bool {
545     let shape = if let Some(lhs_hang) = lhs_range {
546         lhs_hang.required_shape(shape, expression)
547     } else {
548         shape
549     };
550 
551     shape
552         .add_width(binop_expression_length(expression, top_binop))
553         .over_budget()
554 }
555 
556 /// If present, finds the precedence level of the provided binop in the BinOp expression. Otherwise, returns 0
binop_precedence_level(expression: &Expression) -> u8557 fn binop_precedence_level(expression: &Expression) -> u8 {
558     match expression {
559         Expression::BinaryOperator { binop, .. } => binop.precedence(),
560         _ => 0,
561     }
562 }
563 
did_hang_expression(expression: &Expression) -> bool564 fn did_hang_expression(expression: &Expression) -> bool {
565     if let Expression::BinaryOperator { binop, .. } = expression {
566         // Examine the binop's leading trivia for a newline
567         // TODO: this works..., but is it the right solution?
568         binop
569             .surrounding_trivia()
570             .0
571             .iter()
572             .any(|x| trivia_is_newline(x))
573     } else {
574         false
575     }
576 }
577 
578 #[derive(Debug)]
579 enum ExpressionSide {
580     Left,
581     Right,
582 }
583 
hang_binop_expression( ctx: &Context, expression: Expression, top_binop: BinOp, shape: Shape, lhs_range: Option<LeftmostRangeHang>, ) -> Expression584 fn hang_binop_expression(
585     ctx: &Context,
586     expression: Expression,
587     top_binop: BinOp,
588     shape: Shape,
589     lhs_range: Option<LeftmostRangeHang>,
590 ) -> Expression {
591     let full_expression = expression.to_owned();
592 
593     match expression {
594         Expression::BinaryOperator { lhs, binop, rhs } => {
595             // Keep grouping together all operators with the same precedence level as the main BinOp
596             // They should also have the same associativity
597             let same_op_level = binop.precedence() == top_binop.precedence()
598                 && binop.is_right_associative() == top_binop.is_right_associative();
599             let is_right_associative = binop.is_right_associative();
600 
601             let test_shape = if same_op_level {
602                 shape
603             } else {
604                 shape.increment_additional_indent()
605             };
606 
607             let side_to_hang = if is_right_associative {
608                 ExpressionSide::Right
609             } else {
610                 ExpressionSide::Left
611             };
612 
613             // TODO/FIXME: using test_shape here leads to too high of an indent level, causing the expression to hang unnecessarily
614             let over_column_width =
615                 is_hang_binop_over_width(test_shape, &full_expression, &binop, lhs_range);
616             let should_hang = same_op_level
617                 || over_column_width
618                 || binop_expression_contains_comments(&full_expression, &binop);
619 
620             // Only use the indented shape if we are planning to hang
621             let shape = if should_hang { test_shape } else { shape };
622 
623             let mut new_binop = format_binop(ctx, &binop, shape);
624             if should_hang {
625                 new_binop = hang_binop(ctx, binop.to_owned(), shape, &rhs);
626             }
627 
628             let (lhs, rhs) = match should_hang {
629                 true => {
630                     let lhs_shape = shape;
631                     let rhs_shape = shape + strip_trivia(&new_binop).to_string().len() + 1;
632 
633                     let (lhs, rhs) = match side_to_hang {
634                         ExpressionSide::Left => (
635                             hang_binop_expression(
636                                 ctx,
637                                 *lhs,
638                                 if same_op_level { top_binop } else { binop },
639                                 lhs_shape,
640                                 lhs_range,
641                             ),
642                             format_expression(ctx, &*rhs, rhs_shape),
643                         ),
644                         ExpressionSide::Right => (
645                             format_expression(ctx, &*lhs, lhs_shape),
646                             hang_binop_expression(
647                                 ctx,
648                                 *rhs,
649                                 if same_op_level { top_binop } else { binop },
650                                 rhs_shape,
651                                 lhs_range,
652                             ),
653                         ),
654                     };
655                     (
656                         lhs,
657                         rhs.update_leading_trivia(FormatTriviaType::Replace(Vec::new())),
658                     )
659                 }
660                 false => {
661                     // Check if the chain still has comments deeper inside of it.
662                     // If it does, we need to hang that part of the chain still, otherwise the comments will mess it up
663                     let lhs = if contains_comments(&*lhs) {
664                         hang_binop_expression(ctx, *lhs, binop.to_owned(), shape, lhs_range)
665                     } else {
666                         format_expression(ctx, &*lhs, shape)
667                     };
668 
669                     let rhs = if contains_comments(&*rhs) {
670                         hang_binop_expression(ctx, *rhs, binop, shape, lhs_range)
671                     } else {
672                         format_expression(ctx, &*rhs, shape)
673                     };
674 
675                     (lhs, rhs)
676                 }
677             };
678 
679             Expression::BinaryOperator {
680                 lhs: Box::new(lhs),
681                 binop: new_binop,
682                 rhs: Box::new(rhs),
683             }
684         }
685         // Base case: no more binary operators - just return to normal splitting
686         _ => format_hanging_expression_(
687             ctx,
688             &expression,
689             shape,
690             ExpressionContext::Standard,
691             lhs_range,
692         ),
693     }
694 }
695 
696 /// Internal expression formatter, where the binop is also hung
format_hanging_expression_( ctx: &Context, expression: &Expression, shape: Shape, expression_context: ExpressionContext, lhs_range: Option<LeftmostRangeHang>, ) -> Expression697 fn format_hanging_expression_(
698     ctx: &Context,
699     expression: &Expression,
700     shape: Shape,
701     expression_context: ExpressionContext,
702     lhs_range: Option<LeftmostRangeHang>,
703 ) -> Expression {
704     let expression_range = expression.to_range();
705 
706     match expression {
707         Expression::Value {
708             value,
709             #[cfg(feature = "luau")]
710             type_assertion,
711         } => {
712             let value = Box::new(match &**value {
713                 Value::ParenthesesExpression(expression) => {
714                     Value::ParenthesesExpression(format_hanging_expression_(
715                         ctx,
716                         expression,
717                         shape,
718                         expression_context,
719                         lhs_range,
720                     ))
721                 }
722                 _ => {
723                     let shape = if let Some(lhs_hang) = lhs_range {
724                         lhs_hang.required_shape(shape, &expression_range)
725                     } else {
726                         shape
727                     };
728                     format_value(ctx, value, shape)
729                 }
730             });
731             Expression::Value {
732                 value,
733                 #[cfg(feature = "luau")]
734                 type_assertion: type_assertion
735                     .as_ref()
736                     .map(|assertion| format_type_assertion(ctx, assertion, shape)),
737             }
738         }
739         Expression::Parentheses {
740             contained,
741             expression,
742         } => {
743             let lhs_shape = if let Some(lhs_hang) = lhs_range {
744                 lhs_hang.required_shape(shape, &expression_range)
745             } else {
746                 shape
747             };
748 
749             // Examine whether the internal expression requires parentheses
750             // If not, just format and return the internal expression. Otherwise, format the parentheses
751             let use_internal_expression = check_excess_parentheses(expression);
752 
753             // If the context is for a prefix, we should always keep the parentheses, as they are always required
754             if use_internal_expression && !matches!(expression_context, ExpressionContext::Prefix) {
755                 format_hanging_expression_(
756                     ctx,
757                     expression,
758                     lhs_shape,
759                     expression_context,
760                     lhs_range,
761                 )
762             } else {
763                 let contained = format_contained_span(ctx, contained, lhs_shape);
764 
765                 // Provide a sample formatting to see how large it is
766                 // Examine the expression itself to see if needs to be split onto multiple lines
767                 let formatted_expression = format_expression(ctx, expression, lhs_shape + 1); // 1 = opening parentheses
768 
769                 let expression_str = formatted_expression.to_string();
770                 if !lhs_shape.add_width(2 + expression_str.len()).over_budget() {
771                     // The expression inside the parentheses is small, we do not need to break it down further
772                     return Expression::Parentheses {
773                         contained,
774                         expression: Box::new(formatted_expression),
775                     };
776                 }
777 
778                 // Update the expression shape to be used inside the parentheses, applying the indent increase
779                 // Use the original `shape` rather than the LeftmostRangeHang-determined shape, because we are now
780                 // indenting the internal expression, which is not part of the hang
781                 let expression_shape = shape.reset().increment_additional_indent();
782 
783                 // Modify the parentheses to hang the expression
784                 let (start_token, end_token) = contained.tokens();
785 
786                 // Create a newline after the start brace and before the end brace
787                 // Also, indent enough for the first expression in the start brace
788                 let contained = ContainedSpan::new(
789                     start_token.update_trailing_trivia(FormatTriviaType::Append(vec![
790                         create_newline_trivia(ctx),
791                         create_indent_trivia(ctx, expression_shape),
792                     ])),
793                     end_token.update_leading_trivia(FormatTriviaType::Append(vec![
794                         create_newline_trivia(ctx),
795                         create_indent_trivia(ctx, shape),
796                     ])),
797                 );
798 
799                 Expression::Parentheses {
800                     contained,
801                     expression: Box::new(format_hanging_expression_(
802                         ctx,
803                         expression,
804                         expression_shape,
805                         ExpressionContext::Standard,
806                         None,
807                     )),
808                 }
809             }
810         }
811         Expression::UnaryOperator { unop, expression } => {
812             let unop = format_unop(ctx, unop, shape);
813             let shape = shape + strip_leading_trivia(&unop).to_string().len();
814             let expression =
815                 format_hanging_expression_(ctx, expression, shape, expression_context, lhs_range);
816 
817             Expression::UnaryOperator {
818                 unop,
819                 expression: Box::new(expression),
820             }
821         }
822         Expression::BinaryOperator { lhs, binop, rhs } => {
823             // Don't format the lhs and rhs here, because it will be handled later when hang_binop_expression calls back for a Value
824             let lhs =
825                 hang_binop_expression(ctx, *lhs.to_owned(), binop.to_owned(), shape, lhs_range);
826 
827             let current_shape = shape.take_last_line(&lhs) + 1; // 1 = space before binop
828             let mut new_binop = format_binop(ctx, binop, current_shape);
829 
830             let singleline_shape = current_shape + strip_trivia(binop).to_string().len() + 1; // 1 = space after binop
831 
832             let mut new_rhs = hang_binop_expression(
833                 ctx,
834                 *rhs.to_owned(),
835                 binop.to_owned(),
836                 singleline_shape,
837                 None,
838             );
839 
840             // Examine the last line to see if we need to hang this binop, or if the precedence levels match
841             if (did_hang_expression(&lhs) && binop_precedence_level(&lhs) >= binop.precedence())
842                 || (did_hang_expression(&new_rhs)
843                     && binop_precedence_level(&new_rhs) >= binop.precedence())
844                 || contains_comments(binop)
845                 || get_expression_trailing_trivia(&lhs)
846                     .iter()
847                     .any(trivia_util::trivia_is_comment)
848                 || (shape.take_last_line(&lhs) + format!("{}{}", binop, rhs).len()).over_budget()
849             {
850                 let hanging_shape = shape.reset() + strip_trivia(binop).to_string().len() + 1;
851                 new_binop = hang_binop(ctx, binop.to_owned(), shape, rhs);
852                 new_rhs = hang_binop_expression(
853                     ctx,
854                     *rhs.to_owned(),
855                     binop.to_owned(),
856                     hanging_shape,
857                     None,
858                 )
859                 .update_leading_trivia(FormatTriviaType::Replace(Vec::new()));
860             }
861 
862             Expression::BinaryOperator {
863                 lhs: Box::new(lhs),
864                 binop: new_binop,
865                 rhs: Box::new(new_rhs),
866             }
867         }
868         other => panic!("unknown node {:?}", other),
869     }
870 }
871 
hang_expression( ctx: &Context, expression: &Expression, shape: Shape, hang_level: Option<usize>, ) -> Expression872 pub fn hang_expression(
873     ctx: &Context,
874     expression: &Expression,
875     shape: Shape,
876     hang_level: Option<usize>,
877 ) -> Expression {
878     let original_additional_indent_level = shape.indent().additional_indent();
879     let shape = match hang_level {
880         Some(hang_level) => shape.with_indent(shape.indent().add_indent_level(hang_level)),
881         None => shape,
882     };
883 
884     let lhs_range =
885         hang_level.map(|_| LeftmostRangeHang::find(expression, original_additional_indent_level));
886 
887     format_hanging_expression_(
888         ctx,
889         expression,
890         shape,
891         ExpressionContext::Standard,
892         lhs_range,
893     )
894 }
895 
hang_expression_trailing_newline( ctx: &Context, expression: &Expression, shape: Shape, hang_level: Option<usize>, ) -> Expression896 pub fn hang_expression_trailing_newline(
897     ctx: &Context,
898     expression: &Expression,
899     shape: Shape,
900     hang_level: Option<usize>,
901 ) -> Expression {
902     hang_expression(ctx, expression, shape, hang_level)
903         .update_trailing_trivia(FormatTriviaType::Append(vec![create_newline_trivia(ctx)]))
904 }
905