From c4b9178e81c30a956aaace03c6bcea4c4b36fe60 Mon Sep 17 00:00:00 2001 From: Hackerpilot Date: Tue, 8 May 2018 17:28:36 -0700 Subject: [PATCH] Better formatting for UFCS chains --- libdparse | 2 +- src/dfmt/ast_info.d | 29 +++++++++++++++++++++++++++-- src/dfmt/formatter.d | 18 ++++++++++++++++-- tests/allman/breakOnDots.d.ref | 3 ++- tests/allman/ufcschain.d.ref | 6 ++++++ tests/otbs/breakOnDots.d.ref | 3 ++- tests/otbs/ufcschain.d.ref | 5 +++++ tests/ufcschain.d | 4 ++++ 8 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 tests/allman/ufcschain.d.ref create mode 100644 tests/otbs/ufcschain.d.ref create mode 100644 tests/ufcschain.d diff --git a/libdparse b/libdparse index 687c0ca..4f3c9ed 160000 --- a/libdparse +++ b/libdparse @@ -1 +1 @@ -Subproject commit 687c0ca751747ebe498c183da1a3ee3119d57932 +Subproject commit 4f3c9ed6455cc5409c2a570576f8bd994763d652 diff --git a/src/dfmt/ast_info.d b/src/dfmt/ast_info.d index 7b1dade..afb1784 100644 --- a/src/dfmt/ast_info.d +++ b/src/dfmt/ast_info.d @@ -29,7 +29,8 @@ struct ASTInformation /// Sorts the arrays so that binary search will work on them void cleanup() { - import std.algorithm : sort; + import std.algorithm : sort, uniq; + import std.array : array; sort(doubleNewlineLocations); sort(spaceAfterLocations); @@ -48,9 +49,10 @@ struct ASTInformation sort(constructorDestructorLocations); sort(staticConstructorDestructorLocations); sort(sharedStaticConstructorDestructorLocations); - sort!((a,b) => a.endLocation < b.endLocation) (indentInfoSortedByEndLocation); + sort(ufcsHintLocations); + ufcsHintLocations = ufcsHintLocations.uniq().array(); } /// Locations of end braces for struct bodies @@ -104,6 +106,9 @@ struct ASTInformation /// Locations of constructor/destructor "this" tokens ? size_t[] constructorDestructorLocations; + /// Locations of '.' characters that might be UFCS chains. + size_t[] ufcsHintLocations; + BraceIndentInfo[] indentInfoSortedByEndLocation; } @@ -295,6 +300,26 @@ final class FormatVisitor : ASTVisitor override void visit(const UnaryExpression unary) { + import std.typecons : rebindable; + + int chainLength; + auto u = rebindable(unary); + while (u !is null) + { + if (u.identifierOrTemplateInstance !is null + && u.identifierOrTemplateInstance.templateInstance !is null) + chainLength++; + u = u.unaryExpression; + } + if (chainLength > 1) + { + u = unary; + while (u.unaryExpression !is null) + { + astInformation.ufcsHintLocations ~= u.dotLocation; + u = u.unaryExpression; + } + } if (unary.prefix.type == tok!"~" || unary.prefix.type == tok!"&" || unary.prefix.type == tok!"*" || unary.prefix.type == tok!"+" || unary.prefix.type == tok!"-") diff --git a/src/dfmt/formatter.d b/src/dfmt/formatter.d index 4890e31..fc54206 100644 --- a/src/dfmt/formatter.d +++ b/src/dfmt/formatter.d @@ -1229,11 +1229,14 @@ private: break; case tok!".": regenLineBreakHintsIfNecessary(index); - if (linebreakHints.canFind(index) || (linebreakHints.length == 0 + immutable bool ufcsWrap = astInformation.ufcsHintLocations.canFindIndex(current.index); + if (ufcsWrap || linebreakHints.canFind(index) || (linebreakHints.length == 0 && currentLineLength + nextTokenLength() > config.max_line_length)) { pushWrapIndent(); newline(); + if (ufcsWrap) + regenLineBreakHints(index); } writeToken(); break; @@ -1356,7 +1359,18 @@ private: void regenLineBreakHints(immutable size_t i) { - immutable size_t j = expressionEndIndex(i); + import std.range : assumeSorted; + import std.algorithm.comparison : min; + import std.algorithm.searching : countUntil; + + // The end of the tokens considered by the line break algorithm is + // either the expression end index or the next mandatory line break, + // whichever is first. + auto r = assumeSorted(astInformation.ufcsHintLocations).upperBound(tokens[i].index); + immutable ufcsBreakLocation = r.empty + ? size_t.max + : 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 : indentLevel; diff --git a/tests/allman/breakOnDots.d.ref b/tests/allman/breakOnDots.d.ref index 5190a9f..f5ab653 100644 --- a/tests/allman/breakOnDots.d.ref +++ b/tests/allman/breakOnDots.d.ref @@ -3,7 +3,8 @@ unittest { { foreach (abcde, def; abcdef.map!(battlecruiser => battlecruiser[123 .. 1231231]) - .filter!(bravo => charlie[10] > 90000).sum()) + .filter!(bravo => charlie[10] > 90000) + .sum()) { } diff --git a/tests/allman/ufcschain.d.ref b/tests/allman/ufcschain.d.ref new file mode 100644 index 0000000..c36b1ae --- /dev/null +++ b/tests/allman/ufcschain.d.ref @@ -0,0 +1,6 @@ +void main() +{ + stuff[].map!(things => stuff.doThings) + .filter!(stuff) + .array(); +} diff --git a/tests/otbs/breakOnDots.d.ref b/tests/otbs/breakOnDots.d.ref index 0327058..bdfb29b 100644 --- a/tests/otbs/breakOnDots.d.ref +++ b/tests/otbs/breakOnDots.d.ref @@ -2,7 +2,8 @@ unittest { { { foreach (abcde, def; abcdef.map!(battlecruiser => battlecruiser[123 .. 1231231]) - .filter!(bravo => charlie[10] > 90000).sum()) { + .filter!(bravo => charlie[10] > 90000) + .sum()) { } abcdeabcdeabcde(12341234).abcdeabcdeabcde(12341234).abcdeabcdeabcde(12341234) diff --git a/tests/otbs/ufcschain.d.ref b/tests/otbs/ufcschain.d.ref new file mode 100644 index 0000000..2a4dd2b --- /dev/null +++ b/tests/otbs/ufcschain.d.ref @@ -0,0 +1,5 @@ +void main() { + stuff[].map!(things => stuff.doThings) + .filter!(stuff) + .array(); +} diff --git a/tests/ufcschain.d b/tests/ufcschain.d new file mode 100644 index 0000000..dbdd7dc --- /dev/null +++ b/tests/ufcschain.d @@ -0,0 +1,4 @@ +void main() +{ + stuff[].map!(things => stuff.doThings).filter!(stuff).array(); +}