From 20b8badf8c66ab978601dd147b903722282ea72e Mon Sep 17 00:00:00 2001 From: Daniel Zuncke Date: Thu, 26 Oct 2023 10:23:57 +0200 Subject: [PATCH] Fix issue #586 --- README.md | 2 ++ src/dfmt/ast_info.d | 49 ++++++++++++++++++++++++++++++++++++ src/dfmt/config.d | 3 +++ src/dfmt/formatter.d | 8 +++++- src/dfmt/main.d | 5 ++++ tests/allman/issue0586.d.ref | 28 +++++++++++++++++++++ tests/issue0586.d | 31 +++++++++++++++++++++++ tests/knr/issue0586.d.ref | 27 ++++++++++++++++++++ tests/otbs/issue0586.d.ref | 24 ++++++++++++++++++ 9 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 tests/allman/issue0586.d.ref create mode 100644 tests/issue0586.d create mode 100644 tests/knr/issue0586.d.ref create mode 100644 tests/otbs/issue0586.d.ref diff --git a/README.md b/README.md index 7ddaba8..6c3d766 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ found there. * `--soft_max_line_length`: *see dfmt_soft_max_line_length [below](#dfmt-specific-properties)* * `--space_after_cast`: *see dfmt_space_after_cast [below](#dfmt-specific-properties)* * `--space_before_aa_colon`: *see dfmt_space_before_aa_colon [below](#dfmt-specific-properties)* +* `--space_before_named_arg_colon`: *see dfmt_space_before_named_arg_colon [below](#dfmt-specific-properties)* * `--space_before_function_parameters`: *see dfmt_space_before_function_parameters [below](#dfmt-specific-properties)* * `--split_operator_at_line_end`: *see dfmt_split_operator_at_line_end [below](#dfmt-specific-properties)* * `--tab_width`: *see tab_width [below](#standard-editorconfig-properties)* @@ -119,6 +120,7 @@ dfmt_compact_labeled_statements | **`true`**, `false` | Place labels on the same dfmt_template_constraint_style | **`conditional_newline_indent`** `conditional_newline` `always_newline` `always_newline_indent` | Control the formatting of template constraints. dfmt_single_template_constraint_indent | `true`, **`false`** | Set if the constraints are indented by a single tab instead of two. Has only an effect if the style set to `always_newline_indent` or `conditional_newline_indent`. dfmt_space_before_aa_colon | `true`, **`false`** | Adds a space after an associative array key before the `:` like in older dfmt versions. +dfmt_space_before_named_arg_colon | `true`, **`false`** | Adds a space after a named function argument or named struct constructor argument before the `:`. dfmt_keep_line_breaks | `true`, **`false`** | Keep existing line breaks if these don't violate other formatting rules. dfmt_single_indent | `true`, **`false`** | Set if the code in parens is indented by a single tab instead of two. dfmt_reflow_property_chains | **`true`**, `false` | Recalculate the splitting of property chains into multiple lines. diff --git a/src/dfmt/ast_info.d b/src/dfmt/ast_info.d index 6ba0f24..b77095e 100644 --- a/src/dfmt/ast_info.d +++ b/src/dfmt/ast_info.d @@ -64,6 +64,7 @@ struct ASTInformation sort(ufcsHintLocations); ufcsHintLocations = ufcsHintLocations.uniq().array(); sort(ternaryColonLocations); + sort(namedArgumentColonLocations); } /// Locations of end braces for struct bodies @@ -139,6 +140,9 @@ struct ASTInformation /// Locations ternary expression colons. size_t[] ternaryColonLocations; + + /// Locations of named arguments of function call or struct constructor. + size_t[] namedArgumentColonLocations; } /// Collects information from the AST that is useful for the formatter @@ -448,6 +452,51 @@ final class FormatVisitor : ASTVisitor ternaryExpression.accept(this); } + override void visit(const FunctionCallExpression functionCall) + { + // Check if function has any arguments. + if (functionCall.arguments.namedArgumentList is null) + { + functionCall.accept(this); + return; + } + + /+ + Items are function arguments: f(, ); + Iterate them and check if they are named arguments: tok!":" belongs to a + named argument if it is preceeded by one tok!"identifier" (+ any number + of comments): + +/ + foreach (item; functionCall.arguments.namedArgumentList.items) + { + // Set to true after first tok!"identifier". + auto foundIdentifier = false; + + foreach (t; item.tokens) + { + if (t.type == tok!"comment") + { + continue; + } + + if (t.type == tok!"identifier" && !foundIdentifier) + { + foundIdentifier = true; + continue; + } + + if (t.type == tok!":" && foundIdentifier) + { + astInformation.namedArgumentColonLocations ~= t.index; + } + + break; + } + } + + functionCall.accept(this); + } + private: ASTInformation* astInformation; } diff --git a/src/dfmt/config.d b/src/dfmt/config.d index fe41a2d..6b41d4a 100644 --- a/src/dfmt/config.d +++ b/src/dfmt/config.d @@ -67,6 +67,8 @@ struct Config OptionalBoolean dfmt_reflow_property_chains; /// OptionalBoolean dfmt_space_after_statement_keyword; + /// + OptionalBoolean dfmt_space_before_named_arg_colon; mixin StandardEditorConfigFields; @@ -98,6 +100,7 @@ struct Config dfmt_keep_line_breaks = OptionalBoolean.f; dfmt_single_indent = OptionalBoolean.f; dfmt_reflow_property_chains = OptionalBoolean.t; + dfmt_space_before_named_arg_colon = OptionalBoolean.f; } /** diff --git a/src/dfmt/formatter.d b/src/dfmt/formatter.d index d2c0f97..0f7e821 100644 --- a/src/dfmt/formatter.d +++ b/src/dfmt/formatter.d @@ -835,13 +835,13 @@ private: { import dfmt.editorconfig : OptionalBoolean; import std.algorithm : canFind, any; - immutable bool isCase = astInformation.caseEndLocations.canFindIndex(current.index); immutable bool isAttribute = astInformation.attributeDeclarationLines.canFindIndex( current.line); immutable bool isStructInitializer = astInformation.structInfoSortedByEndLocation .canFind!(st => st.startLocation < current.index && current.index < st.endLocation); immutable bool isTernary = astInformation.ternaryColonLocations.canFindIndex(current.index); + immutable bool isNamedArg = astInformation.namedArgumentColonLocations.canFindIndex(current.index); if (isCase || isAttribute) { @@ -862,6 +862,12 @@ private: write(config.dfmt_space_before_aa_colon ? " : " : ": "); ++index; } + // Named function or struct constructor arguments. + else if (isNamedArg) + { + write(config.dfmt_space_before_named_arg_colon ? " : " : ": "); + ++index; + } else if (peekBackIs(tok!"identifier") && [tok!"{", tok!"}", tok!";", tok!":", tok!","] .any!((ptrdiff_t token) => peekBack2Is(cast(IdType)token, true)) diff --git a/src/dfmt/main.d b/src/dfmt/main.d index 55d00af..c5a5577 100644 --- a/src/dfmt/main.d +++ b/src/dfmt/main.d @@ -92,6 +92,9 @@ else case "space_before_aa_colon": optConfig.dfmt_space_before_aa_colon = optVal; break; + case "space_before_named_arg_colon": + optConfig.dfmt_space_before_named_arg_colon = optVal; + break; case "keep_line_breaks": optConfig.dfmt_keep_line_breaks = optVal; break; @@ -133,6 +136,7 @@ else "compact_labeled_statements", &handleBooleans, "single_template_constraint_indent", &handleBooleans, "space_before_aa_colon", &handleBooleans, + "space_before_named_arg_colon", &handleBooleans, "tab_width", &optConfig.tab_width, "template_constraint_style", &optConfig.dfmt_template_constraint_style, "keep_line_breaks", &handleBooleans, @@ -349,6 +353,7 @@ Formatting Options: --compact_labeled_statements --template_constraint_style --space_before_aa_colon + --space_before_named_arg_colon --single_indent --reflow_property_chains `, diff --git a/tests/allman/issue0586.d.ref b/tests/allman/issue0586.d.ref new file mode 100644 index 0000000..cd86519 --- /dev/null +++ b/tests/allman/issue0586.d.ref @@ -0,0 +1,28 @@ +void temp(int v1, int v2) +{ +} + +int f(int i) +{ + return i; +} + +struct S +{ + int i; + int j; +} + +void main() +{ + temp(v1: 1, v2: 2); + temp(v1: 1, v2: 2,); + + auto s = S(5, j: 3); + + temp(v1: 1, v2: f(i: 2)); + + temp(v1: true ? i : false ? 2 : f(i: 3), v2: 4); + + temp(v1: () { S s = S(i: 5); return s.i; }, v2: 1); +} diff --git a/tests/issue0586.d b/tests/issue0586.d new file mode 100644 index 0000000..db744f3 --- /dev/null +++ b/tests/issue0586.d @@ -0,0 +1,31 @@ +void temp(int v1, int v2) +{ +} + +int f(int i) +{ + return i; +} + +struct S +{ + int i; + int j; +} + +void main() +{ + temp(v1: 1, v2: 2); + temp( + v1: 1, + v2: 2, + ); + + auto s = S(5, j: 3); + + temp(v1: 1, v2: f(i: 2)); + + temp(v1: true ? i : false ? 2 : f(i: 3), v2: 4); + + temp(v1: () { S s = S(i: 5); return s.i; }, v2: 1); +} diff --git a/tests/knr/issue0586.d.ref b/tests/knr/issue0586.d.ref new file mode 100644 index 0000000..76c61bc --- /dev/null +++ b/tests/knr/issue0586.d.ref @@ -0,0 +1,27 @@ +void temp(int v1, int v2) +{ +} + +int f(int i) +{ + return i; +} + +struct S { + int i; + int j; +} + +void main() +{ + temp(v1: 1, v2: 2); + temp(v1: 1, v2: 2,); + + auto s = S(5, j: 3); + + temp(v1: 1, v2: f(i: 2)); + + temp(v1: true ? i : false ? 2 : f(i: 3), v2: 4); + + temp(v1: () { S s = S(i: 5); return s.i; }, v2: 1); +} diff --git a/tests/otbs/issue0586.d.ref b/tests/otbs/issue0586.d.ref new file mode 100644 index 0000000..e62ff1a --- /dev/null +++ b/tests/otbs/issue0586.d.ref @@ -0,0 +1,24 @@ +void temp(int v1, int v2) { +} + +int f(int i) { + return i; +} + +struct S { + int i; + int j; +} + +void main() { + temp(v1: 1, v2: 2); + temp(v1: 1, v2: 2,); + + auto s = S(5, j: 3); + + temp(v1: 1, v2: f(i: 2)); + + temp(v1: true ? i : false ? 2 : f(i: 3), v2: 4); + + temp(v1: () { S s = S(i: 5); return s.i; }, v2: 1); +}