Refactoring
This commit is contained in:
parent
88e6397de6
commit
3ce5b5e7c6
|
@ -2,5 +2,4 @@
|
|||
bin
|
||||
*.d.out
|
||||
.dub
|
||||
dfmt
|
||||
dub.selections.json
|
||||
|
|
2
makefile
2
makefile
|
@ -1,5 +1,5 @@
|
|||
SRC := $(shell find src -name "*.d") $(shell find libdparse/src -name "*.d")
|
||||
INCLUDE_PATHS := -Ilibdparse/src
|
||||
INCLUDE_PATHS := -Ilibdparse/src -Isrc
|
||||
DMD_FLAGS := -g -w $(INCLUDE_PATHS)
|
||||
LDC_FLAGS := -g -w -oq $(INCLUDE_PATHS)
|
||||
|
||||
|
|
|
@ -0,0 +1,212 @@
|
|||
// Copyright Brian Schott 2015.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
module dfmt.ast_info;
|
||||
|
||||
import std.d.lexer;
|
||||
import std.d.ast;
|
||||
|
||||
///
|
||||
struct ASTInformation
|
||||
{
|
||||
/// Sorts the arrays so that binary search will work on them
|
||||
void cleanup()
|
||||
{
|
||||
import std.algorithm : sort;
|
||||
|
||||
sort(doubleNewlineLocations);
|
||||
sort(spaceAfterLocations);
|
||||
sort(unaryLocations);
|
||||
sort(attributeDeclarationLines);
|
||||
sort(caseEndLocations);
|
||||
sort(structInitStartLocations);
|
||||
sort(structInitEndLocations);
|
||||
sort(funLitStartLocations);
|
||||
sort(funLitEndLocations);
|
||||
sort(conditionalWithElseLocations);
|
||||
sort(arrayStartLocations);
|
||||
}
|
||||
|
||||
/// Locations of end braces for struct bodies
|
||||
size_t[] doubleNewlineLocations;
|
||||
|
||||
/// Locations of tokens where a space is needed (such as the '*' in a type)
|
||||
size_t[] spaceAfterLocations;
|
||||
|
||||
/// Locations of unary operators
|
||||
size_t[] unaryLocations;
|
||||
|
||||
/// Lines containing attribute declarations
|
||||
size_t[] attributeDeclarationLines;
|
||||
|
||||
/// Case statement colon locations
|
||||
size_t[] caseEndLocations;
|
||||
|
||||
/// Opening braces of struct initializers
|
||||
size_t[] structInitStartLocations;
|
||||
|
||||
/// Closing braces of struct initializers
|
||||
size_t[] structInitEndLocations;
|
||||
|
||||
/// Opening braces of function literals
|
||||
size_t[] funLitStartLocations;
|
||||
|
||||
/// Closing braces of function literals
|
||||
size_t[] funLitEndLocations;
|
||||
|
||||
size_t[] conditionalWithElseLocations;
|
||||
|
||||
size_t[] conditionalStatementLocations;
|
||||
|
||||
size_t[] arrayStartLocations;
|
||||
}
|
||||
|
||||
/// Collects information from the AST that is useful for the formatter
|
||||
final class FormatVisitor : ASTVisitor
|
||||
{
|
||||
///
|
||||
this(ASTInformation* astInformation)
|
||||
{
|
||||
this.astInformation = astInformation;
|
||||
}
|
||||
|
||||
override void visit(const ArrayInitializer arrayInitializer)
|
||||
{
|
||||
astInformation.arrayStartLocations ~= arrayInitializer.startLocation;
|
||||
arrayInitializer.accept(this);
|
||||
}
|
||||
|
||||
override void visit(const ConditionalDeclaration dec)
|
||||
{
|
||||
if (dec.falseDeclaration !is null)
|
||||
{
|
||||
auto condition = dec.compileCondition;
|
||||
if (condition.versionCondition !is null)
|
||||
{
|
||||
astInformation.conditionalWithElseLocations ~= condition.versionCondition.versionIndex;
|
||||
}
|
||||
else if (condition.debugCondition !is null)
|
||||
{
|
||||
astInformation.conditionalWithElseLocations ~= condition.debugCondition.debugIndex;
|
||||
}
|
||||
// Skip "static if" because the formatting for normal "if" handles
|
||||
// it properly
|
||||
}
|
||||
dec.accept(this);
|
||||
}
|
||||
|
||||
override void visit(const ConditionalStatement statement)
|
||||
{
|
||||
auto condition = statement.compileCondition;
|
||||
if (condition.versionCondition !is null)
|
||||
{
|
||||
astInformation.conditionalStatementLocations ~= condition.versionCondition.versionIndex;
|
||||
}
|
||||
else if (condition.debugCondition !is null)
|
||||
{
|
||||
astInformation.conditionalStatementLocations ~= condition.debugCondition.debugIndex;
|
||||
}
|
||||
statement.accept(this);
|
||||
}
|
||||
|
||||
override void visit(const FunctionLiteralExpression funcLit)
|
||||
{
|
||||
astInformation.funLitStartLocations ~= funcLit.functionBody.blockStatement.startLocation;
|
||||
astInformation.funLitEndLocations ~= funcLit.functionBody.blockStatement.endLocation;
|
||||
funcLit.accept(this);
|
||||
}
|
||||
|
||||
override void visit(const DefaultStatement defaultStatement)
|
||||
{
|
||||
astInformation.caseEndLocations ~= defaultStatement.colonLocation;
|
||||
defaultStatement.accept(this);
|
||||
}
|
||||
|
||||
override void visit(const CaseStatement caseStatement)
|
||||
{
|
||||
astInformation.caseEndLocations ~= caseStatement.colonLocation;
|
||||
caseStatement.accept(this);
|
||||
}
|
||||
|
||||
override void visit(const CaseRangeStatement caseRangeStatement)
|
||||
{
|
||||
astInformation.caseEndLocations ~= caseRangeStatement.colonLocation;
|
||||
caseRangeStatement.accept(this);
|
||||
}
|
||||
|
||||
override void visit(const FunctionBody functionBody)
|
||||
{
|
||||
if (functionBody.blockStatement !is null)
|
||||
astInformation.doubleNewlineLocations ~= functionBody.blockStatement.endLocation;
|
||||
if (functionBody.bodyStatement !is null && functionBody.bodyStatement.blockStatement !is null)
|
||||
astInformation.doubleNewlineLocations ~= functionBody.bodyStatement.blockStatement.endLocation;
|
||||
functionBody.accept(this);
|
||||
}
|
||||
|
||||
override void visit(const StructInitializer structInitializer)
|
||||
{
|
||||
astInformation.structInitStartLocations ~= structInitializer.startLocation;
|
||||
astInformation.structInitEndLocations ~= structInitializer.endLocation;
|
||||
structInitializer.accept(this);
|
||||
}
|
||||
|
||||
override void visit(const EnumBody enumBody)
|
||||
{
|
||||
astInformation.doubleNewlineLocations ~= enumBody.endLocation;
|
||||
enumBody.accept(this);
|
||||
}
|
||||
|
||||
override void visit(const Unittest unittest_)
|
||||
{
|
||||
astInformation.doubleNewlineLocations ~= unittest_.blockStatement.endLocation;
|
||||
unittest_.accept(this);
|
||||
}
|
||||
|
||||
override void visit(const Invariant invariant_)
|
||||
{
|
||||
astInformation.doubleNewlineLocations ~= invariant_.blockStatement.endLocation;
|
||||
invariant_.accept(this);
|
||||
}
|
||||
|
||||
override void visit(const StructBody structBody)
|
||||
{
|
||||
astInformation.doubleNewlineLocations ~= structBody.endLocation;
|
||||
structBody.accept(this);
|
||||
}
|
||||
|
||||
override void visit(const TemplateDeclaration templateDeclaration)
|
||||
{
|
||||
astInformation.doubleNewlineLocations ~= templateDeclaration.endLocation;
|
||||
templateDeclaration.accept(this);
|
||||
}
|
||||
|
||||
override void visit(const TypeSuffix typeSuffix)
|
||||
{
|
||||
if (typeSuffix.star.type != tok!"")
|
||||
astInformation.spaceAfterLocations ~= typeSuffix.star.index;
|
||||
typeSuffix.accept(this);
|
||||
}
|
||||
|
||||
override void visit(const UnaryExpression unary)
|
||||
{
|
||||
if (unary.prefix.type == tok!"~" || unary.prefix.type == tok!"&"
|
||||
|| unary.prefix.type == tok!"*" || unary.prefix.type == tok!"+"
|
||||
|| unary.prefix.type == tok!"-")
|
||||
{
|
||||
astInformation.unaryLocations ~= unary.prefix.index;
|
||||
}
|
||||
unary.accept(this);
|
||||
}
|
||||
|
||||
override void visit(const AttributeDeclaration attributeDeclaration)
|
||||
{
|
||||
astInformation.attributeDeclarationLines ~= attributeDeclaration.line;
|
||||
attributeDeclaration.accept(this);
|
||||
}
|
||||
|
||||
private:
|
||||
ASTInformation* astInformation;
|
||||
alias visit = ASTVisitor.visit;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright Brian Schott 2015.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
module dfmt.config;
|
||||
|
||||
/// The only good brace styles
|
||||
enum BraceStyle
|
||||
{
|
||||
allman,
|
||||
otbs
|
||||
}
|
||||
|
||||
/// Configuration options for formatting
|
||||
struct FormatterConfig
|
||||
{
|
||||
/// Number of spaces used for indentation
|
||||
uint indentSize = 4;
|
||||
/// Use tabs or spaces
|
||||
bool useTabs = false;
|
||||
/// Size of a tab character
|
||||
uint tabSize = 4;
|
||||
/// Soft line wrap limit
|
||||
uint columnSoftLimit = 80;
|
||||
/// Hard line wrap limit
|
||||
uint columnHardLimit = 120;
|
||||
/// Use the One True Brace Style
|
||||
BraceStyle braceStyle = BraceStyle.allman;
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
// Copyright Brian Schott 2015.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
module dfmt.indentation;
|
||||
|
||||
import std.d.lexer;
|
||||
|
||||
bool isWrapIndent(IdType type) pure nothrow @nogc @safe
|
||||
{
|
||||
return type != tok!"{" && type != tok!":" && type != tok!"]" && isOperator(type);
|
||||
}
|
||||
|
||||
bool isTempIndent(IdType type) pure nothrow @nogc @safe
|
||||
{
|
||||
return type != tok!"{";
|
||||
}
|
||||
|
||||
struct IndentStack
|
||||
{
|
||||
int indentToMostRecent(IdType item)
|
||||
{
|
||||
size_t i = index;
|
||||
while (true)
|
||||
{
|
||||
if (arr[i] == item)
|
||||
return indentSize(i);
|
||||
if (i > 0)
|
||||
i--;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int wrapIndents() const pure nothrow @property
|
||||
{
|
||||
if (index == 0)
|
||||
return 0;
|
||||
int tempIndentCount = 0;
|
||||
for (size_t i = index; i > 0; i--)
|
||||
{
|
||||
if (!isWrapIndent(arr[i]) && arr[i] != tok!"]")
|
||||
break;
|
||||
tempIndentCount++;
|
||||
}
|
||||
return tempIndentCount;
|
||||
}
|
||||
|
||||
void push(IdType item) pure nothrow
|
||||
{
|
||||
index = index == 255 ? index : index + 1;
|
||||
arr[index] = item;
|
||||
}
|
||||
|
||||
void pop() pure nothrow
|
||||
{
|
||||
index = index == 0 ? index : index - 1;
|
||||
}
|
||||
|
||||
void popWrapIndents() pure nothrow @safe @nogc
|
||||
{
|
||||
while (index > 0 && isWrapIndent(arr[index]))
|
||||
index--;
|
||||
}
|
||||
|
||||
void popTempIndents() pure nothrow @safe @nogc
|
||||
{
|
||||
while (index > 0 && isTempIndent(arr[index]))
|
||||
index--;
|
||||
}
|
||||
|
||||
bool topIs(IdType type) const pure nothrow @safe @nogc
|
||||
{
|
||||
return index > 0 && arr[index] == type;
|
||||
}
|
||||
|
||||
bool topIsOneOf(IdType[] types...) const pure nothrow @safe @nogc
|
||||
{
|
||||
if (index <= 0)
|
||||
return false;
|
||||
immutable topType = arr[index];
|
||||
foreach (t; types)
|
||||
if (t == topType)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
IdType top() const pure nothrow @property @safe @nogc
|
||||
{
|
||||
return arr[index];
|
||||
}
|
||||
|
||||
int indentSize(size_t k = size_t.max) const pure nothrow @safe @nogc
|
||||
{
|
||||
if (index == 0)
|
||||
return 0;
|
||||
immutable size_t j = k == size_t.max ? index : k - 1;
|
||||
int size = 0;
|
||||
foreach (i; 1 .. j + 1)
|
||||
{
|
||||
if ((i + 1 <= index && arr[i] != tok!"]" && !isWrapIndent(arr[i])
|
||||
&& isTempIndent(arr[i]) && (!isTempIndent(arr[i + 1])
|
||||
|| arr[i + 1] == tok!"switch")))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
size++;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
int length() const pure nothrow @property
|
||||
{
|
||||
return cast(int) index;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t index;
|
||||
IdType[256] arr;
|
||||
}
|
1244
src/dfmt/main.d
1244
src/dfmt/main.d
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,218 @@
|
|||
// Copyright Brian Schott 2015.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
module dfmt.tokens;
|
||||
|
||||
import std.d.lexer;
|
||||
|
||||
/// Length of an invalid token
|
||||
enum int INVALID_TOKEN_LENGTH = -1;
|
||||
|
||||
int tokenLength(ref const Token t) pure @safe @nogc
|
||||
{
|
||||
import std.algorithm : countUntil;
|
||||
|
||||
switch (t.type)
|
||||
{
|
||||
case tok!"doubleLiteral":
|
||||
case tok!"floatLiteral":
|
||||
case tok!"idoubleLiteral":
|
||||
case tok!"ifloatLiteral":
|
||||
case tok!"intLiteral":
|
||||
case tok!"longLiteral":
|
||||
case tok!"realLiteral":
|
||||
case tok!"irealLiteral":
|
||||
case tok!"uintLiteral":
|
||||
case tok!"ulongLiteral":
|
||||
case tok!"characterLiteral":
|
||||
return cast(int) t.text.length;
|
||||
case tok!"identifier":
|
||||
case tok!"stringLiteral":
|
||||
case tok!"wstringLiteral":
|
||||
case tok!"dstringLiteral":
|
||||
// TODO: Unicode line breaks and old-Mac line endings
|
||||
auto c = cast(int) t.text.countUntil('\n');
|
||||
if (c == -1)
|
||||
return cast(int) t.text.length;
|
||||
else
|
||||
return c;
|
||||
mixin(generateFixedLengthCases());
|
||||
default:
|
||||
return INVALID_TOKEN_LENGTH;
|
||||
}
|
||||
}
|
||||
|
||||
bool isBreakToken(IdType t) pure nothrow @safe @nogc
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case tok!"||":
|
||||
case tok!"&&":
|
||||
case tok!"(":
|
||||
case tok!"[":
|
||||
case tok!",":
|
||||
case tok!":":
|
||||
case tok!";":
|
||||
case tok!"^^":
|
||||
case tok!"^=":
|
||||
case tok!"^":
|
||||
case tok!"~=":
|
||||
case tok!"<<=":
|
||||
case tok!"<<":
|
||||
case tok!"<=":
|
||||
case tok!"<>=":
|
||||
case tok!"<>":
|
||||
case tok!"<":
|
||||
case tok!"==":
|
||||
case tok!"=>":
|
||||
case tok!"=":
|
||||
case tok!">=":
|
||||
case tok!">>=":
|
||||
case tok!">>>=":
|
||||
case tok!">>>":
|
||||
case tok!">>":
|
||||
case tok!">":
|
||||
case tok!"|=":
|
||||
case tok!"|":
|
||||
case tok!"-=":
|
||||
case tok!"!<=":
|
||||
case tok!"!<>=":
|
||||
case tok!"!<>":
|
||||
case tok!"!<":
|
||||
case tok!"!=":
|
||||
case tok!"!>=":
|
||||
case tok!"!>":
|
||||
case tok!"?":
|
||||
case tok!"/=":
|
||||
case tok!"/":
|
||||
case tok!"..":
|
||||
case tok!"*=":
|
||||
case tok!"&=":
|
||||
case tok!"%=":
|
||||
case tok!"%":
|
||||
case tok!"+=":
|
||||
case tok!".":
|
||||
case tok!"~":
|
||||
case tok!"+":
|
||||
case tok!"-":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int breakCost(IdType t) pure nothrow @safe @nogc
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case tok!"||":
|
||||
case tok!"&&":
|
||||
case tok!",":
|
||||
return 0;
|
||||
case tok!"(":
|
||||
return 60;
|
||||
case tok!"[":
|
||||
return 400;
|
||||
case tok!":":
|
||||
case tok!";":
|
||||
case tok!"^^":
|
||||
case tok!"^=":
|
||||
case tok!"^":
|
||||
case tok!"~=":
|
||||
case tok!"<<=":
|
||||
case tok!"<<":
|
||||
case tok!"<=":
|
||||
case tok!"<>=":
|
||||
case tok!"<>":
|
||||
case tok!"<":
|
||||
case tok!"==":
|
||||
case tok!"=>":
|
||||
case tok!"=":
|
||||
case tok!">=":
|
||||
case tok!">>=":
|
||||
case tok!">>>=":
|
||||
case tok!">>>":
|
||||
case tok!">>":
|
||||
case tok!">":
|
||||
case tok!"|=":
|
||||
case tok!"|":
|
||||
case tok!"-=":
|
||||
case tok!"!<=":
|
||||
case tok!"!<>=":
|
||||
case tok!"!<>":
|
||||
case tok!"!<":
|
||||
case tok!"!=":
|
||||
case tok!"!>=":
|
||||
case tok!"!>":
|
||||
case tok!"?":
|
||||
case tok!"/=":
|
||||
case tok!"/":
|
||||
case tok!"..":
|
||||
case tok!"*=":
|
||||
case tok!"&=":
|
||||
case tok!"%=":
|
||||
case tok!"%":
|
||||
case tok!"+":
|
||||
case tok!"-":
|
||||
case tok!"~":
|
||||
case tok!"+=":
|
||||
return 200;
|
||||
case tok!".":
|
||||
return 900;
|
||||
default:
|
||||
return 1000;
|
||||
}
|
||||
}
|
||||
|
||||
pure nothrow @safe @nogc unittest
|
||||
{
|
||||
foreach (ubyte u; 0 .. ubyte.max)
|
||||
if (isBreakToken(u))
|
||||
assert(breakCost(u) != 1000);
|
||||
}
|
||||
|
||||
|
||||
private string generateFixedLengthCases()
|
||||
{
|
||||
import std.algorithm : map;
|
||||
import std.string : format;
|
||||
import std.array : join;
|
||||
|
||||
assert(__ctfe);
|
||||
|
||||
string[] spacedOperatorTokens = [
|
||||
",", "..", "...", "/", "/=", "!", "!<", "!<=", "!<>", "!<>=", "!=", "!>",
|
||||
"!>=", "%", "%=", "&", "&&", "&=", "*", "*=", "+", "+=", "-", "-=", ":",
|
||||
";", "<", "<<", "<<=", "<=", "<>", "<>=", "=", "==", "=>", ">", ">=",
|
||||
">>", ">>=", ">>>", ">>>=", "?", "@", "^", "^=", "^^", "^^=", "|", "|=", "||",
|
||||
"~", "~="
|
||||
];
|
||||
immutable spacedOperatorTokenCases = spacedOperatorTokens.map!(
|
||||
a => format(`case tok!"%s": return %d + 1;`, a, a.length)).join("\n\t");
|
||||
|
||||
string[] identifierTokens = [
|
||||
"abstract", "alias", "align", "asm", "assert", "auto", "body", "bool",
|
||||
"break", "byte", "case", "cast", "catch", "cdouble", "cent", "cfloat",
|
||||
"char", "class", "const", "continue", "creal", "dchar", "debug",
|
||||
"default", "delegate", "delete", "deprecated", "do", "double", "else",
|
||||
"enum", "export", "extern", "false", "final", "finally", "float", "for",
|
||||
"foreach", "foreach_reverse", "function", "goto", "idouble", "if",
|
||||
"ifloat", "immutable", "import", "in", "inout", "int", "interface",
|
||||
"invariant", "ireal", "is", "lazy", "long", "macro", "mixin", "module",
|
||||
"new", "nothrow", "null", "out", "override", "package", "pragma",
|
||||
"private", "protected", "public", "pure", "real", "ref", "return",
|
||||
"scope", "shared", "short", "static", "struct", "super", "switch",
|
||||
"synchronized", "template", "this", "throw", "true", "try", "typedef",
|
||||
"typeid", "typeof", "ubyte", "ucent", "uint", "ulong", "union",
|
||||
"unittest", "ushort", "version", "void", "volatile", "wchar", "while",
|
||||
"with", "__DATE__", "__EOF__", "__FILE__", "__FUNCTION__", "__gshared",
|
||||
"__LINE__", "__MODULE__", "__parameters", "__PRETTY_FUNCTION__",
|
||||
"__TIME__", "__TIMESTAMP__", "__traits", "__vector", "__VENDOR__",
|
||||
"__VERSION__", "$", "++", "--", ".", "[", "]", "(", ")", "{", "}"
|
||||
];
|
||||
immutable identifierTokenCases = identifierTokens.map!(
|
||||
a => format(`case tok!"%s": return %d;`, a, a.length)).join("\n\t");
|
||||
return spacedOperatorTokenCases ~ identifierTokenCases;
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
// Copyright Brian Schott 2015.
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
module dfmt.wrapping;
|
||||
|
||||
import std.d.lexer;
|
||||
import dfmt.tokens;
|
||||
import dfmt.config;
|
||||
|
||||
struct State
|
||||
{
|
||||
this(size_t[] breaks, const Token[] tokens, immutable short[] depths, int depth,
|
||||
const FormatterConfig* formatterConfig, int currentLineLength, int indentLevel) pure @safe
|
||||
{
|
||||
import std.math : abs;
|
||||
|
||||
immutable remainingCharsMultiplier = 40;
|
||||
immutable newlinePenalty = 800;
|
||||
|
||||
this.breaks = breaks;
|
||||
this._depth = depth;
|
||||
import std.algorithm : map, sum;
|
||||
|
||||
this._cost = 0;
|
||||
for (size_t i = 0; i != breaks.length; ++i)
|
||||
{
|
||||
immutable b = tokens[breaks[i]].type;
|
||||
immutable p = abs(depths[breaks[i]]);
|
||||
immutable bc = breakCost(b) * (p == 0 ? 1 : p * 2);
|
||||
this._cost += bc;
|
||||
}
|
||||
int ll = currentLineLength;
|
||||
size_t breakIndex = 0;
|
||||
size_t i = 0;
|
||||
this._solved = true;
|
||||
if (breaks.length == 0)
|
||||
{
|
||||
immutable int l = currentLineLength + tokens.map!(a => tokenLength(a)).sum();
|
||||
_cost = l;
|
||||
if (l > formatterConfig.columnSoftLimit)
|
||||
{
|
||||
immutable longPenalty = (l - formatterConfig.columnSoftLimit) * remainingCharsMultiplier;
|
||||
_cost += longPenalty;
|
||||
this._solved = longPenalty < newlinePenalty;
|
||||
}
|
||||
else
|
||||
this._solved = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
immutable size_t j = breakIndex < breaks.length ? breaks[breakIndex] : tokens.length;
|
||||
ll += tokens[i .. j].map!(a => tokenLength(a)).sum();
|
||||
if (ll > formatterConfig.columnHardLimit)
|
||||
{
|
||||
this._solved = false;
|
||||
break;
|
||||
}
|
||||
else if (ll > formatterConfig.columnSoftLimit)
|
||||
_cost += (ll - formatterConfig.columnSoftLimit) * remainingCharsMultiplier;
|
||||
i = j;
|
||||
ll = indentLevel * formatterConfig.indentSize;
|
||||
breakIndex++;
|
||||
}
|
||||
while (i + 1 < tokens.length);
|
||||
}
|
||||
this._cost += breaks.length * newlinePenalty;
|
||||
}
|
||||
|
||||
int cost() const pure nothrow @safe @property
|
||||
{
|
||||
return _cost;
|
||||
}
|
||||
|
||||
int depth() const pure nothrow @safe @property
|
||||
{
|
||||
return _depth;
|
||||
}
|
||||
|
||||
int solved() const pure nothrow @safe @property
|
||||
{
|
||||
return _solved;
|
||||
}
|
||||
|
||||
int opCmp(ref const State other) const pure nothrow @safe
|
||||
{
|
||||
if (cost < other.cost || (cost == other.cost && ((breaks.length
|
||||
&& other.breaks.length && breaks[0] > other.breaks[0]) || (_solved && !other.solved))))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return other.cost > _cost;
|
||||
}
|
||||
|
||||
bool opEquals(ref const State other) const pure nothrow @safe
|
||||
{
|
||||
return other.breaks == breaks;
|
||||
}
|
||||
|
||||
size_t toHash() const nothrow @safe
|
||||
{
|
||||
return typeid(breaks).getHash(&breaks);
|
||||
}
|
||||
|
||||
size_t[] breaks;
|
||||
private:
|
||||
int _cost;
|
||||
int _depth;
|
||||
bool _solved;
|
||||
}
|
||||
|
||||
size_t[] chooseLineBreakTokens(size_t index, const Token[] tokens, immutable short[] depths,
|
||||
const FormatterConfig* formatterConfig, int currentLineLength, int indentLevel) pure
|
||||
{
|
||||
import std.container.rbtree : RedBlackTree;
|
||||
import std.algorithm : filter, min;
|
||||
|
||||
enum ALGORITHMIC_COMPLEXITY_SUCKS = 25;
|
||||
immutable size_t tokensEnd = min(tokens.length, ALGORITHMIC_COMPLEXITY_SUCKS);
|
||||
int depth = 0;
|
||||
auto open = new RedBlackTree!State;
|
||||
open.insert(State(cast(size_t[])[], tokens[0 .. tokensEnd],
|
||||
depths[0 .. tokensEnd], depth, formatterConfig, currentLineLength, indentLevel));
|
||||
State lowest;
|
||||
while (!open.empty)
|
||||
{
|
||||
State current = open.front();
|
||||
if (current.cost < lowest.cost)
|
||||
lowest = current;
|
||||
open.removeFront();
|
||||
if (current.solved)
|
||||
{
|
||||
current.breaks[] += index;
|
||||
return current.breaks;
|
||||
}
|
||||
foreach (next; validMoves(tokens[0 .. tokensEnd], depths[0 .. tokensEnd],
|
||||
current, formatterConfig, currentLineLength, indentLevel, depth))
|
||||
{
|
||||
open.insert(next);
|
||||
}
|
||||
}
|
||||
if (open.empty)
|
||||
{
|
||||
lowest.breaks[] += index;
|
||||
return lowest.breaks;
|
||||
}
|
||||
foreach (r; open[].filter!(a => a.solved))
|
||||
{
|
||||
r.breaks[] += index;
|
||||
return r.breaks;
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
|
||||
State[] validMoves(const Token[] tokens, immutable short[] depths, ref const State current,
|
||||
const FormatterConfig* formatterConfig, int currentLineLength, int indentLevel,
|
||||
int depth) pure @safe
|
||||
{
|
||||
import std.algorithm : sort, canFind;
|
||||
import std.array : insertInPlace;
|
||||
|
||||
State[] states;
|
||||
foreach (i, token; tokens)
|
||||
{
|
||||
if (!isBreakToken(token.type) || current.breaks.canFind(i))
|
||||
continue;
|
||||
size_t[] breaks;
|
||||
breaks ~= current.breaks;
|
||||
breaks ~= i;
|
||||
sort(breaks);
|
||||
states ~= State(breaks, tokens, depths, depth + 1, formatterConfig,
|
||||
currentLineLength, indentLevel);
|
||||
}
|
||||
return states;
|
||||
}
|
Loading…
Reference in New Issue