Skip to content

Commit edf11fe

Browse files
Keep multiline comments in place before commas (#822)
* Keep multiline comments in place when formatting punctuated sequences * Update changelog
1 parent 095e2b0 commit edf11fe

File tree

7 files changed

+107
-23
lines changed

7 files changed

+107
-23
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2121
- Fix Luau `\z` escape parsing
2222

2323
- Simplified access and modification patterns for StyLua configuration. You can now access the properties directly
24+
2425
- **Deprecated:** the old access patterns of `.property()` and `.with_property()` are now deprecated
2526
- **Breaking Change (WASM):** due to JS/TS lack of differentiation between `.property` / `.property()` implementation, the `.property()` functions were removed from WASM output.
2627

28+
- Multiline comments before commas will now remain in place and not move to after the comma. This is to support type-assertions-via-comments that is commonly used by some language servers. ([#778](https://github.com/JohnnyMorganz/StyLua/issues/778))
29+
2730
### Fixed
2831

2932
- Wasm build now correctly supports configuring sort requires ([#818](https://github.com/JohnnyMorganz/StyLua/issues/818))

src/formatters/general.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use crate::{
55
formatters::{
66
trivia::{FormatTriviaType, UpdateLeadingTrivia, UpdateTrailingTrivia, UpdateTrivia},
77
trivia_util::{
8-
self, punctuated_inline_comments, take_trailing_comments, GetLeadingTrivia,
9-
GetTrailingTrivia, HasInlineComments,
8+
self, punctuated_inline_comments, CommentSearch, GetLeadingTrivia, GetTrailingTrivia,
9+
HasInlineComments,
1010
},
1111
},
1212
shape::Shape,
@@ -522,9 +522,15 @@ where
522522
ctx, shape,
523523
)]));
524524

525-
// Take any trailing trivia (i.e. comments) from the argument, and append it to the end of the punctuation
526-
let (formatted_argument, mut trailing_comments) =
527-
take_trailing_comments(&formatted_argument);
525+
// Any singleline comments must be moved to after the punctuation
526+
// We should keep multiline comments in the same location
527+
let multiline_comments =
528+
formatted_argument.trailing_comments_search(CommentSearch::Multiline);
529+
let singleline_comments =
530+
formatted_argument.trailing_comments_search(CommentSearch::Single);
531+
532+
let formatted_argument = formatted_argument
533+
.update_trailing_trivia(FormatTriviaType::Replace(multiline_comments));
528534

529535
let punctuation = match argument.punctuation() {
530536
Some(punctuation) => {
@@ -544,8 +550,8 @@ where
544550
x,
545551
]
546552
})
553+
.chain(singleline_comments)
547554
.collect();
548-
trailing_trivia.append(&mut trailing_comments);
549555
trailing_trivia.push(create_newline_trivia(ctx));
550556

551557
let symbol = symbol.update_trivia(
@@ -559,7 +565,7 @@ where
559565
// We need to do this because in function declarations, we format parameters but if they have a type
560566
// specifier we don't have access to put it after the type specifier
561567
None => Some(TokenReference::new(
562-
trailing_comments,
568+
singleline_comments,
563569
create_newline_trivia(ctx),
564570
vec![],
565571
)),

src/formatters/luau.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -839,15 +839,18 @@ pub fn format_type_field(
839839
let shape = shape + (strip_leading_trivia(&key).to_string().len() + 2);
840840
let mut value = format_type_info(ctx, type_field.value(), shape);
841841

842-
let trailing_trivia = value.trailing_trivia();
842+
// Trailing trivia consists only of single line comments - multiline comments are kept in place
843+
let trailing_trivia = value.trailing_comments_search(CommentSearch::Single);
843844

844845
if let TableType::MultiLine = table_type {
845846
// If still over budget, hang the type
846847
if can_hang_type(type_field.value()) && shape.test_over_budget(&value) {
847848
value = hang_type_info(ctx, type_field.value(), TypeInfoContext::new(), shape, 1)
848849
};
849850

850-
value = value.update_trailing_trivia(FormatTriviaType::Replace(vec![]))
851+
// Keep multiline comments in place
852+
let multiline_comments = value.trailing_comments_search(CommentSearch::Multiline);
853+
value = value.update_trailing_trivia(FormatTriviaType::Replace(multiline_comments))
851854
}
852855

853856
(

src/formatters/table.rs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99
trivia_to_vec, EndTokenType, FormatTokenType,
1010
},
1111
trivia::{strip_trivia, FormatTriviaType, UpdateLeadingTrivia, UpdateTrailingTrivia},
12-
trivia_util::{self, GetTrailingTrivia, HasInlineComments},
12+
trivia_util::{self, CommentSearch, GetTrailingTrivia, HasInlineComments},
1313
},
1414
shape::Shape,
1515
};
@@ -40,17 +40,19 @@ fn format_field_expression_value(
4040
expression: &Expression,
4141
shape: Shape,
4242
) -> Expression {
43-
// Remove all trivia from the output expression as it will be moved after the comma
43+
// Remove singleline comments from the output expression as it will be moved after the comma
44+
// Retain multiline comments in place
45+
let multiline_comments = expression.trailing_comments_search(CommentSearch::Multiline);
46+
let trailing_trivia = FormatTriviaType::Replace(multiline_comments);
4447

4548
if trivia_util::can_hang_expression(expression) {
4649
if expression.has_inline_comments() {
47-
hang_expression(ctx, expression, shape, Some(1))
48-
.update_trailing_trivia(FormatTriviaType::Replace(vec![]))
50+
hang_expression(ctx, expression, shape, Some(1)).update_trailing_trivia(trailing_trivia)
4951
} else {
5052
let singleline_value = format_expression(ctx, expression, shape)
51-
.update_trailing_trivia(FormatTriviaType::Replace(vec![]));
53+
.update_trailing_trivia(trailing_trivia.clone());
5254
let hanging_value = hang_expression(ctx, expression, shape, Some(1))
53-
.update_trailing_trivia(FormatTriviaType::Replace(vec![]));
55+
.update_trailing_trivia(trailing_trivia);
5456

5557
if shape.test_over_budget(&singleline_value)
5658
|| format!("{hanging_value}").lines().count()
@@ -62,8 +64,7 @@ fn format_field_expression_value(
6264
}
6365
}
6466
} else {
65-
format_expression(ctx, expression, shape)
66-
.update_trailing_trivia(FormatTriviaType::Replace(vec![]))
67+
format_expression(ctx, expression, shape).update_trailing_trivia(trailing_trivia)
6768
}
6869
}
6970

@@ -139,6 +140,8 @@ fn format_field(
139140
_ => FormatTriviaType::NoChange,
140141
};
141142

143+
// Trailing trivia is taken out and moved to after the comma
144+
// We only move singleline comments, multiline comments remain in place
142145
let trailing_trivia;
143146
let field = match field {
144147
Field::ExpressionKey {
@@ -147,7 +150,7 @@ fn format_field(
147150
equal,
148151
value,
149152
} => {
150-
trailing_trivia = value.trailing_trivia();
153+
trailing_trivia = value.trailing_comments_search(CommentSearch::Single);
151154
let brackets = format_contained_span(ctx, brackets, shape);
152155

153156
let space_brackets = is_brackets_string(key);
@@ -185,7 +188,7 @@ fn format_field(
185188
}
186189
}
187190
Field::NameKey { key, equal, value } => {
188-
trailing_trivia = value.trailing_trivia();
191+
trailing_trivia = value.trailing_comments_search(CommentSearch::Single);
189192
let key = format_token_reference(ctx, key, shape);
190193

191194
// Get the new leading comments to add before the key, and the equal token
@@ -205,7 +208,7 @@ fn format_field(
205208
Field::NameKey { key, equal, value }
206209
}
207210
Field::NoKey(expression) => {
208-
trailing_trivia = expression.trailing_trivia();
211+
trailing_trivia = expression.trailing_comments_search(CommentSearch::Single);
209212

210213
if let TableType::MultiLine = table_type {
211214
let formatted_expression = format_field_expression_value(ctx, expression, shape);
@@ -305,7 +308,8 @@ where
305308

306309
// Format the field. We will ignore the taken trailing trivia, as we do not need it.
307310
// (If there were any comments present, this function should never have been called)
308-
let formatted_field = formatter(ctx, field, table_type, shape).0;
311+
let (formatted_field, trailing_trivia) = formatter(ctx, field, table_type, shape);
312+
assert!(trailing_trivia.is_empty());
309313

310314
let formatted_punctuation = match current_fields.peek() {
311315
Some(_) => {
@@ -370,6 +374,7 @@ where
370374
trailing_trivia = Vec::new();
371375
} else {
372376
// Filter trailing trivia for any newlines
377+
// NOTE: in practice, this should only consist of singleline comments
373378
trailing_trivia = trailing_trivia
374379
.iter()
375380
.filter(|x| !trivia_util::trivia_is_whitespace(x))

src/formatters/trivia_util.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,20 @@ pub trait GetTrailingTrivia {
4444

4545
// Retrieves all the trailing comments from the token
4646
// Prepends a space before each comment
47-
fn trailing_comments(&self) -> Vec<Token> {
47+
fn trailing_comments_search(&self, search: CommentSearch) -> Vec<Token> {
4848
self.trailing_trivia()
4949
.iter()
50-
.filter(|token| trivia_is_comment(token))
50+
.filter(|token| trivia_is_comment_search(token, search))
5151
.flat_map(|x| {
5252
// Prepend a single space beforehand
5353
vec![Token::new(TokenType::spaces(1)), x.to_owned()]
5454
})
5555
.collect()
5656
}
57+
58+
fn trailing_comments(&self) -> Vec<Token> {
59+
self.trailing_comments_search(CommentSearch::All)
60+
}
5761
}
5862

5963
pub fn trivia_is_whitespace(trivia: &Token) -> bool {
@@ -64,13 +68,25 @@ pub fn trivia_is_singleline_comment(trivia: &Token) -> bool {
6468
matches!(trivia.token_kind(), TokenKind::SingleLineComment)
6569
}
6670

71+
fn trivia_is_multiline_comment(trivia: &Token) -> bool {
72+
matches!(trivia.token_kind(), TokenKind::MultiLineComment)
73+
}
74+
6775
pub fn trivia_is_comment(trivia: &Token) -> bool {
6876
matches!(
6977
trivia.token_kind(),
7078
TokenKind::SingleLineComment | TokenKind::MultiLineComment
7179
)
7280
}
7381

82+
fn trivia_is_comment_search(trivia: &Token, search: CommentSearch) -> bool {
83+
match search {
84+
CommentSearch::Single => trivia_is_singleline_comment(trivia),
85+
CommentSearch::Multiline => trivia_is_multiline_comment(trivia),
86+
CommentSearch::All => trivia_is_comment(trivia),
87+
}
88+
}
89+
7490
pub fn trivia_is_newline(trivia: &Token) -> bool {
7591
if let TokenType::Whitespace { characters } = trivia.token_type() {
7692
if characters.find('\n').is_some() {
@@ -825,6 +841,8 @@ impl GetTrailingTrivia for LastStmt {
825841
pub enum CommentSearch {
826842
// Only care about singleline comments
827843
Single,
844+
// Only care about multiline comments
845+
Multiline,
828846
// Looking for all comments
829847
All,
830848
}
@@ -835,6 +853,7 @@ fn trivia_contains_comments<'a>(
835853
) -> bool {
836854
let tester = match search {
837855
CommentSearch::Single => trivia_is_singleline_comment,
856+
CommentSearch::Multiline => trivia_is_multiline_comment,
838857
CommentSearch::All => trivia_is_comment,
839858
};
840859

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
-- https://github.com/JohnnyMorganz/StyLua/issues/778
2+
-- comments should stay before punctuation to ensure type assertions work in sumneko-lua
3+
4+
function fun(
5+
a --[[ a commnet]],
6+
b
7+
)
8+
end
9+
10+
local tab = {
11+
a = 1 --[[@as integer ]],
12+
b = 1,
13+
}
14+
15+
call(
16+
long_argument_name --[[@as integer ]],
17+
long_argument_name,
18+
long_argument_name,
19+
long_argument_name,
20+
long_argument_name
21+
)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
source: tests/tests.rs
3+
expression: format(&contents)
4+
input_file: tests/inputs/comments-before-punctuation.lua
5+
---
6+
-- https://github.com/JohnnyMorganz/StyLua/issues/778
7+
-- comments should stay before punctuation to ensure type assertions work in sumneko-lua
8+
9+
function fun(
10+
a --[[ a commnet]],
11+
b
12+
)
13+
end
14+
15+
local tab = {
16+
a = 1 --[[@as integer ]],
17+
b = 1,
18+
}
19+
20+
call(
21+
long_argument_name --[[@as integer ]],
22+
long_argument_name,
23+
long_argument_name,
24+
long_argument_name,
25+
long_argument_name
26+
)
27+

0 commit comments

Comments
 (0)