Skip to content

Commit e996c06

Browse files
committed
Merge branch 'master' of https://github.com/JohnnyMorganz/StyLua into type-comments
2 parents 3023b35 + 8b3b1dd commit e996c06

File tree

8 files changed

+170
-36
lines changed

8 files changed

+170
-36
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
### Fixed
99
- Fixed leading trivia on semicolon lost when semicolon is removed ([#431](https://github.com/JohnnyMorganz/StyLua/issues/431))
1010
- Fixed shape calculation of the RHS of a binary expression not correctly reset when hanging, causing it to expand unnecessarily ([#432](https://github.com/JohnnyMorganz/StyLua/issues/432))
11+
- Fixed unstable formatting of tables at column width boundary ([#436](https://github.com/JohnnyMorganz/StyLua/issues/436))
12+
- Fixed assignments no longer hanging at equals token if a comment is present, but the expression is not hangable at a binop. ([#439](https://github.com/JohnnyMorganz/StyLua/issues/439))
1113
- Fixed unstable formatting around comments within type declarations ([#397](https://github.com/JohnnyMorganz/StyLua/issues/397), [#430](https://github.com/JohnnyMorganz/StyLua/issues/430))
1214

1315
## [0.13.0] - 2022-03-31

src/formatters/assignment.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,9 @@ fn attempt_assignment_tactics(
239239

240240
// Find the better format out of the hanging shape or the normal formatting
241241
// If the expression contains comments, we must hang
242-
if trivia_util::expression_contains_inline_comments(expression)
243-
|| hanging_shape.used_width() < formatting_shape.used_width()
242+
if trivia_util::can_hang_expression(expression)
243+
&& (trivia_util::expression_contains_inline_comments(expression)
244+
|| hanging_shape.used_width() < formatting_shape.used_width())
244245
{
245246
// Hanging version is better
246247
(hanging_expr_list, equal_token)

src/formatters/table.rs

Lines changed: 64 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::{
55
expression::{format_expression, hang_expression, is_brackets_string},
66
general::{format_contained_span, format_end_token, format_token_reference, EndTokenType},
77
trivia::{strip_trivia, FormatTriviaType, UpdateLeadingTrivia, UpdateTrailingTrivia},
8-
trivia_util,
8+
trivia_util::{self, table_field_trailing_trivia},
99
},
1010
shape::Shape,
1111
};
@@ -16,7 +16,7 @@ use full_moon::{
1616
Expression, Field, TableConstructor, Value,
1717
},
1818
node::Node,
19-
tokenizer::{Token, TokenReference, TokenType},
19+
tokenizer::{Token, TokenKind, TokenReference, TokenType},
2020
};
2121

2222
/// Used to provide information about the table
@@ -418,6 +418,7 @@ fn should_expand(table_constructor: &TableConstructor) -> bool {
418418
return true;
419419
}
420420
}
421+
421422
false
422423
}
423424
}
@@ -427,6 +428,8 @@ pub fn format_table_constructor(
427428
table_constructor: &TableConstructor,
428429
shape: Shape,
429430
) -> TableConstructor {
431+
const BRACE_LEN: usize = "{".len();
432+
430433
let (start_brace, end_brace) = table_constructor.braces().tokens();
431434

432435
// Determine if we need to force the table multiline
@@ -437,38 +440,66 @@ pub fn format_table_constructor(
437440
(true, _) => TableType::MultiLine,
438441

439442
(false, Some(_)) => {
440-
// Compare the difference between the position of the start brace and the end brace to
441-
// guess how long the table is. This heuristic is very naiive, since it relies on the input.
442-
// If the input is badly formatted (e.g. lots of spaces in the table), then it would flag this over width.
443-
// However, this is currently our best solution: attempting to format the input onto a single line to
444-
// see if we are over width (both completely and in a fail-fast shape.over_budget() check) leads to
445-
// exponential time complexity with respect to how deep the table is.
446-
// TODO: find an improved heuristic whilst comparing against benchmarks
447-
let braces_range = (
448-
// Use the position of the last trivia in case there is some present (e.g. whitespace)
449-
// So that we don't include an extra space
450-
if let Some(token) = start_brace.leading_trivia().last() {
451-
token.end_position().bytes()
452-
} else {
453-
start_brace.token().end_position().bytes()
454-
},
455-
end_brace.token().start_position().bytes(),
456-
);
457-
let singleline_shape = shape + (braces_range.1 - braces_range.0) + 3; // 4 = two braces + single space before last brace
458-
459-
match singleline_shape.over_budget() {
460-
true => TableType::MultiLine,
461-
false => {
462-
// Determine if there was a new line at the end of the start brace
463-
// If so, then we should always be multiline
464-
if start_brace
465-
.trailing_trivia()
466-
.any(trivia_util::trivia_is_newline)
467-
{
468-
TableType::MultiLine
443+
// Determine if there was a new line at the end of the start brace
444+
// If so, then we should always be multiline
445+
if start_brace
446+
.trailing_trivia()
447+
.any(trivia_util::trivia_is_newline)
448+
{
449+
TableType::MultiLine
450+
} else {
451+
// Compare the difference between the position of the start brace and the end brace to
452+
// guess how long the table is. This heuristic is very naiive, since it relies on the input.
453+
// If the input is badly formatted (e.g. lots of spaces in the table), then it would flag this over width.
454+
// However, this is currently our best solution: attempting to format the input onto a single line to
455+
// see if we are over width (both completely and in a fail-fast shape.over_budget() check) leads to
456+
// exponential time complexity with respect to how deep the table is.
457+
// TODO: find an improved heuristic whilst comparing against benchmarks
458+
let braces_range = (
459+
// Use the position of the last trivia in case there is some present (e.g. whitespace)
460+
// So that we don't include an extra space
461+
if let Some(token) = start_brace.leading_trivia().last() {
462+
token.end_position().bytes()
469463
} else {
470-
TableType::SingleLine
471-
}
464+
start_brace.token().end_position().bytes()
465+
},
466+
end_brace.token().start_position().bytes(),
467+
);
468+
469+
let last_field = table_constructor
470+
.fields()
471+
.last()
472+
.expect("atleast one field must be present");
473+
474+
// See if we need to +1 because we will be adding spaces
475+
let additional_shape = match (
476+
start_brace
477+
.trailing_trivia()
478+
.any(|x| x.token_kind() == TokenKind::Whitespace),
479+
// A space will be present on the end of the last field, not the start of the end brace
480+
match (last_field.value(), last_field.punctuation()) {
481+
(_, Some(token)) => token
482+
.trailing_trivia()
483+
.any(|x| x.token_kind() == TokenKind::Whitespace),
484+
(field, None) => table_field_trailing_trivia(field)
485+
.iter()
486+
.any(|x| x.token_kind() == TokenKind::Whitespace),
487+
},
488+
) {
489+
(true, true) => 0,
490+
(true, false) | (false, true) => 1,
491+
(false, false) => 2,
492+
};
493+
494+
let singleline_shape = shape
495+
+ (braces_range.1 - braces_range.0)
496+
+ additional_shape
497+
+ BRACE_LEN
498+
+ BRACE_LEN;
499+
500+
match singleline_shape.over_budget() {
501+
true => TableType::MultiLine,
502+
false => TableType::SingleLine,
472503
}
473504
}
474505
}

src/formatters/trivia_util.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ pub fn spans_multiple_lines<T: std::fmt::Display>(item: &T) -> bool {
6161

6262
pub fn can_hang_expression(expression: &Expression) -> bool {
6363
match expression {
64-
Expression::Parentheses { expression, .. } => can_hang_expression(expression),
64+
Expression::Parentheses { .. } => true, // Can always hang parentheses if necessary
6565
Expression::UnaryOperator { expression, .. } => can_hang_expression(expression),
6666
Expression::BinaryOperator { .. } => true, // If a binop is present, then we can hang the expression
6767
Expression::Value { value, .. } => match &**value {
@@ -1113,6 +1113,15 @@ pub fn table_fields_contains_comments(table_constructor: &TableConstructor) -> b
11131113
})
11141114
}
11151115

1116+
pub fn table_field_trailing_trivia(field: &Field) -> Vec<Token> {
1117+
match field {
1118+
Field::ExpressionKey { value, .. } => get_expression_trailing_trivia(value),
1119+
Field::NameKey { value, .. } => get_expression_trailing_trivia(value),
1120+
Field::NoKey(expression) => get_expression_leading_trivia(expression),
1121+
other => panic!("unknown node {:?}", other),
1122+
}
1123+
}
1124+
11161125
// Checks to see whether an expression contains comments inline inside of it
11171126
// This can only happen if the expression is a BinOp
11181127
// We should ignore any comments which are trailing for the whole expression, as they are not inline
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
-- https://github.com/JohnnyMorganz/StyLua/issues/439
2+
exports.separateDisplayNameAndHOCs =
3+
function(displayName: string | nil, type_: ElementType): (string | nil, Array<string> | nil)
4+
if displayName == nil then
5+
return nil, nil
6+
end
7+
8+
local hocDisplayNames: Array<string>? = nil
9+
10+
if
11+
type_ == ElementTypeClass
12+
or type_ == ElementTypeForwardRef
13+
or type_ == ElementTypeFunction
14+
or type_ == ElementTypeMemo
15+
then
16+
-- ROBLOX deviation: use match instead of indexOf
17+
if (displayName :: string):match("%(") then
18+
-- ROBLOX deviation: use gmatch instead of /[^()]+/g
19+
local matches = (displayName :: string):gmatch("[^()]+")
20+
local nextMatch = matches()
21+
if nextMatch then
22+
displayName = nextMatch
23+
hocDisplayNames = {}
24+
while nextMatch :: any ~= nil do
25+
nextMatch = matches()
26+
table.insert(hocDisplayNames :: Array<string>, nextMatch)
27+
end
28+
end
29+
end
30+
end
31+
end

tests/inputs/table-7.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- https://github.com/JohnnyMorganz/StyLua/issues/436
2+
local OffsetEnum = {aValue = 10, anotherValue = 11, yetAnotherValue = 12, reset = 0, postReset = 1, aaaaaaaaaaa = true}
3+
4+
local OffsetEnum = { aValue = 10, anotherValue = 11, yetAnotherValue = 12, reset = 0, postReset = 1, aaaaaaaaa = true }
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
---
2+
source: tests/tests.rs
3+
assertion_line: 30
4+
expression: format(&contents)
5+
6+
---
7+
-- https://github.com/JohnnyMorganz/StyLua/issues/439
8+
exports.separateDisplayNameAndHOCs =
9+
function(displayName: string | nil, type_: ElementType): (string | nil, Array<string> | nil)
10+
if displayName == nil then
11+
return nil, nil
12+
end
13+
14+
local hocDisplayNames: Array<string>? = nil
15+
16+
if
17+
type_ == ElementTypeClass
18+
or type_ == ElementTypeForwardRef
19+
or type_ == ElementTypeFunction
20+
or type_ == ElementTypeMemo
21+
then
22+
-- ROBLOX deviation: use match instead of indexOf
23+
if (displayName :: string):match("%(") then
24+
-- ROBLOX deviation: use gmatch instead of /[^()]+/g
25+
local matches = (displayName :: string):gmatch("[^()]+")
26+
local nextMatch = matches()
27+
if nextMatch then
28+
displayName = nextMatch
29+
hocDisplayNames = {}
30+
while nextMatch :: any ~= nil do
31+
nextMatch = matches()
32+
table.insert(hocDisplayNames :: Array<string>, nextMatch)
33+
end
34+
end
35+
end
36+
end
37+
end
38+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
source: tests/tests.rs
3+
assertion_line: 12
4+
expression: format(&contents)
5+
6+
---
7+
-- https://github.com/JohnnyMorganz/StyLua/issues/436
8+
local OffsetEnum = {
9+
aValue = 10,
10+
anotherValue = 11,
11+
yetAnotherValue = 12,
12+
reset = 0,
13+
postReset = 1,
14+
aaaaaaaaaaa = true,
15+
}
16+
17+
local OffsetEnum = { aValue = 10, anotherValue = 11, yetAnotherValue = 12, reset = 0, postReset = 1, aaaaaaaaa = true }
18+

0 commit comments

Comments
 (0)