Better formatting for UFCS chains

This commit is contained in:
Hackerpilot 2018-05-08 17:28:36 -07:00 committed by stefan-koch-sociomantic
parent 6eb7b95173
commit c4b9178e81
8 changed files with 63 additions and 7 deletions

@ -1 +1 @@
Subproject commit 687c0ca751747ebe498c183da1a3ee3119d57932
Subproject commit 4f3c9ed6455cc5409c2a570576f8bd994763d652

View file

@ -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!"-")

View file

@ -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;

View file

@ -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())
{
}

View file

@ -0,0 +1,6 @@
void main()
{
stuff[].map!(things => stuff.doThings)
.filter!(stuff)
.array();
}

View file

@ -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)

View file

@ -0,0 +1,5 @@
void main() {
stuff[].map!(things => stuff.doThings)
.filter!(stuff)
.array();
}

4
tests/ufcschain.d Normal file
View file

@ -0,0 +1,4 @@
void main()
{
stuff[].map!(things => stuff.doThings).filter!(stuff).array();
}