From a5cc5e4fa42833ac28d2bc5299da9568243585ff Mon Sep 17 00:00:00 2001 From: D Date: Tue, 1 Nov 2022 14:48:47 +1100 Subject: [PATCH 01/11] conditionals: add support for nested indentation --- src/configuration/builder.rs | 9 ++++- src/configuration/resolve_config.rs | 2 ++ src/configuration/types.rs | 3 ++ src/generation/generate.rs | 28 ++++++++++----- ...alExpression_UseNestedIndentation_True.txt | 34 +++++++++++++++++++ 5 files changed, 67 insertions(+), 9 deletions(-) create mode 100644 tests/specs/expressions/ConditionalExpression/ConditionalExpression_UseNestedIndentation_True.txt diff --git a/src/configuration/builder.rs b/src/configuration/builder.rs index e813bc90..878caab8 100644 --- a/src/configuration/builder.rs +++ b/src/configuration/builder.rs @@ -747,6 +747,11 @@ impl ConfigurationBuilder { self.insert("whileStatement.preferHanging", value.into()) } + /* situational indentation */ + pub fn conditional_expression_use_nested_indentation(&mut self, value: bool) -> &mut Self { + self.insert("conditionalExpression.useNestedIndentation", value.into()) + } + /* force single line */ pub fn export_declaration_force_single_line(&mut self, value: bool) -> &mut Self { @@ -1138,6 +1143,8 @@ mod tests { .union_and_intersection_type_prefer_hanging(true) .variable_statement_prefer_hanging(true) .while_statement_prefer_hanging(true) + /* situational indentation */ + .conditional_expression_use_nested_indentation(false) /* member spacing */ .enum_declaration_member_spacing(MemberSpacing::Maintain) /* next control flow position */ @@ -1246,7 +1253,7 @@ mod tests { .while_statement_space_around(true); let inner_config = config.get_inner_config(); - assert_eq!(inner_config.len(), 175); + assert_eq!(inner_config.len(), 176); let diagnostics = resolve_config(inner_config, &resolve_global_config(ConfigKeyMap::new(), &Default::default()).config).diagnostics; assert_eq!(diagnostics.len(), 0); } diff --git a/src/configuration/resolve_config.rs b/src/configuration/resolve_config.rs index f489ad5a..4cd82412 100644 --- a/src/configuration/resolve_config.rs +++ b/src/configuration/resolve_config.rs @@ -169,6 +169,8 @@ pub fn resolve_config(config: ConfigKeyMap, global_config: &GlobalConfiguration) union_and_intersection_type_prefer_hanging: get_value(&mut config, "unionAndIntersectionType.preferHanging", prefer_hanging, &mut diagnostics), variable_statement_prefer_hanging: get_value(&mut config, "variableStatement.preferHanging", prefer_hanging, &mut diagnostics), while_statement_prefer_hanging: get_value(&mut config, "whileStatement.preferHanging", prefer_hanging, &mut diagnostics), + /* situational indentation */ + conditional_expression_use_nested_indentation: get_value(&mut config, "conditionalExpression.useNestedIndentation", false, &mut diagnostics), /* member spacing */ enum_declaration_member_spacing: get_value(&mut config, "enumDeclaration.memberSpacing", MemberSpacing::Maintain, &mut diagnostics), /* next control flow position */ diff --git a/src/configuration/types.rs b/src/configuration/types.rs index 4476ba6c..05786955 100644 --- a/src/configuration/types.rs +++ b/src/configuration/types.rs @@ -405,6 +405,9 @@ pub struct Configuration { pub variable_statement_prefer_hanging: bool, #[serde(rename = "whileStatement.preferHanging")] pub while_statement_prefer_hanging: bool, + /* situational indentation */ + #[serde(rename = "conditionalExpression.useNestedIndentation")] + pub conditional_expression_use_nested_indentation: bool, /* member spacing */ #[serde(rename = "enumDeclaration.memberSpacing")] pub enum_declaration_member_spacing: MemberSpacing, diff --git a/src/generation/generate.rs b/src/generation/generate.rs index 09d69525..23f6bab7 100644 --- a/src/generation/generate.rs +++ b/src/generation/generate.rs @@ -2180,6 +2180,9 @@ fn gen_conditional_expr<'a>(node: &'a CondExpr, context: &mut Context<'a>) -> Pr force_test_cons_newline = true; force_cons_alt_newline = true; } + let is_parent_conditional_expr = node.parent().kind() == NodeKind::CondExpr; + let is_nested_conditional = is_parent_conditional_expr || node.cons.kind() == NodeKind::CondExpr || node.alt.kind() == NodeKind::CondExpr; + let use_new_lines_for_nested_conditional = context.config.conditional_expression_use_nested_indentation && is_nested_conditional; let (question_position, colon_position) = get_operator_position(node, question_token, colon_token, context); let top_most_data = get_top_most_data(node, context); @@ -2210,7 +2213,7 @@ fn gen_conditional_expr<'a>(node: &'a CondExpr, context: &mut Context<'a>) -> Pr items.push_anchor(LineNumberAnchor::new(end_ln)); items.push_anchor(LineNumberAnchor::new(before_alternate_ln)); - let multi_line_reevaluation = if force_test_cons_newline { + let multi_line_reevaluation = if force_test_cons_newline || use_new_lines_for_nested_conditional { items.push_signal(Signal::NewLine); None } else if line_per_expression { @@ -2252,7 +2255,7 @@ fn gen_conditional_expr<'a>(node: &'a CondExpr, context: &mut Context<'a>) -> Pr items.extend(colon_comment_items.previous_lines); - if force_cons_alt_newline { + if force_cons_alt_newline || use_new_lines_for_nested_conditional { items.push_signal(Signal::NewLine); } else if line_per_expression { items.push_condition(conditions::new_line_if_multiple_lines_space_or_new_line_otherwise( @@ -2282,7 +2285,7 @@ fn gen_conditional_expr<'a>(node: &'a CondExpr, context: &mut Context<'a>) -> Pr items }; - if top_most_data.is_top_most { + if use_new_lines_for_nested_conditional { items.push_condition(conditions::indent_if_start_of_line(cons_and_alt_items)); } else { items.push_condition(indent_if_sol_and_same_indent_as_top_most(cons_and_alt_items, top_most_data.il)); @@ -5212,10 +5215,13 @@ fn gen_array_type<'a>(node: &'a TsArrayType, context: &mut Context<'a>) -> Print } fn gen_conditional_type<'a>(node: &'a TsConditionalType, context: &mut Context<'a>) -> PrintItems { - let use_new_lines = - !context.config.conditional_type_prefer_single_line && node_helpers::get_use_new_lines_for_nodes(&node.true_type, &node.false_type, context.program); let top_most_data = get_top_most_data(node, context); let is_parent_conditional_type = node.parent().kind() == NodeKind::TsConditionalType; + let is_nested_conditional = + is_parent_conditional_type || node.true_type.kind() == NodeKind::TsConditionalType || node.false_type.kind() == NodeKind::TsConditionalType; + let use_new_lines_for_nested_conditional = context.config.conditional_expression_use_nested_indentation && is_nested_conditional; + let force_new_lines_for_false_type = + !context.config.conditional_type_prefer_single_line && node_helpers::get_use_new_lines_for_nodes(&node.true_type, &node.false_type, context.program); let mut items = PrintItems::new(); let before_false_ln = LineNumber::new("beforeFalse"); let question_token = context.token_finder.get_first_operator_after(&node.extends_type, "?").unwrap(); @@ -5243,7 +5249,9 @@ fn gen_conditional_type<'a>(node: &'a TsConditionalType, context: &mut Context<' items }))); - if question_comment_items.previous_lines.is_empty() { + if use_new_lines_for_nested_conditional { + items.push_signal(Signal::NewLine); + } else if question_comment_items.previous_lines.is_empty() { items.push_signal(Signal::SpaceOrNewLine); } else { items.extend(question_comment_items.previous_lines); @@ -5275,7 +5283,7 @@ fn gen_conditional_type<'a>(node: &'a TsConditionalType, context: &mut Context<' items.extend(colon_comment_items.previous_lines); // false type - if use_new_lines { + if force_new_lines_for_false_type || use_new_lines_for_nested_conditional { items.push_signal(Signal::NewLine); } else { items.push_condition(conditions::new_line_if_multiple_lines_space_or_new_line_otherwise( @@ -5303,7 +5311,11 @@ fn gen_conditional_type<'a>(node: &'a TsConditionalType, context: &mut Context<' items }; - if is_parent_conditional_type { + // Unless `use_nested_indentation` is enabled, we keep the indentation the same. e.g.: + // type A = T extends string ? 'string' + // : T extends number ? 'number' + // : T extends boolean ? 'boolean'; + if !use_new_lines_for_nested_conditional && is_parent_conditional_type { items.extend(false_type_generated); } else { items.push_condition(conditions::indent_if_start_of_line(false_type_generated)); diff --git a/tests/specs/expressions/ConditionalExpression/ConditionalExpression_UseNestedIndentation_True.txt b/tests/specs/expressions/ConditionalExpression/ConditionalExpression_UseNestedIndentation_True.txt new file mode 100644 index 00000000..ffeb19b8 --- /dev/null +++ b/tests/specs/expressions/ConditionalExpression/ConditionalExpression_UseNestedIndentation_True.txt @@ -0,0 +1,34 @@ +~~ lineWidth: 80, conditionalExpression.useNestedIndentation: true ~~ +== should handle nested indentation on a basic ternary / conditional expression == +const value = is_prod +? do1() +: is_laptop +? do2() +: do3(); + +[expect] +const value = is_prod + ? do1() + : is_laptop + ? do2() + : do3(); + +== should handle nested indentation with extends and mapped types == +export type R = S extends Record> ? { [K in keyof S]: ReturnType } : S extends T +? _T extends Array +? I +: _T : S extends SZ ? _T : S extends DZ +? _T : never; + +[expect] +export type R = S extends Record> + ? { [K in keyof S]: ReturnType } + : S extends T + ? _T extends Array + ? I + : _T + : S extends SZ + ? _T + : S extends DZ + ? _T + : never; From 273275c2918b1b9434d0a30759f8e981d388f2a2 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Sat, 3 Dec 2022 16:03:30 -0500 Subject: [PATCH 02/11] Update schema.json --- deployment/schema.json | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/deployment/schema.json b/deployment/schema.json index 8f73699f..18c5932c 100644 --- a/deployment/schema.json +++ b/deployment/schema.json @@ -318,18 +318,6 @@ "description": "Maintains the line breaks as written by the programmer." }] }, - "conditionalExpression.linePerExpression": { - "description": "Whether to force a line per expression when spanning multiple lines.", - "type": "boolean", - "default": true, - "oneOf": [{ - "const": true, - "description": "Formats with each part on a new line." - }, { - "const": false, - "description": "Maintains the line breaks as written by the programmer." - }] - }, "memberExpression.linePerExpression": { "description": "Whether to force a line per expression when spanning multiple lines.", "type": "boolean", @@ -793,6 +781,30 @@ "binaryExpression.linePerExpression": { "$ref": "#/definitions/binaryExpression.linePerExpression" }, + "conditionalExpression.linePerExpression": { + "description": "Whether to force a line per expression when spanning multiple lines.", + "type": "boolean", + "default": true, + "oneOf": [{ + "const": true, + "description": "Formats with each part on a new line." + }, { + "const": false, + "description": "Maintains the line breaks as written by the programmer." + }] + }, + "conditionalExpression.useNestedIndentation": { + "description": "Whether to use nested indentation within conditional expressions.", + "type": "boolean", + "default": true, + "oneOf": [{ + "const": true, + "description": "Uses nested indentation in the true and false expressions." + }, { + "const": false, + "description": "Don't use nested indentation." + }] + }, "jsx.bracketPosition": { "$ref": "#/definitions/jsx.bracketPosition" }, From 38a9be5277ddc02ed7ffcf951581363e4f378701 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Sat, 3 Dec 2022 16:20:21 -0500 Subject: [PATCH 03/11] Move out conditional type from conditional expression. --- deployment/schema.json | 12 +++++++++++ src/configuration/builder.rs | 8 +++++++- src/configuration/resolve_config.rs | 1 + src/configuration/types.rs | 2 ++ src/generation/generate.rs | 2 +- ...alExpression_UseNestedIndentation_True.txt | 20 ------------------- ...ditionalType_UseNestedIndentation_True.txt | 20 +++++++++++++++++++ 7 files changed, 43 insertions(+), 22 deletions(-) create mode 100644 tests/specs/types/ConditionalType/ConditionalType_UseNestedIndentation_True.txt diff --git a/deployment/schema.json b/deployment/schema.json index 18c5932c..88c2ed14 100644 --- a/deployment/schema.json +++ b/deployment/schema.json @@ -805,6 +805,18 @@ "description": "Don't use nested indentation." }] }, + "conditionalType.useNestedIndentation": { + "description": "Whether to use nested indentation within conditional types.", + "type": "boolean", + "default": true, + "oneOf": [{ + "const": true, + "description": "Uses nested indentation in the true and false expressions." + }, { + "const": false, + "description": "Don't use nested indentation." + }] + }, "jsx.bracketPosition": { "$ref": "#/definitions/jsx.bracketPosition" }, diff --git a/src/configuration/builder.rs b/src/configuration/builder.rs index 878caab8..ca4e216b 100644 --- a/src/configuration/builder.rs +++ b/src/configuration/builder.rs @@ -748,10 +748,15 @@ impl ConfigurationBuilder { } /* situational indentation */ + pub fn conditional_expression_use_nested_indentation(&mut self, value: bool) -> &mut Self { self.insert("conditionalExpression.useNestedIndentation", value.into()) } + pub fn conditional_type_use_nested_indentation(&mut self, value: bool) -> &mut Self { + self.insert("conditionalType.useNestedIndentation", value.into()) + } + /* force single line */ pub fn export_declaration_force_single_line(&mut self, value: bool) -> &mut Self { @@ -1145,6 +1150,7 @@ mod tests { .while_statement_prefer_hanging(true) /* situational indentation */ .conditional_expression_use_nested_indentation(false) + .conditional_type_use_nested_indentation(false) /* member spacing */ .enum_declaration_member_spacing(MemberSpacing::Maintain) /* next control flow position */ @@ -1253,7 +1259,7 @@ mod tests { .while_statement_space_around(true); let inner_config = config.get_inner_config(); - assert_eq!(inner_config.len(), 176); + assert_eq!(inner_config.len(), 177); let diagnostics = resolve_config(inner_config, &resolve_global_config(ConfigKeyMap::new(), &Default::default()).config).diagnostics; assert_eq!(diagnostics.len(), 0); } diff --git a/src/configuration/resolve_config.rs b/src/configuration/resolve_config.rs index 4cd82412..c39bdb5d 100644 --- a/src/configuration/resolve_config.rs +++ b/src/configuration/resolve_config.rs @@ -171,6 +171,7 @@ pub fn resolve_config(config: ConfigKeyMap, global_config: &GlobalConfiguration) while_statement_prefer_hanging: get_value(&mut config, "whileStatement.preferHanging", prefer_hanging, &mut diagnostics), /* situational indentation */ conditional_expression_use_nested_indentation: get_value(&mut config, "conditionalExpression.useNestedIndentation", false, &mut diagnostics), + conditional_type_use_nested_indentation: get_value(&mut config, "conditionalType.useNestedIndentation", false, &mut diagnostics), /* member spacing */ enum_declaration_member_spacing: get_value(&mut config, "enumDeclaration.memberSpacing", MemberSpacing::Maintain, &mut diagnostics), /* next control flow position */ diff --git a/src/configuration/types.rs b/src/configuration/types.rs index 05786955..18ba6c11 100644 --- a/src/configuration/types.rs +++ b/src/configuration/types.rs @@ -408,6 +408,8 @@ pub struct Configuration { /* situational indentation */ #[serde(rename = "conditionalExpression.useNestedIndentation")] pub conditional_expression_use_nested_indentation: bool, + #[serde(rename = "conditionalType.useNestedIndentation")] + pub conditional_type_use_nested_indentation: bool, /* member spacing */ #[serde(rename = "enumDeclaration.memberSpacing")] pub enum_declaration_member_spacing: MemberSpacing, diff --git a/src/generation/generate.rs b/src/generation/generate.rs index 23f6bab7..d97ef96a 100644 --- a/src/generation/generate.rs +++ b/src/generation/generate.rs @@ -5219,7 +5219,7 @@ fn gen_conditional_type<'a>(node: &'a TsConditionalType, context: &mut Context<' let is_parent_conditional_type = node.parent().kind() == NodeKind::TsConditionalType; let is_nested_conditional = is_parent_conditional_type || node.true_type.kind() == NodeKind::TsConditionalType || node.false_type.kind() == NodeKind::TsConditionalType; - let use_new_lines_for_nested_conditional = context.config.conditional_expression_use_nested_indentation && is_nested_conditional; + let use_new_lines_for_nested_conditional = context.config.conditional_type_use_nested_indentation && is_nested_conditional; let force_new_lines_for_false_type = !context.config.conditional_type_prefer_single_line && node_helpers::get_use_new_lines_for_nodes(&node.true_type, &node.false_type, context.program); let mut items = PrintItems::new(); diff --git a/tests/specs/expressions/ConditionalExpression/ConditionalExpression_UseNestedIndentation_True.txt b/tests/specs/expressions/ConditionalExpression/ConditionalExpression_UseNestedIndentation_True.txt index ffeb19b8..895e2cca 100644 --- a/tests/specs/expressions/ConditionalExpression/ConditionalExpression_UseNestedIndentation_True.txt +++ b/tests/specs/expressions/ConditionalExpression/ConditionalExpression_UseNestedIndentation_True.txt @@ -12,23 +12,3 @@ const value = is_prod : is_laptop ? do2() : do3(); - -== should handle nested indentation with extends and mapped types == -export type R = S extends Record> ? { [K in keyof S]: ReturnType } : S extends T -? _T extends Array -? I -: _T : S extends SZ ? _T : S extends DZ -? _T : never; - -[expect] -export type R = S extends Record> - ? { [K in keyof S]: ReturnType } - : S extends T - ? _T extends Array - ? I - : _T - : S extends SZ - ? _T - : S extends DZ - ? _T - : never; diff --git a/tests/specs/types/ConditionalType/ConditionalType_UseNestedIndentation_True.txt b/tests/specs/types/ConditionalType/ConditionalType_UseNestedIndentation_True.txt new file mode 100644 index 00000000..3293cea8 --- /dev/null +++ b/tests/specs/types/ConditionalType/ConditionalType_UseNestedIndentation_True.txt @@ -0,0 +1,20 @@ +~~ lineWidth: 80, conditionalType.useNestedIndentation: true ~~ +== should handle nested indentation with extends and mapped types == +export type R = S extends Record> ? { [K in keyof S]: ReturnType } : S extends T +? _T extends Array +? I +: _T : S extends SZ ? _T : S extends DZ +? _T : never; + +[expect] +export type R = S extends Record> + ? { [K in keyof S]: ReturnType } + : S extends T + ? _T extends Array + ? I + : _T + : S extends SZ + ? _T + : S extends DZ + ? _T + : never; From 2c9873d353299d1f3b49df8df6e9f1e8b84b13f2 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Mon, 5 Dec 2022 22:04:14 -0500 Subject: [PATCH 04/11] Add back top most data check and only analyze nodes if this option is enabled --- src/generation/generate.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/generation/generate.rs b/src/generation/generate.rs index d97ef96a..76f77c02 100644 --- a/src/generation/generate.rs +++ b/src/generation/generate.rs @@ -2180,9 +2180,9 @@ fn gen_conditional_expr<'a>(node: &'a CondExpr, context: &mut Context<'a>) -> Pr force_test_cons_newline = true; force_cons_alt_newline = true; } - let is_parent_conditional_expr = node.parent().kind() == NodeKind::CondExpr; - let is_nested_conditional = is_parent_conditional_expr || node.cons.kind() == NodeKind::CondExpr || node.alt.kind() == NodeKind::CondExpr; - let use_new_lines_for_nested_conditional = context.config.conditional_expression_use_nested_indentation && is_nested_conditional; + let use_new_lines_for_nested_conditional = context.config.conditional_expression_use_nested_indentation && { + node.parent().kind() == NodeKind::CondExpr || node.cons.kind() == NodeKind::CondExpr || node.alt.kind() == NodeKind::CondExpr + }; let (question_position, colon_position) = get_operator_position(node, question_token, colon_token, context); let top_most_data = get_top_most_data(node, context); @@ -2285,7 +2285,7 @@ fn gen_conditional_expr<'a>(node: &'a CondExpr, context: &mut Context<'a>) -> Pr items }; - if use_new_lines_for_nested_conditional { + if top_most_data.is_top_most || use_new_lines_for_nested_conditional { items.push_condition(conditions::indent_if_start_of_line(cons_and_alt_items)); } else { items.push_condition(indent_if_sol_and_same_indent_as_top_most(cons_and_alt_items, top_most_data.il)); @@ -5217,9 +5217,9 @@ fn gen_array_type<'a>(node: &'a TsArrayType, context: &mut Context<'a>) -> Print fn gen_conditional_type<'a>(node: &'a TsConditionalType, context: &mut Context<'a>) -> PrintItems { let top_most_data = get_top_most_data(node, context); let is_parent_conditional_type = node.parent().kind() == NodeKind::TsConditionalType; - let is_nested_conditional = - is_parent_conditional_type || node.true_type.kind() == NodeKind::TsConditionalType || node.false_type.kind() == NodeKind::TsConditionalType; - let use_new_lines_for_nested_conditional = context.config.conditional_type_use_nested_indentation && is_nested_conditional; + let use_new_lines_for_nested_conditional = context.config.conditional_type_use_nested_indentation && { + is_parent_conditional_type || node.true_type.kind() == NodeKind::TsConditionalType || node.false_type.kind() == NodeKind::TsConditionalType + }; let force_new_lines_for_false_type = !context.config.conditional_type_prefer_single_line && node_helpers::get_use_new_lines_for_nodes(&node.true_type, &node.false_type, context.program); let mut items = PrintItems::new(); From 0de6f0105c11bbb991e3c71763413d7ca49b7603 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Mon, 5 Dec 2022 22:18:04 -0500 Subject: [PATCH 05/11] Add failing tests. --- ...alExpression_UseNestedIndentation_True.txt | 26 ++++++++++++++++++- ...ditionalType_UseNestedIndentation_True.txt | 6 +++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/tests/specs/expressions/ConditionalExpression/ConditionalExpression_UseNestedIndentation_True.txt b/tests/specs/expressions/ConditionalExpression/ConditionalExpression_UseNestedIndentation_True.txt index 895e2cca..cd243087 100644 --- a/tests/specs/expressions/ConditionalExpression/ConditionalExpression_UseNestedIndentation_True.txt +++ b/tests/specs/expressions/ConditionalExpression/ConditionalExpression_UseNestedIndentation_True.txt @@ -1,4 +1,4 @@ -~~ lineWidth: 80, conditionalExpression.useNestedIndentation: true ~~ +~~ lineWidth: 50, conditionalExpression.useNestedIndentation: true ~~ == should handle nested indentation on a basic ternary / conditional expression == const value = is_prod ? do1() @@ -12,3 +12,27 @@ const value = is_prod : is_laptop ? do2() : do3(); + +== should keep on single line if fits on a single line == +const value = a ? b ? c : d : e; + +[expect] +const value = a ? b ? c : d : e; + +== should go multi-line when exceeding the line width == +const value = testing ? this ? out : testing : exceedsWidth; + +[expect] +const value = testing + ? this ? out : testing + : exceedsWidth; + +== should both go multi-line when exceeding the line width twice == +const value = testing ? this ? out : testingTestingTestingTestingTesting : exceedsWidth; + +[expect] +const value = testing + ? this + ? out + : testingTestingTestingTestingTesting + : exceedsWidth; diff --git a/tests/specs/types/ConditionalType/ConditionalType_UseNestedIndentation_True.txt b/tests/specs/types/ConditionalType/ConditionalType_UseNestedIndentation_True.txt index 3293cea8..5b990532 100644 --- a/tests/specs/types/ConditionalType/ConditionalType_UseNestedIndentation_True.txt +++ b/tests/specs/types/ConditionalType/ConditionalType_UseNestedIndentation_True.txt @@ -18,3 +18,9 @@ export type R = S extends Record> : S extends DZ ? _T : never; + +== should keep on single line if fits on a single line == +type Test = A extends B ? C extends D ? c : d : e; + +[expect] +type Test = A extends B ? C extends D ? c : d : e; From 2424d3e645f42310393e9692184896f215fc7c6e Mon Sep 17 00:00:00 2001 From: Declan Vong Date: Tue, 20 Dec 2022 22:35:15 +1100 Subject: [PATCH 06/11] . --- src/configuration/builder.rs | 11 ++++- src/configuration/resolve_config.rs | 1 + src/configuration/types.rs | 2 + src/generation/generate.rs | 42 ++++++++++++------- ...stedIndentation_LinePerExpression_True.txt | 26 ++++++++++++ ...ditionalType_UseNestedIndentation_True.txt | 3 +- 6 files changed, 67 insertions(+), 18 deletions(-) create mode 100644 tests/specs/types/ConditionalType/ConditionalType_UseNestedIndentation_LinePerExpression_True.txt diff --git a/src/configuration/builder.rs b/src/configuration/builder.rs index ca4e216b..eb092133 100644 --- a/src/configuration/builder.rs +++ b/src/configuration/builder.rs @@ -490,6 +490,14 @@ impl ConfigurationBuilder { self.insert("conditionalExpression.linePerExpression", value.into()) } + /// Whether to force a line per expression when spanning multiple lines. + /// + /// * `true` - Formats with each part on a new line. + /// * `false` (default) - Maintains the line breaks as written by the programmer. + pub fn conditional_type_line_per_expression(&mut self, value: bool) -> &mut Self { + self.insert("conditionalType.linePerExpression", value.into()) + } + /// Whether to force a line per expression when spanning multiple lines. /// /// * `true` - Formats with each part on a new line. @@ -1088,6 +1096,7 @@ mod tests { .arrow_function_use_parentheses(UseParentheses::Maintain) .binary_expression_line_per_expression(false) .conditional_expression_line_per_expression(true) + .conditional_type_line_per_expression(true) .member_expression_line_per_expression(false) .type_literal_separator_kind(SemiColonOrComma::Comma) .type_literal_separator_kind_single_line(SemiColonOrComma::Comma) @@ -1259,7 +1268,7 @@ mod tests { .while_statement_space_around(true); let inner_config = config.get_inner_config(); - assert_eq!(inner_config.len(), 177); + assert_eq!(inner_config.len(), 178); let diagnostics = resolve_config(inner_config, &resolve_global_config(ConfigKeyMap::new(), &Default::default()).config).diagnostics; assert_eq!(diagnostics.len(), 0); } diff --git a/src/configuration/resolve_config.rs b/src/configuration/resolve_config.rs index c39bdb5d..8939ee85 100644 --- a/src/configuration/resolve_config.rs +++ b/src/configuration/resolve_config.rs @@ -95,6 +95,7 @@ pub fn resolve_config(config: ConfigKeyMap, global_config: &GlobalConfiguration) arrow_function_use_parentheses: get_value(&mut config, "arrowFunction.useParentheses", UseParentheses::Maintain, &mut diagnostics), binary_expression_line_per_expression: get_value(&mut config, "binaryExpression.linePerExpression", false, &mut diagnostics), conditional_expression_line_per_expression: get_value(&mut config, "conditionalExpression.linePerExpression", true, &mut diagnostics), + conditional_type_line_per_expression: get_value(&mut config, "conditionalType.linePerExpression", false, &mut diagnostics), jsx_quote_style: get_value(&mut config, "jsx.quoteStyle", quote_style.to_jsx_quote_style(), &mut diagnostics), jsx_multi_line_parens: get_value(&mut config, "jsx.multiLineParens", JsxMultiLineParens::Prefer, &mut diagnostics), jsx_force_new_lines_surrounding_content: get_value(&mut config, "jsx.forceNewLinesSurroundingContent", false, &mut diagnostics), diff --git a/src/configuration/types.rs b/src/configuration/types.rs index 18ba6c11..591c3ab5 100644 --- a/src/configuration/types.rs +++ b/src/configuration/types.rs @@ -283,6 +283,8 @@ pub struct Configuration { pub binary_expression_line_per_expression: bool, #[serde(rename = "conditionalExpression.linePerExpression")] pub conditional_expression_line_per_expression: bool, + #[serde(rename = "conditionalType.linePerExpression")] + pub conditional_type_line_per_expression: bool, #[serde(rename = "jsx.quoteStyle")] pub jsx_quote_style: JsxQuoteStyle, #[serde(rename = "jsx.multiLineParens")] diff --git a/src/generation/generate.rs b/src/generation/generate.rs index 76f77c02..9a26b988 100644 --- a/src/generation/generate.rs +++ b/src/generation/generate.rs @@ -2172,17 +2172,19 @@ fn gen_conditional_expr<'a>(node: &'a CondExpr, context: &mut Context<'a>) -> Pr let colon_token = context.token_finder.get_first_operator_after(&node.cons, ":").unwrap(); let line_per_expression = context.config.conditional_expression_line_per_expression; let has_newline_test_cons = node_helpers::get_use_new_lines_for_nodes(&node.test, &node.cons, context.program); - let has_newline_const_alt = node_helpers::get_use_new_lines_for_nodes(&node.cons, &node.alt, context.program); - let mut force_test_cons_newline = !context.config.conditional_expression_prefer_single_line && has_newline_test_cons; - let mut force_cons_alt_newline = !context.config.conditional_expression_prefer_single_line && has_newline_const_alt; + let has_newline_cons_alt = node_helpers::get_use_new_lines_for_nodes(&node.cons, &node.alt, context.program); + let use_new_lines_for_nested_conditional = context.config.conditional_expression_use_nested_indentation && { + node.parent().kind() == NodeKind::CondExpr || node.cons.kind() == NodeKind::CondExpr || node.alt.kind() == NodeKind::CondExpr + }; + let mut force_test_cons_newline = + has_newline_test_cons && (!context.config.conditional_expression_prefer_single_line || use_new_lines_for_nested_conditional); + let mut force_cons_alt_newline = + has_newline_cons_alt && (!context.config.conditional_expression_prefer_single_line || use_new_lines_for_nested_conditional); if line_per_expression && (force_test_cons_newline || force_cons_alt_newline) { // for line per expression, if one is true then both should be true force_test_cons_newline = true; force_cons_alt_newline = true; } - let use_new_lines_for_nested_conditional = context.config.conditional_expression_use_nested_indentation && { - node.parent().kind() == NodeKind::CondExpr || node.cons.kind() == NodeKind::CondExpr || node.alt.kind() == NodeKind::CondExpr - }; let (question_position, colon_position) = get_operator_position(node, question_token, colon_token, context); let top_most_data = get_top_most_data(node, context); @@ -2213,7 +2215,7 @@ fn gen_conditional_expr<'a>(node: &'a CondExpr, context: &mut Context<'a>) -> Pr items.push_anchor(LineNumberAnchor::new(end_ln)); items.push_anchor(LineNumberAnchor::new(before_alternate_ln)); - let multi_line_reevaluation = if force_test_cons_newline || use_new_lines_for_nested_conditional { + let multi_line_reevaluation = if force_test_cons_newline { items.push_signal(Signal::NewLine); None } else if line_per_expression { @@ -2255,7 +2257,7 @@ fn gen_conditional_expr<'a>(node: &'a CondExpr, context: &mut Context<'a>) -> Pr items.extend(colon_comment_items.previous_lines); - if force_cons_alt_newline || use_new_lines_for_nested_conditional { + if force_cons_alt_newline { items.push_signal(Signal::NewLine); } else if line_per_expression { items.push_condition(conditions::new_line_if_multiple_lines_space_or_new_line_otherwise( @@ -5217,11 +5219,16 @@ fn gen_array_type<'a>(node: &'a TsArrayType, context: &mut Context<'a>) -> Print fn gen_conditional_type<'a>(node: &'a TsConditionalType, context: &mut Context<'a>) -> PrintItems { let top_most_data = get_top_most_data(node, context); let is_parent_conditional_type = node.parent().kind() == NodeKind::TsConditionalType; + let line_per_expression = context.config.conditional_type_line_per_expression; let use_new_lines_for_nested_conditional = context.config.conditional_type_use_nested_indentation && { is_parent_conditional_type || node.true_type.kind() == NodeKind::TsConditionalType || node.false_type.kind() == NodeKind::TsConditionalType }; - let force_new_lines_for_false_type = - !context.config.conditional_type_prefer_single_line && node_helpers::get_use_new_lines_for_nodes(&node.true_type, &node.false_type, context.program); + let prefer_single_line = context.config.conditional_type_prefer_single_line; + let has_newline_extends_true = node_helpers::get_use_new_lines_for_nodes(&node.extends_type, &node.true_type, context.program); + let has_newline_true_false = node_helpers::get_use_new_lines_for_nodes(&node.true_type, &node.false_type, context.program); + let force_newline_extends_true = has_newline_extends_true && (!prefer_single_line || use_new_lines_for_nested_conditional); + let force_newline_true_false = has_newline_true_false && (!prefer_single_line || use_new_lines_for_nested_conditional); + let mut items = PrintItems::new(); let before_false_ln = LineNumber::new("beforeFalse"); let question_token = context.token_finder.get_first_operator_after(&node.extends_type, "?").unwrap(); @@ -5249,12 +5256,17 @@ fn gen_conditional_type<'a>(node: &'a TsConditionalType, context: &mut Context<' items }))); - if use_new_lines_for_nested_conditional { + if !question_comment_items.previous_lines.is_empty() { + items.extend(question_comment_items.previous_lines); + } else if line_per_expression { + items.push_condition(conditions::new_line_if_multiple_lines_space_or_new_line_otherwise( + top_most_data.ln, + Some(before_false_ln), + )); + } else if force_newline_extends_true { items.push_signal(Signal::NewLine); - } else if question_comment_items.previous_lines.is_empty() { - items.push_signal(Signal::SpaceOrNewLine); } else { - items.extend(question_comment_items.previous_lines); + items.push_signal(Signal::SpaceOrNewLine); } items.push_condition({ @@ -5283,7 +5295,7 @@ fn gen_conditional_type<'a>(node: &'a TsConditionalType, context: &mut Context<' items.extend(colon_comment_items.previous_lines); // false type - if force_new_lines_for_false_type || use_new_lines_for_nested_conditional { + if force_newline_true_false { items.push_signal(Signal::NewLine); } else { items.push_condition(conditions::new_line_if_multiple_lines_space_or_new_line_otherwise( diff --git a/tests/specs/types/ConditionalType/ConditionalType_UseNestedIndentation_LinePerExpression_True.txt b/tests/specs/types/ConditionalType/ConditionalType_UseNestedIndentation_LinePerExpression_True.txt new file mode 100644 index 00000000..cdca49a5 --- /dev/null +++ b/tests/specs/types/ConditionalType/ConditionalType_UseNestedIndentation_LinePerExpression_True.txt @@ -0,0 +1,26 @@ +~~ lineWidth: 80, conditionalType.useNestedIndentation: true, conditionalType.linePerExpression: true ~~ +== should handle nested indentation with extends and mapped types == +export type R = S extends Record> ? { [K in keyof S]: ReturnType } : S extends T +? _T extends Array +? I +: _T : S extends SZ ? _T : S extends DZ +? _T : never; + +[expect] +export type R = S extends Record> + ? { [K in keyof S]: ReturnType } + : S extends T + ? _T extends Array + ? I + : _T + : S extends SZ + ? _T + : S extends DZ + ? _T + : never; + +== should keep on single line if fits on a single line == +type Test = A extends B ? C extends D ? c : d : e; + +[expect] +type Test = A extends B ? C extends D ? c : d : e; diff --git a/tests/specs/types/ConditionalType/ConditionalType_UseNestedIndentation_True.txt b/tests/specs/types/ConditionalType/ConditionalType_UseNestedIndentation_True.txt index 5b990532..e614ce1f 100644 --- a/tests/specs/types/ConditionalType/ConditionalType_UseNestedIndentation_True.txt +++ b/tests/specs/types/ConditionalType/ConditionalType_UseNestedIndentation_True.txt @@ -13,8 +13,7 @@ export type R = S extends Record> ? _T extends Array ? I : _T - : S extends SZ - ? _T + : S extends SZ ? _T : S extends DZ ? _T : never; From a6d106ae16f2a151c0127a9041a87549f562c202 Mon Sep 17 00:00:00 2001 From: Declan Vong Date: Tue, 20 Dec 2022 22:38:28 +1100 Subject: [PATCH 07/11] . --- src/generation/generate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generation/generate.rs b/src/generation/generate.rs index 9a26b988..9b4b2c55 100644 --- a/src/generation/generate.rs +++ b/src/generation/generate.rs @@ -2257,7 +2257,7 @@ fn gen_conditional_expr<'a>(node: &'a CondExpr, context: &mut Context<'a>) -> Pr items.extend(colon_comment_items.previous_lines); - if force_cons_alt_newline { + if force_cons_alt_newline { items.push_signal(Signal::NewLine); } else if line_per_expression { items.push_condition(conditions::new_line_if_multiple_lines_space_or_new_line_otherwise( From f12470b724f0205fc6388117ad82db98bdbb79f5 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Wed, 1 Feb 2023 16:58:23 -0500 Subject: [PATCH 08/11] Add failing test --- deployment/schema.json | 12 +++++++++++ src/configuration/builder.rs | 4 ++-- ...ConditionalType_LinePerExpression_True.txt | 20 +++++++++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 tests/specs/types/ConditionalType/ConditionalType_LinePerExpression_True.txt diff --git a/deployment/schema.json b/deployment/schema.json index fe52c82a..2b965260 100644 --- a/deployment/schema.json +++ b/deployment/schema.json @@ -820,6 +820,18 @@ "description": "Maintains the line breaks as written by the programmer." }] }, + "conditionalType.linePerExpression": { + "description": "Whether to force a line per expression when spanning multiple lines.", + "type": "boolean", + "default": false, + "oneOf": [{ + "const": true, + "description": "Formats with each part on a new line." + }, { + "const": false, + "description": "Uses the default behaviour." + }] + }, "conditionalExpression.useNestedIndentation": { "description": "Whether to use nested indentation within conditional expressions.", "type": "boolean", diff --git a/src/configuration/builder.rs b/src/configuration/builder.rs index 2e553041..e7210127 100644 --- a/src/configuration/builder.rs +++ b/src/configuration/builder.rs @@ -484,8 +484,8 @@ impl ConfigurationBuilder { /// Whether to force a line per expression when spanning multiple lines. /// - /// * `true` - Formats with each part on a new line. - /// * `false` (default) - Maintains the line breaks as written by the programmer. + /// * `true` (default) - Formats with each part on a new line. + /// * `false` - Maintains the line breaks as written by the programmer. pub fn conditional_expression_line_per_expression(&mut self, value: bool) -> &mut Self { self.insert("conditionalExpression.linePerExpression", value.into()) } diff --git a/tests/specs/types/ConditionalType/ConditionalType_LinePerExpression_True.txt b/tests/specs/types/ConditionalType/ConditionalType_LinePerExpression_True.txt new file mode 100644 index 00000000..3a926464 --- /dev/null +++ b/tests/specs/types/ConditionalType/ConditionalType_LinePerExpression_True.txt @@ -0,0 +1,20 @@ +~~ lineWidth: 80, conditionalType.linePerExpression: true ~~ +== should use a line per expression when multiLine == +type Test = A extends B + ? C extends D + ? c + : testingTesting + : testingTestingTestingTesting; + +[expect] +type Test = A extends B + ? C extends D + ? c + : testingTesting + : testingTestingTestingTesting; + +== should keep on single line if fits on a single line == +type Test = A extends B ? C extends D ? c : d : e; + +[expect] +type Test = A extends B ? C extends D ? c : d : e; From 1e8ad441ae008b433a39f3ae711b748ee3892932 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Wed, 1 Feb 2023 16:58:59 -0500 Subject: [PATCH 09/11] Update --- .../ConditionalType_LinePerExpression_True.txt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/specs/types/ConditionalType/ConditionalType_LinePerExpression_True.txt b/tests/specs/types/ConditionalType/ConditionalType_LinePerExpression_True.txt index 3a926464..0b997d3e 100644 --- a/tests/specs/types/ConditionalType/ConditionalType_LinePerExpression_True.txt +++ b/tests/specs/types/ConditionalType/ConditionalType_LinePerExpression_True.txt @@ -1,17 +1,17 @@ ~~ lineWidth: 80, conditionalType.linePerExpression: true ~~ == should use a line per expression when multiLine == type Test = A extends B - ? C extends D - ? c - : testingTesting - : testingTestingTestingTesting; + ? C extends D + ? c + : testingTesting + : testingTestingTestingTesting; [expect] type Test = A extends B - ? C extends D - ? c - : testingTesting - : testingTestingTestingTesting; + ? C extends D + ? c + : testingTesting + : testingTestingTestingTesting; == should keep on single line if fits on a single line == type Test = A extends B ? C extends D ? c : d : e; From 278b0d6c4fc00abb77b2745bf119571c0e397f91 Mon Sep 17 00:00:00 2001 From: Declan Vong Date: Wed, 22 Feb 2023 20:48:29 +1100 Subject: [PATCH 10/11] . --- src/generation/generate.rs | 14 ++++++++------ .../types/ConditionalType/ConditionalType_All.txt | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/generation/generate.rs b/src/generation/generate.rs index c3cc528f..dadcbc8d 100644 --- a/src/generation/generate.rs +++ b/src/generation/generate.rs @@ -5323,12 +5323,14 @@ fn gen_conditional_type<'a>(node: &'a TsConditionalType, context: &mut Context<' items } .into_rc_path(); - if_true_or( - "isStartOfLineIndentElseQueue", - condition_resolvers::is_start_of_line(), - with_indent(inner_items.into()), - with_queued_indent(inner_items.into()), - ) + + Condition::new("isStartOfLineAndTopLevelOrNestedConditionalIndentElseQueue", ConditionProperties { + condition: Rc::new(move |context| { + Some(context.writer_info.is_start_of_line() && (!is_parent_conditional_type || use_new_lines_for_nested_conditional)) + }), + true_path: Some(with_indent(inner_items.into())), + false_path: Some(with_queued_indent(inner_items.into())), + }) }); items.extend(colon_comment_items.previous_lines); diff --git a/tests/specs/types/ConditionalType/ConditionalType_All.txt b/tests/specs/types/ConditionalType/ConditionalType_All.txt index 04bcb9eb..0593dca8 100644 --- a/tests/specs/types/ConditionalType/ConditionalType_All.txt +++ b/tests/specs/types/ConditionalType/ConditionalType_All.txt @@ -25,7 +25,7 @@ type Type = T extends string ? number [expect] type Type = T extends string ? number : T extends hereIsAVeryLongExtendsClause - ? testingThis + ? testingThis : boolean; == should not line break before the extends keyword == From 827d6cd73438948627c28a8e72ef9f1b58c3c4d5 Mon Sep 17 00:00:00 2001 From: Declan Vong Date: Wed, 22 Feb 2023 20:52:58 +1100 Subject: [PATCH 11/11] reformat --- src/generation/generate.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/generation/generate.rs b/src/generation/generate.rs index dadcbc8d..d8c535a2 100644 --- a/src/generation/generate.rs +++ b/src/generation/generate.rs @@ -2191,8 +2191,7 @@ fn gen_conditional_expr<'a>(node: &'a CondExpr, context: &mut Context<'a>) -> Pr }; let mut force_test_cons_newline = has_newline_test_cons && (!context.config.conditional_expression_prefer_single_line || use_new_lines_for_nested_conditional); - let mut force_cons_alt_newline = - has_newline_cons_alt && (!context.config.conditional_expression_prefer_single_line || use_new_lines_for_nested_conditional); + let mut force_cons_alt_newline = has_newline_cons_alt && (!context.config.conditional_expression_prefer_single_line || use_new_lines_for_nested_conditional); if line_per_expression && (force_test_cons_newline || force_cons_alt_newline) { // for line per expression, if one is true then both should be true force_test_cons_newline = true; @@ -5324,13 +5323,16 @@ fn gen_conditional_type<'a>(node: &'a TsConditionalType, context: &mut Context<' } .into_rc_path(); - Condition::new("isStartOfLineAndTopLevelOrNestedConditionalIndentElseQueue", ConditionProperties { - condition: Rc::new(move |context| { - Some(context.writer_info.is_start_of_line() && (!is_parent_conditional_type || use_new_lines_for_nested_conditional)) - }), - true_path: Some(with_indent(inner_items.into())), - false_path: Some(with_queued_indent(inner_items.into())), - }) + Condition::new( + "isStartOfLineAndTopLevelOrNestedConditionalIndentElseQueue", + ConditionProperties { + condition: Rc::new(move |context| { + Some(context.writer_info.is_start_of_line() && (!is_parent_conditional_type || use_new_lines_for_nested_conditional)) + }), + true_path: Some(with_indent(inner_items.into())), + false_path: Some(with_queued_indent(inner_items.into())), + }, + ) }); items.extend(colon_comment_items.previous_lines);