Skip to content

Commit d404219

Browse files
authored
feat: add config for forced multiline named imports/exports (#473)
1 parent 60d7a83 commit d404219

File tree

7 files changed

+224
-4
lines changed

7 files changed

+224
-4
lines changed

deployment/schema.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,18 @@
264264
"description": ""
265265
}]
266266
},
267+
"forceMultiLineSpecifiers": {
268+
"description": "If code import/export specifiers should be forced to be on multiple lines.",
269+
"type": "boolean",
270+
"default": false,
271+
"oneOf": [{
272+
"const": true,
273+
"description": ""
274+
}, {
275+
"const": false,
276+
"description": ""
277+
}]
278+
},
267279
"sortOrder": {
268280
"description": "The kind of sort ordering to use.",
269281
"type": "string",
@@ -1273,6 +1285,12 @@
12731285
},
12741286
"importDeclaration.forceSingleLine": {
12751287
"$ref": "#/definitions/forceSingleLine"
1288+
},
1289+
"exportDeclaration.forceMultiLine": {
1290+
"$ref": "#/definitions/forceMultiLineSpecifiers"
1291+
},
1292+
"importDeclaration.forceMultiLine": {
1293+
"$ref": "#/definitions/forceMultiLineSpecifiers"
12761294
}
12771295
}
12781296
}

src/configuration/builder.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,16 @@ impl ConfigurationBuilder {
757757
self.insert("importDeclaration.forceSingleLine", value.into())
758758
}
759759

760+
/* force multi line specifiers */
761+
762+
pub fn export_declaration_force_multi_line(&mut self, value: bool) -> &mut Self {
763+
self.insert("exportDeclaration.forceMultiLine", value.into())
764+
}
765+
766+
pub fn import_declaration_force_multi_line(&mut self, value: bool) -> &mut Self {
767+
self.insert("importDeclaration.forceMultiLine", value.into())
768+
}
769+
760770
/* member spacing */
761771

762772
pub fn enum_declaration_member_spacing(&mut self, value: MemberSpacing) -> &mut Self {
@@ -1201,6 +1211,9 @@ mod tests {
12011211
/* force single line */
12021212
.export_declaration_force_single_line(true)
12031213
.import_declaration_force_single_line(true)
1214+
/* force multi line specifiers */
1215+
.export_declaration_force_multi_line(true)
1216+
.import_declaration_force_multi_line(true)
12041217
/* space settings */
12051218
.binary_expression_space_surrounding_bitwise_and_arithmetic_operator(true)
12061219
.comment_line_force_space_after_slashes(false)
@@ -1246,7 +1259,7 @@ mod tests {
12461259
.while_statement_space_around(true);
12471260

12481261
let inner_config = config.get_inner_config();
1249-
assert_eq!(inner_config.len(), 175);
1262+
assert_eq!(inner_config.len(), 177);
12501263
let diagnostics = resolve_config(inner_config, &resolve_global_config(ConfigKeyMap::new(), &Default::default()).config).diagnostics;
12511264
assert_eq!(diagnostics.len(), 0);
12521265
}

src/configuration/resolve_config.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,9 @@ pub fn resolve_config(config: ConfigKeyMap, global_config: &GlobalConfiguration)
252252
/* force single line */
253253
import_declaration_force_single_line: get_value(&mut config, "importDeclaration.forceSingleLine", false, &mut diagnostics),
254254
export_declaration_force_single_line: get_value(&mut config, "exportDeclaration.forceSingleLine", false, &mut diagnostics),
255+
/* force multi line specifiers */
256+
import_declaration_force_multi_line: get_value(&mut config, "importDeclaration.forceMultiLine", false, &mut diagnostics),
257+
export_declaration_force_multi_line: get_value(&mut config, "exportDeclaration.forceMultiLine", false, &mut diagnostics),
255258
/* space settings */
256259
binary_expression_space_surrounding_bitwise_and_arithmetic_operator: get_value(
257260
&mut config,

src/configuration/types.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,11 @@ pub struct Configuration {
523523
pub import_declaration_force_single_line: bool,
524524
#[serde(rename = "exportDeclaration.forceSingleLine")]
525525
pub export_declaration_force_single_line: bool,
526+
/* force multi line specifiers */
527+
#[serde(rename = "exportDeclaration.forceMultiLine")]
528+
pub export_declaration_force_multi_line: bool,
529+
#[serde(rename = "importDeclaration.forceMultiLine")]
530+
pub import_declaration_force_multi_line: bool,
526531

527532
/* use space separator */
528533
#[serde(rename = "binaryExpression.spaceSurroundingBitwiseAndArithmeticOperator")]

src/generation/generate.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -992,7 +992,7 @@ fn gen_export_named_decl<'a>(node: &'a NamedExport, context: &mut Context<'a>) -
992992
let should_single_line = force_single_line
993993
|| (default_export.is_none()
994994
&& namespace_export.is_none()
995-
&& named_exports.len() <= 1
995+
&& (named_exports.len() <= 1 && !context.config.export_declaration_force_multi_line)
996996
&& node.start_line_fast(context.program) == node.end_line_fast(context.program));
997997

998998
// generate
@@ -1011,6 +1011,7 @@ fn gen_export_named_decl<'a>(node: &'a NamedExport, context: &mut Context<'a>) -
10111011
parent: node.into(),
10121012
specifiers: named_exports.into_iter().map(|x| x.into()).collect(),
10131013
force_single_line,
1014+
force_multi_line_specifiers: context.config.export_declaration_force_multi_line,
10141015
},
10151016
context,
10161017
));
@@ -1177,7 +1178,7 @@ fn gen_import_decl<'a>(node: &'a ImportDecl, context: &mut Context<'a>) -> Print
11771178
let should_single_line = force_single_line
11781179
|| (default_import.is_none()
11791180
&& namespace_import.is_none()
1180-
&& named_imports.len() <= 1
1181+
&& (named_imports.len() <= 1 && !context.config.import_declaration_force_multi_line)
11811182
&& node.start_line_fast(context.program) == node.end_line_fast(context.program));
11821183
let has_named_imports = !named_imports.is_empty() || {
11831184
let from_keyword = context.token_finder.get_previous_token_if_from_keyword(node.src);
@@ -1211,6 +1212,7 @@ fn gen_import_decl<'a>(node: &'a ImportDecl, context: &mut Context<'a>) -> Print
12111212
parent: node.into(),
12121213
specifiers: named_imports.into_iter().map(|x| x.into()).collect(),
12131214
force_single_line,
1215+
force_multi_line_specifiers: context.config.import_declaration_force_multi_line,
12141216
},
12151217
context,
12161218
));
@@ -1395,6 +1397,7 @@ struct GenNamedImportOrExportSpecifierOptions<'a> {
13951397
parent: Node<'a>,
13961398
specifiers: Vec<Node<'a>>,
13971399
force_single_line: bool,
1400+
force_multi_line_specifiers: bool,
13981401
}
13991402

14001403
fn gen_named_import_or_export_specifiers<'a>(opts: GenNamedImportOrExportSpecifierOptions<'a>, context: &mut Context<'a>) -> PrintItems {
@@ -1406,6 +1409,7 @@ fn gen_named_import_or_export_specifiers<'a>(opts: GenNamedImportOrExportSpecifi
14061409
prefer_hanging: get_prefer_hanging(opts.parent, context),
14071410
prefer_single_line: get_prefer_single_line(opts.parent, context),
14081411
force_single_line: opts.force_single_line,
1412+
force_multi_line: opts.force_multi_line_specifiers,
14091413
surround_single_line_with_spaces: get_use_space(opts.parent, context),
14101414
allow_blank_lines: false,
14111415
node_sorter: get_node_sorter(opts.parent, context),
@@ -2630,6 +2634,7 @@ fn gen_object_lit<'a>(node: &'a ObjectLit, context: &mut Context<'a>) -> PrintIt
26302634
prefer_hanging: context.config.object_expression_prefer_hanging,
26312635
prefer_single_line: context.config.object_expression_prefer_single_line,
26322636
force_single_line: false,
2637+
force_multi_line: false,
26332638
surround_single_line_with_spaces: context.config.object_expression_space_surrounding_properties,
26342639
allow_blank_lines: true,
26352640
node_sorter: None,
@@ -3359,6 +3364,7 @@ fn gen_type_lit<'a>(node: &'a TsTypeLit, context: &mut Context<'a>) -> PrintItem
33593364
prefer_hanging: context.config.type_literal_prefer_hanging,
33603365
prefer_single_line: context.config.type_literal_prefer_single_line,
33613366
force_single_line: false,
3367+
force_multi_line: false,
33623368
surround_single_line_with_spaces: context.config.type_literal_space_surrounding_properties,
33633369
allow_blank_lines: true,
33643370
node_sorter: None,
@@ -4073,6 +4079,7 @@ fn gen_object_pat<'a>(node: &'a ObjectPat, context: &mut Context<'a>) -> PrintIt
40734079
prefer_hanging: context.config.object_pattern_prefer_hanging,
40744080
prefer_single_line: context.config.object_pattern_prefer_single_line,
40754081
force_single_line: false,
4082+
force_multi_line: false,
40764083
surround_single_line_with_spaces: context.config.object_pattern_space_surrounding_properties,
40774084
allow_blank_lines: true,
40784085
node_sorter: None,
@@ -7675,6 +7682,7 @@ struct GenObjectLikeNodeOptions<'a> {
76757682
prefer_hanging: bool,
76767683
prefer_single_line: bool,
76777684
force_single_line: bool,
7685+
force_multi_line: bool,
76787686
surround_single_line_with_spaces: bool,
76797687
allow_blank_lines: bool,
76807688
node_sorter: Option<Box<dyn Fn((usize, Option<Node<'a>>), (usize, Option<Node<'a>>), &Program<'a>) -> std::cmp::Ordering>>,
@@ -7686,7 +7694,8 @@ fn gen_object_like_node<'a>(opts: GenObjectLikeNodeOptions<'a>, context: &mut Co
76867694
let child_tokens = get_tokens_from_children_with_tokens(opts.node, context.program);
76877695
let open_brace_token = child_tokens.iter().find(|t| t.token == Token::LBrace);
76887696
let close_brace_token = child_tokens.iter().rev().find(|t| t.token == Token::RBrace);
7689-
let force_multi_line = !opts.force_single_line && get_use_new_lines_for_nodes_with_preceeding_token("{", &opts.members, opts.prefer_single_line, context);
7697+
let force_multi_line =
7698+
opts.force_multi_line || !opts.force_single_line && get_use_new_lines_for_nodes_with_preceeding_token("{", &opts.members, opts.prefer_single_line, context);
76907699

76917700
let first_member_range = opts.members.get(0).map(|x| x.range());
76927701
let obj_range = if let (Some(open_brace_token), Some(close_brace_token)) = (open_brace_token, close_brace_token) {
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
~~ exportDeclaration.forceMultiLine: true, lineWidth: 40 ~~
2+
== should always add a new line between exports ==
3+
export { testing, a, b, c, d, e } from "./test.ts";
4+
export { testing, a, b, c, d, e // test
5+
} from "./test.ts";
6+
export { a, b, c, d, e /* this is ok though testing testing */ } from "./test.ts";
7+
export { a, b, c, d, e /* and
8+
not ok */ } from "./test.ts";
9+
10+
[expect]
11+
export {
12+
a,
13+
b,
14+
c,
15+
d,
16+
e,
17+
testing,
18+
} from "./test.ts";
19+
export {
20+
a,
21+
b,
22+
c,
23+
d,
24+
e, // test
25+
testing,
26+
} from "./test.ts";
27+
export {
28+
a,
29+
b,
30+
c,
31+
d,
32+
e, /* this is ok though testing testing */
33+
} from "./test.ts";
34+
export {
35+
a,
36+
b,
37+
c,
38+
d,
39+
e, /* and
40+
not ok */
41+
} from "./test.ts";
42+
43+
== should not collapse a multi-line one ==
44+
export {
45+
46+
a,
47+
48+
b,
49+
c,
50+
/* testing */
51+
d,
52+
} from "./test.ts";
53+
export {
54+
a,
55+
b,
56+
c,
57+
d,
58+
} from "./test.ts";
59+
60+
[expect]
61+
export {
62+
a,
63+
b,
64+
c,
65+
/* testing */
66+
d,
67+
} from "./test.ts";
68+
export {
69+
a,
70+
b,
71+
c,
72+
d,
73+
} from "./test.ts";
74+
75+
== should make a single export multi-line ==
76+
export { a } from "./test.ts";
77+
78+
[expect]
79+
export {
80+
a,
81+
} from "./test.ts";
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
~~ importDeclaration.forceMultiLine: true, lineWidth: 40 ~~
2+
== should always add a new line between ==
3+
import { testing, a, b, c, d, e } from "./test.ts";
4+
import { testing, a, b, c, d, e // test
5+
} from "./test.ts";
6+
import { a, b, c, d, e /* this is ok though testing testing */ } from "./test.ts";
7+
import { a, b, c, d, e /* and
8+
not ok */ } from "./test.ts";
9+
10+
[expect]
11+
import {
12+
a,
13+
b,
14+
c,
15+
d,
16+
e,
17+
testing,
18+
} from "./test.ts";
19+
import {
20+
a,
21+
b,
22+
c,
23+
d,
24+
e, // test
25+
testing,
26+
} from "./test.ts";
27+
import {
28+
a,
29+
b,
30+
c,
31+
d,
32+
e, /* this is ok though testing testing */
33+
} from "./test.ts";
34+
import {
35+
a,
36+
b,
37+
c,
38+
d,
39+
e, /* and
40+
not ok */
41+
} from "./test.ts";
42+
43+
== should collapse a multi-line one ==
44+
import { /* a */
45+
46+
a,
47+
48+
/* test */ b, /* other */
49+
c,
50+
/* testing */
51+
d,
52+
} from "./test.ts"; // actually, a block comment at the start of this line causes a bug
53+
// where the block comment will not have a space after it. Not worth fixing though
54+
// because it will be fixed by formatting again (which the CLI does automatically)
55+
// and it's super rare that someone would put a comment there.
56+
import {
57+
/* a */
58+
a,
59+
b,
60+
c,
61+
d,
62+
} from "./test.ts";
63+
64+
[expect]
65+
import {
66+
/* a */
67+
68+
a,
69+
/* test */ b, /* other */
70+
c,
71+
/* testing */
72+
d,
73+
} from "./test.ts"; // actually, a block comment at the start of this line causes a bug
74+
// where the block comment will not have a space after it. Not worth fixing though
75+
// because it will be fixed by formatting again (which the CLI does automatically)
76+
// and it's super rare that someone would put a comment there.
77+
import {
78+
/* a */
79+
a,
80+
b,
81+
c,
82+
d,
83+
} from "./test.ts";
84+
85+
== should make a single import multi-line ==
86+
import { a } from "./test.ts";
87+
88+
[expect]
89+
import {
90+
a,
91+
} from "./test.ts";

0 commit comments

Comments
 (0)