Skip to content

feat: add config for forced multiline named imports/exports #473

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions deployment/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -1273,6 +1285,12 @@
},
"importDeclaration.forceSingleLine": {
"$ref": "#/definitions/forceSingleLine"
},
"exportDeclaration.forceMultiLine": {
"$ref": "#/definitions/forceMultiLineSpecifiers"
},
"importDeclaration.forceMultiLine": {
"$ref": "#/definitions/forceMultiLineSpecifiers"
}
}
}
15 changes: 14 additions & 1 deletion src/configuration/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
}
Expand Down
3 changes: 3 additions & 0 deletions src/configuration/resolve_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
5 changes: 5 additions & 0 deletions src/configuration/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
15 changes: 12 additions & 3 deletions src/generation/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
));
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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,
));
Expand Down Expand Up @@ -1391,6 +1393,7 @@ struct GenNamedImportOrExportSpecifierOptions<'a> {
parent: Node<'a>,
specifiers: Vec<Node<'a>>,
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 {
Expand All @@ -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),
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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<Box<dyn Fn((usize, Option<Node<'a>>), (usize, Option<Node<'a>>), &Program<'a>) -> std::cmp::Ordering>>,
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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";
Original file line number Diff line number Diff line change
@@ -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";