diff --git a/deployment/schema.json b/deployment/schema.json index 8f73699f..07b548c1 100644 --- a/deployment/schema.json +++ b/deployment/schema.json @@ -264,6 +264,18 @@ "description": "" }] }, + "forceMultiLineSpecifiers": { + "description": "If code import/export specifiers should be forced to be on multiple lines.", + "type": "boolean", + "default": false, + "oneOf": [{ + "const": true, + "description": "" + }, { + "const": false, + "description": "" + }] + }, "sortOrder": { "description": "The kind of sort ordering to use.", "type": "string", @@ -1273,6 +1285,12 @@ }, "importDeclaration.forceSingleLine": { "$ref": "#/definitions/forceSingleLine" + }, + "exportDeclaration.forceMultiLine": { + "$ref": "#/definitions/forceMultiLineSpecifiers" + }, + "importDeclaration.forceMultiLine": { + "$ref": "#/definitions/forceMultiLineSpecifiers" } } } diff --git a/src/configuration/builder.rs b/src/configuration/builder.rs index e813bc90..c8a694b7 100644 --- a/src/configuration/builder.rs +++ b/src/configuration/builder.rs @@ -757,6 +757,16 @@ impl ConfigurationBuilder { self.insert("importDeclaration.forceSingleLine", value.into()) } + /* force multi line specifiers */ + + pub fn export_declaration_force_multi_line(&mut self, value: bool) -> &mut Self { + self.insert("exportDeclaration.forceMultiLine", value.into()) + } + + pub fn import_declaration_force_multi_line(&mut self, value: bool) -> &mut Self { + self.insert("importDeclaration.forceMultiLine", value.into()) + } + /* member spacing */ pub fn enum_declaration_member_spacing(&mut self, value: MemberSpacing) -> &mut Self { @@ -1201,6 +1211,9 @@ mod tests { /* force single line */ .export_declaration_force_single_line(true) .import_declaration_force_single_line(true) + /* force multi line specifiers */ + .export_declaration_force_multi_line(true) + .import_declaration_force_multi_line(true) /* space settings */ .binary_expression_space_surrounding_bitwise_and_arithmetic_operator(true) .comment_line_force_space_after_slashes(false) @@ -1246,7 +1259,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(), 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 f489ad5a..5efb7539 100644 --- a/src/configuration/resolve_config.rs +++ b/src/configuration/resolve_config.rs @@ -252,6 +252,9 @@ pub fn resolve_config(config: ConfigKeyMap, global_config: &GlobalConfiguration) /* force single line */ import_declaration_force_single_line: get_value(&mut config, "importDeclaration.forceSingleLine", false, &mut diagnostics), export_declaration_force_single_line: get_value(&mut config, "exportDeclaration.forceSingleLine", false, &mut diagnostics), + /* force multi line specifiers */ + import_declaration_force_multi_line: get_value(&mut config, "importDeclaration.forceMultiLine", false, &mut diagnostics), + export_declaration_force_multi_line: get_value(&mut config, "exportDeclaration.forceMultiLine", false, &mut diagnostics), /* space settings */ binary_expression_space_surrounding_bitwise_and_arithmetic_operator: get_value( &mut config, diff --git a/src/configuration/types.rs b/src/configuration/types.rs index 4476ba6c..234c71e7 100644 --- a/src/configuration/types.rs +++ b/src/configuration/types.rs @@ -523,6 +523,11 @@ pub struct Configuration { pub import_declaration_force_single_line: bool, #[serde(rename = "exportDeclaration.forceSingleLine")] pub export_declaration_force_single_line: bool, + /* force multi line specifiers */ + #[serde(rename = "exportDeclaration.forceMultiLine")] + pub export_declaration_force_multi_line: bool, + #[serde(rename = "importDeclaration.forceMultiLine")] + pub import_declaration_force_multi_line: bool, /* use space separator */ #[serde(rename = "binaryExpression.spaceSurroundingBitwiseAndArithmeticOperator")] diff --git a/src/generation/generate.rs b/src/generation/generate.rs index 26bcfaa5..85c2e0fa 100644 --- a/src/generation/generate.rs +++ b/src/generation/generate.rs @@ -988,7 +988,7 @@ fn gen_export_named_decl<'a>(node: &'a NamedExport, context: &mut Context<'a>) - let should_single_line = force_single_line || (default_export.is_none() && namespace_export.is_none() - && named_exports.len() <= 1 + && (named_exports.len() <= 1 && !context.config.export_declaration_force_multi_line) && node.start_line_fast(context.program) == node.end_line_fast(context.program)); // generate @@ -1007,6 +1007,7 @@ fn gen_export_named_decl<'a>(node: &'a NamedExport, context: &mut Context<'a>) - parent: node.into(), specifiers: named_exports.into_iter().map(|x| x.into()).collect(), force_single_line, + force_multi_line_specifiers: context.config.export_declaration_force_multi_line, }, context, )); @@ -1173,7 +1174,7 @@ fn gen_import_decl<'a>(node: &'a ImportDecl, context: &mut Context<'a>) -> Print let should_single_line = force_single_line || (default_import.is_none() && namespace_import.is_none() - && named_imports.len() <= 1 + && (named_imports.len() <= 1 && !context.config.import_declaration_force_multi_line) && node.start_line_fast(context.program) == node.end_line_fast(context.program)); let has_named_imports = !named_imports.is_empty() || { let from_keyword = context.token_finder.get_previous_token_if_from_keyword(node.src); @@ -1207,6 +1208,7 @@ fn gen_import_decl<'a>(node: &'a ImportDecl, context: &mut Context<'a>) -> Print parent: node.into(), specifiers: named_imports.into_iter().map(|x| x.into()).collect(), force_single_line, + force_multi_line_specifiers: context.config.import_declaration_force_multi_line, }, context, )); @@ -1391,6 +1393,7 @@ struct GenNamedImportOrExportSpecifierOptions<'a> { parent: Node<'a>, specifiers: Vec>, force_single_line: bool, + force_multi_line_specifiers: bool, } fn gen_named_import_or_export_specifiers<'a>(opts: GenNamedImportOrExportSpecifierOptions<'a>, context: &mut Context<'a>) -> PrintItems { @@ -1402,6 +1405,7 @@ fn gen_named_import_or_export_specifiers<'a>(opts: GenNamedImportOrExportSpecifi prefer_hanging: get_prefer_hanging(opts.parent, context), prefer_single_line: get_prefer_single_line(opts.parent, context), force_single_line: opts.force_single_line, + force_multi_line: opts.force_multi_line_specifiers, surround_single_line_with_spaces: get_use_space(opts.parent, context), allow_blank_lines: false, node_sorter: get_node_sorter(opts.parent, context), @@ -2626,6 +2630,7 @@ fn gen_object_lit<'a>(node: &'a ObjectLit, context: &mut Context<'a>) -> PrintIt prefer_hanging: context.config.object_expression_prefer_hanging, prefer_single_line: context.config.object_expression_prefer_single_line, force_single_line: false, + force_multi_line: false, surround_single_line_with_spaces: context.config.object_expression_space_surrounding_properties, allow_blank_lines: true, node_sorter: None, @@ -3355,6 +3360,7 @@ fn gen_type_lit<'a>(node: &'a TsTypeLit, context: &mut Context<'a>) -> PrintItem prefer_hanging: context.config.type_literal_prefer_hanging, prefer_single_line: context.config.type_literal_prefer_single_line, force_single_line: false, + force_multi_line: false, surround_single_line_with_spaces: context.config.type_literal_space_surrounding_properties, allow_blank_lines: true, node_sorter: None, @@ -4069,6 +4075,7 @@ fn gen_object_pat<'a>(node: &'a ObjectPat, context: &mut Context<'a>) -> PrintIt prefer_hanging: context.config.object_pattern_prefer_hanging, prefer_single_line: context.config.object_pattern_prefer_single_line, force_single_line: false, + force_multi_line: false, surround_single_line_with_spaces: context.config.object_pattern_space_surrounding_properties, allow_blank_lines: true, node_sorter: None, @@ -7664,6 +7671,7 @@ struct GenObjectLikeNodeOptions<'a> { prefer_hanging: bool, prefer_single_line: bool, force_single_line: bool, + force_multi_line: bool, surround_single_line_with_spaces: bool, allow_blank_lines: bool, node_sorter: Option>), (usize, Option>), &Program<'a>) -> std::cmp::Ordering>>, @@ -7675,7 +7683,8 @@ fn gen_object_like_node<'a>(opts: GenObjectLikeNodeOptions<'a>, context: &mut Co let child_tokens = get_tokens_from_children_with_tokens(opts.node, context.program); let open_brace_token = child_tokens.iter().find(|t| t.token == Token::LBrace); let close_brace_token = child_tokens.iter().rev().find(|t| t.token == Token::RBrace); - let force_multi_line = !opts.force_single_line && get_use_new_lines_for_nodes_with_preceeding_token("{", &opts.members, opts.prefer_single_line, context); + let force_multi_line = + opts.force_multi_line || !opts.force_single_line && get_use_new_lines_for_nodes_with_preceeding_token("{", &opts.members, opts.prefer_single_line, context); let first_member_range = opts.members.get(0).map(|x| x.range()); let obj_range = if let (Some(open_brace_token), Some(close_brace_token)) = (open_brace_token, close_brace_token) { diff --git a/tests/specs/declarations/export/ExportDeclaration_ForceMultiLine_True.txt b/tests/specs/declarations/export/ExportDeclaration_ForceMultiLine_True.txt new file mode 100644 index 00000000..e9eec2fb --- /dev/null +++ b/tests/specs/declarations/export/ExportDeclaration_ForceMultiLine_True.txt @@ -0,0 +1,81 @@ +~~ exportDeclaration.forceMultiLine: true, lineWidth: 40 ~~ +== should always add a new line between exports == +export { testing, a, b, c, d, e } from "./test.ts"; +export { testing, a, b, c, d, e // test +} from "./test.ts"; +export { a, b, c, d, e /* this is ok though testing testing */ } from "./test.ts"; +export { a, b, c, d, e /* and +not ok */ } from "./test.ts"; + +[expect] +export { + a, + b, + c, + d, + e, + testing, +} from "./test.ts"; +export { + a, + b, + c, + d, + e, // test + testing, +} from "./test.ts"; +export { + a, + b, + c, + d, + e, /* this is ok though testing testing */ +} from "./test.ts"; +export { + a, + b, + c, + d, + e, /* and +not ok */ +} from "./test.ts"; + +== should not collapse a multi-line one == +export { + + a, + + b, + c, + /* testing */ + d, +} from "./test.ts"; +export { + a, + b, + c, + d, +} from "./test.ts"; + +[expect] +export { + a, + b, + c, + /* testing */ + d, +} from "./test.ts"; +export { + a, + b, + c, + d, +} from "./test.ts"; + +== should make a single export multi-line == +export { a } from "./test.ts"; + +[expect] +export { + a, +} from "./test.ts"; diff --git a/tests/specs/declarations/import/ImportDeclaration_ForceMultiLine.txt b/tests/specs/declarations/import/ImportDeclaration_ForceMultiLine.txt new file mode 100644 index 00000000..d8771179 --- /dev/null +++ b/tests/specs/declarations/import/ImportDeclaration_ForceMultiLine.txt @@ -0,0 +1,91 @@ +~~ importDeclaration.forceMultiLine: true, lineWidth: 40 ~~ +== should always add a new line between == +import { testing, a, b, c, d, e } from "./test.ts"; +import { testing, a, b, c, d, e // test +} from "./test.ts"; +import { a, b, c, d, e /* this is ok though testing testing */ } from "./test.ts"; +import { a, b, c, d, e /* and +not ok */ } from "./test.ts"; + +[expect] +import { + a, + b, + c, + d, + e, + testing, +} from "./test.ts"; +import { + a, + b, + c, + d, + e, // test + testing, +} from "./test.ts"; +import { + a, + b, + c, + d, + e, /* this is ok though testing testing */ +} from "./test.ts"; +import { + a, + b, + c, + d, + e, /* and +not ok */ +} from "./test.ts"; + +== should collapse a multi-line one == +import { /* a */ + + a, + + /* test */ b, /* other */ + c, + /* testing */ + d, +} from "./test.ts"; // actually, a block comment at the start of this line causes a bug +// where the block comment will not have a space after it. Not worth fixing though +// because it will be fixed by formatting again (which the CLI does automatically) +// and it's super rare that someone would put a comment there. +import { + /* a */ + a, + b, + c, + d, +} from "./test.ts"; + +[expect] +import { + /* a */ + + a, + /* test */ b, /* other */ + c, + /* testing */ + d, +} from "./test.ts"; // actually, a block comment at the start of this line causes a bug +// where the block comment will not have a space after it. Not worth fixing though +// because it will be fixed by formatting again (which the CLI does automatically) +// and it's super rare that someone would put a comment there. +import { + /* a */ + a, + b, + c, + d, +} from "./test.ts"; + +== should make a single import multi-line == +import { a } from "./test.ts"; + +[expect] +import { + a, +} from "./test.ts";