From 3b094b16d9e8d6d8ed98d8cf9799dcfcf5f11cd3 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Fri, 11 Jan 2019 00:44:11 +0100 Subject: [PATCH 1/8] Add ArrayLiterals into arrayStartLocations list This is done to properly format arrays in function arguments --- src/dfmt/ast_info.d | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/dfmt/ast_info.d b/src/dfmt/ast_info.d index e8157b9..c5bd3cc 100644 --- a/src/dfmt/ast_info.d +++ b/src/dfmt/ast_info.d @@ -45,6 +45,7 @@ struct ASTInformation sort(conditionalWithElseLocations); sort(conditionalStatementLocations); sort(arrayStartLocations); + sort(assocArrayStartLocations); sort(contractLocations); sort(constraintLocations); sort(constructorDestructorLocations); @@ -95,6 +96,9 @@ struct ASTInformation /// Locations of start locations of array initializers size_t[] arrayStartLocations; + /// Locations of start locations of associative array initializers + size_t[] assocArrayStartLocations; + /// Locations of "in" and "out" tokens that begin contracts size_t[] contractLocations; @@ -136,6 +140,19 @@ final class FormatVisitor : ASTVisitor arrayInitializer.accept(this); } + override void visit(const ArrayLiteral arrayLiteral) + { + astInformation.arrayStartLocations ~= arrayLiteral.tokens[0].index; + arrayLiteral.accept(this); + } + + override void visit(const AssocArrayLiteral assocArrayLiteral) + { + astInformation.arrayStartLocations ~= assocArrayLiteral.tokens[0].index; + astInformation.assocArrayStartLocations ~= assocArrayLiteral.tokens[0].index; + assocArrayLiteral.accept(this); + } + override void visit (const SharedStaticConstructor sharedStaticConstructor) { astInformation.sharedStaticConstructorDestructorLocations ~= sharedStaticConstructor.location; From 6e4136a353f737ead16bd74636efd6a8c0d63898 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Fri, 11 Jan 2019 00:45:47 +0100 Subject: [PATCH 2/8] Make colon almost never break a line --- src/dfmt/tokens.d | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dfmt/tokens.d b/src/dfmt/tokens.d index 952cc31..569c3dc 100644 --- a/src/dfmt/tokens.d +++ b/src/dfmt/tokens.d @@ -134,7 +134,6 @@ int breakCost(IdType p, IdType c) pure nothrow @safe @nogc case tok!"||": case tok!"&&": case tok!",": - case tok!":": case tok!"?": return 0; case tok!"(": @@ -184,6 +183,8 @@ int breakCost(IdType p, IdType c) pure nothrow @safe @nogc case tok!"~": case tok!"+=": return 200; + case tok!":": + return p == tok!"identifier" ? 0 : 300; case tok!".": return p == tok!")" ? 0 : 300; default: From 053b775cd1a80531b1476b2e15d5f5cd293aca8e Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Fri, 11 Jan 2019 00:47:50 +0100 Subject: [PATCH 3/8] Add details to indentation stack This makes more advanced state handling easily possible. Also moved isWrapIndent/isTempIndent into this, which allows for exceptions for certain tokens and more control. --- src/dfmt/formatter.d | 25 ++++++++++++---- src/dfmt/indentation.d | 68 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 78 insertions(+), 15 deletions(-) diff --git a/src/dfmt/formatter.d b/src/dfmt/formatter.d index bd47b93..b2716c7 100644 --- a/src/dfmt/formatter.d +++ b/src/dfmt/formatter.d @@ -1240,8 +1240,13 @@ private: break; case tok!"]": indents.popWrapIndents(); - if (indents.topIs(tok!"]")) - newline(); + if (indents.topIsTemp(tok!"]")) + { + if (!indents.topDetails.mini) + newline(); + else + indents.pop(); + } writeToken(); if (currentIs(tok!"identifier")) write(" "); @@ -1402,7 +1407,7 @@ private: : tokens[i .. $].countUntil!(t => t.index == r.front) + i; immutable size_t j = min(expressionEndIndex(i), ufcsBreakLocation); // Use magical negative value for array literals and wrap indents - immutable inLvl = (indents.topIsWrap() || indents.topIs(tok!"]")) ? -indentLevel + immutable inLvl = (indents.topIsWrap() || indents.topIsTemp(tok!"]")) ? -indentLevel : indentLevel; linebreakHints = chooseLineBreakTokens(i, tokens[i .. j], depths[i .. j], config, currentLineLength, inLvl); @@ -1528,7 +1533,7 @@ private: else if (currentIs(tok!"]")) { indents.popWrapIndents(); - if (indents.topIs(tok!"]")) + if (indents.topIsTemp(tok!"]")) { indents.pop(); indentLevel = indents.indentLevel; @@ -1671,13 +1676,21 @@ private: void pushWrapIndent(IdType type = tok!"") { immutable t = type == tok!"" ? tokens[index].type : type; + IndentStack.Details detail; + detail.wrap = isWrapIndent(t); + detail.temp = isTempIndent(t); + pushWrapIndent(t, detail); + } + + void pushWrapIndent(IdType type, IndentStack.Details detail) + { if (parenDepth == 0) { if (indents.wrapIndents == 0) - indents.push(t); + indents.push(type, detail); } else if (indents.wrapIndents < 1) - indents.push(t); + indents.push(type, detail); } const pure @safe @nogc: diff --git a/src/dfmt/indentation.d b/src/dfmt/indentation.d index 8eeacea..d2be341 100644 --- a/src/dfmt/indentation.d +++ b/src/dfmt/indentation.d @@ -7,6 +7,8 @@ module dfmt.indentation; import dparse.lexer; +import std.bitmanip : bitfields; + /** * Returns: true if the given token type is a wrap indent type */ @@ -29,6 +31,20 @@ bool isTempIndent(IdType type) pure nothrow @nogc @safe */ struct IndentStack { + static struct Details + { + mixin(bitfields!( + // generally true for all operators except {, case, @, ], (, ) + bool, "wrap", 1, + // temporary indentation which get's reverted when a block starts + // generally true for all tokens except ), {, case, @ + bool, "temp", 1, + // emit minimal newlines + bool, "mini", 1, + bool, "isAA", 1, + uint, "", 28)); + } + /** * Get the indent size at the most recent occurrence of the given indent type */ @@ -55,7 +71,7 @@ struct IndentStack int tempIndentCount = 0; for (size_t i = index; i > 0; i--) { - if (!isWrapIndent(arr[i - 1]) && arr[i - 1] != tok!"]") + if (!details[i - 1].wrap && arr[i - 1] != tok!"]") break; tempIndentCount++; } @@ -66,8 +82,20 @@ struct IndentStack * Pushes the given indent type on to the stack. */ void push(IdType item) pure nothrow + { + Details detail; + detail.wrap = isWrapIndent(item); + detail.temp = isTempIndent(item); + push(item, detail); + } + + /** + * Pushes the given indent type on to the stack. + */ + void push(IdType item, Details detail) pure nothrow { arr[index] = item; + details[index] = detail; //FIXME this is actually a bad thing to do, //we should not just override when the stack is //at it's limit @@ -91,7 +119,7 @@ struct IndentStack */ void popWrapIndents() pure nothrow @safe @nogc { - while (index > 0 && isWrapIndent(arr[index - 1])) + while (index > 0 && details[index - 1].wrap) index--; } @@ -100,7 +128,7 @@ struct IndentStack */ void popTempIndents() pure nothrow @safe @nogc { - while (index > 0 && isTempIndent(arr[index - 1])) + while (index > 0 && details[index - 1].temp) index--; } @@ -125,7 +153,15 @@ struct IndentStack */ bool topIsTemp() { - return index > 0 && index <= arr.length && isTempIndent(arr[index - 1]); + return index > 0 && index <= arr.length && details[index - 1].temp; + } + + /** + * Returns: `true` if the top of the indent stack is a temporary indent with the specified token + */ + bool topIsTemp(IdType item) + { + return index > 0 && index <= arr.length && arr[index - 1] == item && details[index - 1].temp; } /** @@ -133,7 +169,15 @@ struct IndentStack */ bool topIsWrap() { - return index > 0 && index <= arr.length && isWrapIndent(arr[index - 1]); + return index > 0 && index <= arr.length && details[index - 1].wrap; + } + + /** + * Returns: `true` if the top of the indent stack is a temporary indent with the specified token + */ + bool topIsWrap(IdType item) + { + return index > 0 && index <= arr.length && arr[index - 1] == item && details[index - 1].wrap; } /** @@ -156,6 +200,11 @@ struct IndentStack return arr[index - 1]; } + Details topDetails() const pure nothrow @property @safe @nogc + { + return details[index - 1]; + } + int indentLevel() const pure nothrow @property @safe @nogc { return indentSize(); @@ -183,6 +232,7 @@ private: size_t index; IdType[256] arr; + Details[arr.length] details; int indentSize(const size_t k = size_t.max) const pure nothrow @safe @nogc { @@ -196,17 +246,17 @@ private: { immutable int pc = (arr[i] == tok!"!" || arr[i] == tok!"(" || arr[i] == tok!")") ? parenCount + 1 : parenCount; - if ((isWrapIndent(arr[i]) || arr[i] == tok!"(") && parenCount > 1) + if ((details[i].wrap || arr[i] == tok!"(") && parenCount > 1) { parenCount = pc; continue; } if (i + 1 < index) { - if (arr[i] == tok!"]") + if (arr[i] == tok!"]" && details[i].temp) continue; - immutable currentIsNonWrapTemp = !isWrapIndent(arr[i]) - && isTempIndent(arr[i]) && arr[i] != tok!")" && arr[i] != tok!"!"; + immutable currentIsNonWrapTemp = !details[i].wrap + && details[i].temp && arr[i] != tok!")" && arr[i] != tok!"!"; if (arr[i] == tok!"static" && arr[i + 1].among!(tok!"if", tok!"else", tok!"foreach", tok!"foreach_reverse") && (i + 2 >= index || arr[i + 2] != tok!"{")) From 85c7d57167be4e6ce583d82e062e60fbcf5e1d7b Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Fri, 11 Jan 2019 00:48:59 +0100 Subject: [PATCH 4/8] make multiline checking code modular Arrays and delegates now use the check whether a line is longer than the max line length using easy to reuse code --- src/dfmt/formatter.d | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/dfmt/formatter.d b/src/dfmt/formatter.d index b2716c7..c8bbe18 100644 --- a/src/dfmt/formatter.d +++ b/src/dfmt/formatter.d @@ -569,9 +569,9 @@ private: // No heuristics apply if we can't look before the opening paren/bracket if (index < 1) return; - immutable bool arrayInitializerStart = p == tok!"[" && linebreakHints.length != 0 + immutable bool arrayInitializerStart = p == tok!"[" && astInformation.arrayStartLocations.canFindIndex(tokens[index - 1].index); - if (arrayInitializerStart) + if (arrayInitializerStart && isMultilineAt(index)) { // Use the close bracket as the indent token to distinguish // the array initialiazer from an array index in the newline @@ -790,12 +790,7 @@ private: sBraceDepth++; if (peekBackIsOneOf(true, tok!")", tok!"identifier")) write(" "); - auto e = expressionEndIndex(index); - immutable int l = currentLineLength + tokens[index .. e].map!(a => tokenLength(a)) - .sum(); - immutable bool multiline = l > config.dfmt_soft_max_line_length - || tokens[index .. e].canFind!(a => a.type == tok!"comment" - || isBlockHeaderToken(a.type))(); + immutable bool multiline = isMultilineAt(index); writeToken(); if (multiline) { @@ -1698,6 +1693,7 @@ const pure @safe @nogc: size_t expressionEndIndex(size_t i) nothrow { immutable bool braces = i < tokens.length && tokens[i].type == tok!"{"; + immutable bool brackets = i < tokens.length && tokens[i].type == tok!"["; immutable d = depths[i]; while (true) { @@ -1705,13 +1701,23 @@ const pure @safe @nogc: break; if (depths[i] < d) break; - if (!braces && (tokens[i].type == tok!";" || tokens[i].type == tok!"{")) + if (!braces && !brackets && (tokens[i].type == tok!";" || tokens[i].type == tok!"{")) break; i++; } return i; } + bool isMultilineAt(size_t i) + { + import std.algorithm : map, sum, canFind; + + auto e = expressionEndIndex(i); + immutable int l = currentLineLength + tokens[i .. e].map!(a => tokenLength(a)).sum(); + return l > config.dfmt_soft_max_line_length + || tokens[i .. e].canFind!(a => a.type == tok!"comment" || isBlockHeaderToken(a.type))(); + } + bool peekIsKeyword() nothrow { return index + 1 < tokens.length && isKeyword(tokens[index + 1].type); From fcad21ba614152b3875d20c701d60999337826e8 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Fri, 11 Jan 2019 00:50:50 +0100 Subject: [PATCH 5/8] Improve AA formatting fix #143, fix Pure-D/code-d#188 --- README.md | 2 ++ src/dfmt/config.d | 3 +++ src/dfmt/formatter.d | 30 ++++++++++++++++++++++++++-- src/dfmt/main.d | 5 +++++ tests/allman/associative_array.d.ref | 26 ++++++++++++++++++++++++ tests/associative_array.d | 26 ++++++++++++++++++++++++ tests/issue0128.args | 1 + tests/otbs/associative_array.d.ref | 25 +++++++++++++++++++++++ 8 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 tests/allman/associative_array.d.ref create mode 100644 tests/associative_array.d create mode 100644 tests/issue0128.args create mode 100644 tests/otbs/associative_array.d.ref diff --git a/README.md b/README.md index b97e46f..3020f32 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ found in .editorconfig files. * **--selective_import_space**: See **dfmt_selective_import_space** below * **--compact_labeled_statements**: See **dfmt_compact_labeled_statements** below * **--template_constraint_style**: See **dfmt_template_constraint_style** below +* **--space_before_aa_colon**: See **dfmt_space_before_aa_colon** below ### Example ``` @@ -107,6 +108,7 @@ dfmt_selective_import_space | `true`, `false` | `true` | Insert space after the dfmt_compact_labeled_statements | `true`, `false` | `true` | Place labels on the same line as the labeled `switch`, `for`, `foreach`, or `while` statement. dfmt_template_constraint_style | `conditional_newline_indent` `conditional_newline` `always_newline` `always_newline_indent` | `conditional_newline_indent` | Control the formatting of template constraints. dfmt_single_template_constraint_indent | `true`, `false` | `false` | Set if the constraints are indented by a single tab instead of two. Has only an effect for if indentation style if set to `always_newline_indent` or `conditional_newline_indent`. +dfmt_space_before_aa_colon | `true`, `false` | `false` | Adds a space after an associative array key before the `:` like in older dfmt versions. ## Terminology * Braces - `{` and `}` diff --git a/src/dfmt/config.d b/src/dfmt/config.d index a9e59a3..66d9b77 100644 --- a/src/dfmt/config.d +++ b/src/dfmt/config.d @@ -55,6 +55,8 @@ struct Config TemplateConstraintStyle dfmt_template_constraint_style; /// OptionalBoolean dfmt_single_template_constraint_indent; + /// + OptionalBoolean dfmt_space_before_aa_colon; mixin StandardEditorConfigFields; @@ -82,6 +84,7 @@ struct Config dfmt_compact_labeled_statements = OptionalBoolean.t; dfmt_template_constraint_style = TemplateConstraintStyle.conditional_newline_indent; dfmt_single_template_constraint_indent = OptionalBoolean.f; + dfmt_space_before_aa_colon = OptionalBoolean.f; } /** diff --git a/src/dfmt/formatter.d b/src/dfmt/formatter.d index c8bbe18..1d0c3d6 100644 --- a/src/dfmt/formatter.d +++ b/src/dfmt/formatter.d @@ -576,12 +576,28 @@ private: // Use the close bracket as the indent token to distinguish // the array initialiazer from an array index in the newline // handling code - pushWrapIndent(tok!"]"); + IndentStack.Details detail; + detail.wrap = false; + detail.temp = true; + detail.isAA = astInformation.assocArrayStartLocations.canFindIndex(tokens[index - 1].index); + pushWrapIndent(tok!"]", detail); + newline(); immutable size_t j = expressionEndIndex(index); linebreakHints = chooseLineBreakTokens(index, tokens[index .. j], depths[index .. j], config, currentLineLength, indentLevel); } + else if (arrayInitializerStart) + { + // This is a short (non-breaking) AA value + IndentStack.Details detail; + detail.wrap = false; + detail.temp = true; + detail.isAA = true; + detail.mini = true; + + pushWrapIndent(tok!"]", detail); + } else if (!currentIs(tok!")") && !currentIs(tok!"]") && (linebreakHints.canFindIndex(index - 1) || (linebreakHints.length == 0 && currentLineLength > config.max_line_length))) @@ -700,7 +716,12 @@ private: } else { - write(" : "); + const inAA = indents.topIs(tok!"]") && indents.topDetails.isAA; + + if (inAA && !config.dfmt_space_before_aa_colon) + write(": "); + else + write(" : "); index++; } } @@ -1368,6 +1389,11 @@ private: writeToken(); newline(); } + else if (indents.topIsTemp(tok!"]") && indents.topDetails.isAA && !indents.topDetails.mini) + { + writeToken(); + newline(); + } else if (!peekIs(tok!"}") && (linebreakHints.canFind(index) || (linebreakHints.length == 0 && currentLineLength > config.max_line_length))) { diff --git a/src/dfmt/main.d b/src/dfmt/main.d index 1e39bb3..6844cfc 100644 --- a/src/dfmt/main.d +++ b/src/dfmt/main.d @@ -89,6 +89,9 @@ else case "single_template_constraint_indent": optConfig.dfmt_single_template_constraint_indent = optVal; break; + case "space_before_aa_colon": + optConfig.dfmt_space_before_aa_colon = optVal; + break; default: assert(false, "Invalid command-line switch"); } @@ -116,6 +119,7 @@ else "split_operator_at_line_end", &handleBooleans, "compact_labeled_statements", &handleBooleans, "single_template_constraint_indent", &handleBooleans, + "space_before_aa_colon", &handleBooleans, "tab_width", &optConfig.tab_width, "template_constraint_style", &optConfig.dfmt_template_constraint_style); // dfmt on @@ -314,6 +318,7 @@ Formatting Options: --split_operator_at_line_end --compact_labeled_statements --template_constraint_style + --space_before_aa_colon `, optionsToString!(typeof(Config.dfmt_template_constraint_style))); } diff --git a/tests/allman/associative_array.d.ref b/tests/allman/associative_array.d.ref new file mode 100644 index 0000000..0a0e9d8 --- /dev/null +++ b/tests/allman/associative_array.d.ref @@ -0,0 +1,26 @@ +unittest +{ + Bson base = Bson([ + "maps": Bson([ + Bson(["id": Bson(4), "comment": Bson("hello")]), + Bson(["id": Bson(49), "comment": Bson(null)]) + ]), + "short": Bson(["a": "b", "c": "d"]), + "numbers": Bson([ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 + ]), + "shuffleOnReset": serializeToBson([ + "all": false, + "selected": true, + "maybe": false + ]), + "resetOnEmpty": Bson(false), + "applyMods": Bson(true), + "sendComments": Bson(true) + ]); + int[] x = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 + ]; +} diff --git a/tests/associative_array.d b/tests/associative_array.d new file mode 100644 index 0000000..d38762c --- /dev/null +++ b/tests/associative_array.d @@ -0,0 +1,26 @@ +unittest +{ + Bson base = Bson([ + "maps": Bson([ + Bson([ + "id": Bson(4), + "comment": Bson("hello") + ]), + Bson([ + "id": Bson(49), + "comment": Bson(null) + ]) + ]), + "short": Bson(["a": "b", "c": "d"]), + "numbers": Bson([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0]), + "shuffleOnReset": serializeToBson([ + "all": false, + "selected": true, + "maybe": false + ]), + "resetOnEmpty": Bson(false), + "applyMods": Bson(true), + "sendComments": Bson(true) + ]); + int[] x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; +} diff --git a/tests/issue0128.args b/tests/issue0128.args new file mode 100644 index 0000000..03f0da5 --- /dev/null +++ b/tests/issue0128.args @@ -0,0 +1 @@ +--space_before_aa_colon=true diff --git a/tests/otbs/associative_array.d.ref b/tests/otbs/associative_array.d.ref new file mode 100644 index 0000000..f167877 --- /dev/null +++ b/tests/otbs/associative_array.d.ref @@ -0,0 +1,25 @@ +unittest { + Bson base = Bson([ + "maps": Bson([ + Bson(["id": Bson(4), "comment": Bson("hello")]), + Bson(["id": Bson(49), "comment": Bson(null)]) + ]), + "short": Bson(["a": "b", "c": "d"]), + "numbers": Bson([ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 + ]), + "shuffleOnReset": serializeToBson([ + "all": false, + "selected": true, + "maybe": false + ]), + "resetOnEmpty": Bson(false), + "applyMods": Bson(true), + "sendComments": Bson(true) + ]); + int[] x = [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, + 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 + ]; +} From 48b2b84c338d184d414e0fcc0a4607cd78203a83 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Fri, 11 Jan 2019 01:11:03 +0100 Subject: [PATCH 6/8] Add documentation/comments & undo minor name change --- src/dfmt/formatter.d | 13 +++++++++---- src/dfmt/tokens.d | 2 ++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/dfmt/formatter.d b/src/dfmt/formatter.d index 1d0c3d6..a09db2b 100644 --- a/src/dfmt/formatter.d +++ b/src/dfmt/formatter.d @@ -579,6 +579,9 @@ private: IndentStack.Details detail; detail.wrap = false; detail.temp = true; + // wrap and temp are set manually to the values it would actually + // receive here because we want to set isAA for the ] token to know if + // we should definitely always new-line after every comma for a big AA detail.isAA = astInformation.assocArrayStartLocations.canFindIndex(tokens[index - 1].index); pushWrapIndent(tok!"]", detail); @@ -1256,7 +1259,7 @@ private: break; case tok!"]": indents.popWrapIndents(); - if (indents.topIsTemp(tok!"]")) + if (indents.topIs(tok!"]")) { if (!indents.topDetails.mini) newline(); @@ -1389,7 +1392,7 @@ private: writeToken(); newline(); } - else if (indents.topIsTemp(tok!"]") && indents.topDetails.isAA && !indents.topDetails.mini) + else if (indents.topIs(tok!"]") && indents.topDetails.isAA && !indents.topDetails.mini) { writeToken(); newline(); @@ -1428,7 +1431,7 @@ private: : tokens[i .. $].countUntil!(t => t.index == r.front) + i; immutable size_t j = min(expressionEndIndex(i), ufcsBreakLocation); // Use magical negative value for array literals and wrap indents - immutable inLvl = (indents.topIsWrap() || indents.topIsTemp(tok!"]")) ? -indentLevel + immutable inLvl = (indents.topIsWrap() || indents.topIs(tok!"]")) ? -indentLevel : indentLevel; linebreakHints = chooseLineBreakTokens(i, tokens[i .. j], depths[i .. j], config, currentLineLength, inLvl); @@ -1554,7 +1557,7 @@ private: else if (currentIs(tok!"]")) { indents.popWrapIndents(); - if (indents.topIsTemp(tok!"]")) + if (indents.topIs(tok!"]")) { indents.pop(); indentLevel = indents.indentLevel; @@ -1734,6 +1737,8 @@ const pure @safe @nogc: return i; } + /// Returns: true when the expression starting at index goes over the line length limit. + /// Uses matching `{}` or `[]` or otherwise takes everything up until a semicolon or opening brace using expressionEndIndex. bool isMultilineAt(size_t i) { import std.algorithm : map, sum, canFind; diff --git a/src/dfmt/tokens.d b/src/dfmt/tokens.d index 569c3dc..8f918f6 100644 --- a/src/dfmt/tokens.d +++ b/src/dfmt/tokens.d @@ -184,6 +184,8 @@ int breakCost(IdType p, IdType c) pure nothrow @safe @nogc case tok!"+=": return 200; case tok!":": + // colon could be after a label or an import, where it should normally wrap like before + // for everything else (associative arrays) try not breaking around colons return p == tok!"identifier" ? 0 : 300; case tok!".": return p == tok!")" ? 0 : 300; From 1da1ca6545c3d8922e2c883e5c5ce6f1eb697f6e Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Fri, 11 Jan 2019 20:19:50 +0100 Subject: [PATCH 7/8] Fix isMultilineAt for array + refactor right bracket Refactor formatRightBracket into own function --- src/dfmt/formatter.d | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/dfmt/formatter.d b/src/dfmt/formatter.d index a09db2b..a7ab809 100644 --- a/src/dfmt/formatter.d +++ b/src/dfmt/formatter.d @@ -571,7 +571,7 @@ private: return; immutable bool arrayInitializerStart = p == tok!"[" && astInformation.arrayStartLocations.canFindIndex(tokens[index - 1].index); - if (arrayInitializerStart && isMultilineAt(index)) + if (arrayInitializerStart && isMultilineAt(index - 1)) { // Use the close bracket as the indent token to distinguish // the array initialiazer from an array index in the newline @@ -644,6 +644,26 @@ private: writeToken(); } + void formatRightBracket() + in + { + assert(currentIs(tok!"]")); + } + body + { + indents.popWrapIndents(); + if (indents.topIs(tok!"]")) + { + if (!indents.topDetails.mini) + newline(); + else + indents.pop(); + } + writeToken(); + if (currentIs(tok!"identifier")) + write(" "); + } + void formatAt() { immutable size_t atIndex = tokens[index].index; @@ -1258,17 +1278,7 @@ private: formatColon(); break; case tok!"]": - indents.popWrapIndents(); - if (indents.topIs(tok!"]")) - { - if (!indents.topDetails.mini) - newline(); - else - indents.pop(); - } - writeToken(); - if (currentIs(tok!"identifier")) - write(" "); + formatRightBracket(); break; case tok!";": formatSemicolon(); From 60b2cff18a43f4751f2e3021621561360a0a9bc0 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Fri, 11 Jan 2019 20:22:30 +0100 Subject: [PATCH 8/8] Adjust issue0017 for now (minor improvement) --- tests/allman/issue0017.d.ref | 3 ++- tests/otbs/issue0017.d.ref | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/allman/issue0017.d.ref b/tests/allman/issue0017.d.ref index 8a32cec..67b8931 100644 --- a/tests/allman/issue0017.d.ref +++ b/tests/allman/issue0017.d.ref @@ -1,3 +1,4 @@ -immutable NameId[] namesA = [{"Aacgr", 0x00386}, // GREEK CAPITAL LETTER ALPHA WITH TONOS +immutable NameId[] namesA = [ +{"Aacgr", 0x00386}, // GREEK CAPITAL LETTER ALPHA WITH TONOS {"aacgr", 0x003AC}, // GREEK SMALL LETTER ALPHA WITH TONOS ]; diff --git a/tests/otbs/issue0017.d.ref b/tests/otbs/issue0017.d.ref index 8a32cec..67b8931 100644 --- a/tests/otbs/issue0017.d.ref +++ b/tests/otbs/issue0017.d.ref @@ -1,3 +1,4 @@ -immutable NameId[] namesA = [{"Aacgr", 0x00386}, // GREEK CAPITAL LETTER ALPHA WITH TONOS +immutable NameId[] namesA = [ +{"Aacgr", 0x00386}, // GREEK CAPITAL LETTER ALPHA WITH TONOS {"aacgr", 0x003AC}, // GREEK SMALL LETTER ALPHA WITH TONOS ];