More line wrapping tuning

This commit is contained in:
Hackerpilot 2015-03-23 00:33:59 -07:00
parent 3e4a131069
commit 48bc7ba0c2
1 changed files with 157 additions and 147 deletions

View File

@ -9,172 +9,182 @@ import std.d.lexer;
import dfmt.tokens; import dfmt.tokens;
import dfmt.config; import dfmt.config;
version = WTF_DMD;
struct State struct State
{ {
this(uint breaks, const Token[] tokens, immutable short[] depths, int depth, this(uint breaks, const Token[] tokens, immutable short[] depths,
const Config* config, int currentLineLength, int indentLevel) pure @safe const Config* config, int currentLineLength, int indentLevel) pure @safe
{ {
import std.math : abs; import std.math : abs;
import core.bitop : popcnt, bsf; import core.bitop : popcnt, bsf;
import std.algorithm:min; import std.algorithm : min;
import std.algorithm : map, sum;
immutable remainingCharsMultiplier = 40; // TODO: Figure out what is going on here.
immutable newlinePenalty = 800; version (WTF_DMD)
{
enum int remainingCharsMultiplier = 40;
enum int newlinePenalty = 800;
}
else
{
immutable int remainingCharsMultiplier = config.columnHardLimit - config.columnSoftLimit;
immutable int newlinePenalty = remainingCharsMultiplier * 20;
assert(remainingCharsMultiplier == 40);
assert(newlinePenalty == 800);
}
this.breaks = breaks; int cost = 0;
this._depth = depth; for (size_t i = 0; i != uint.sizeof * 8; ++i)
import std.algorithm : map, sum; {
if (((1 << i) & breaks) == 0)
continue;
immutable b = tokens[i].type;
immutable p = abs(depths[i]);
immutable bc = breakCost(b) * (p == 0 ? 1 : p * 2);
cost += bc;
}
int ll = currentLineLength;
bool solved = true;
if (breaks == 0)
{
immutable int l = currentLineLength + tokens.map!(a => tokenLength(a)).sum();
cost = l;
if (l > config.columnSoftLimit)
{
immutable int longPenalty = (l - config.columnSoftLimit) * remainingCharsMultiplier;
cost += longPenalty;
solved = longPenalty < newlinePenalty;
}
else
solved = true;
}
else
{
size_t i = 0;
foreach (_; 0 .. uint.sizeof * 8)
{
immutable size_t k = breaks >>> i;
immutable bool b = k == 0;
immutable size_t j = min(i + bsf(k) + 1, tokens.length);
ll += tokens[i .. j].map!(a => tokenLength(a)).sum();
if (ll > config.columnHardLimit)
{
solved = false;
break;
}
else if (ll > config.columnSoftLimit)
cost += (ll - config.columnSoftLimit) * remainingCharsMultiplier;
i = j;
ll = indentLevel * config.indentSize;
if (b)
break;
}
}
cost += popcnt(breaks) * newlinePenalty;
this._cost = 0; this.breaks = breaks;
for (size_t i = 0; i != uint.sizeof * 8; ++i) this._cost = cost;
{ this._solved = solved;
if (((1 << i) & breaks) == 0) }
continue;
immutable b = tokens[i].type;
immutable p = abs(depths[i]);
immutable bc = breakCost(b) * (p == 0 ? 1 : p * 2);
this._cost += bc;
}
int ll = currentLineLength;
this._solved = true;
if (breaks == 0)
{
immutable int l = currentLineLength + tokens.map!(a => tokenLength(a)).sum();
_cost = l;
if (l > config.columnSoftLimit)
{
immutable longPenalty = (l - config.columnSoftLimit) * remainingCharsMultiplier;
_cost += longPenalty;
this._solved = longPenalty < newlinePenalty;
}
else
this._solved = true;
}
else
{
size_t i = 0;
foreach (_; 0 .. 32)
{
immutable size_t k = breaks >>> i;
immutable bool b = k == 0;
immutable size_t j = min(i + bsf(k) + 1, tokens.length);
ll += tokens[i .. j].map!(a => tokenLength(a)).sum();
if (ll > config.columnHardLimit)
{
this._solved = false;
break;
}
else if (ll > config.columnSoftLimit)
_cost += (ll - config.columnSoftLimit) * remainingCharsMultiplier;
i = j;
ll = indentLevel * config.indentSize;
if (b)
break;
}
}
this._cost += popcnt(breaks) * newlinePenalty;
}
int cost() const pure nothrow @safe @property int cost() const pure nothrow @safe @property
{ {
return _cost; return _cost;
} }
int depth() const pure nothrow @safe @property int solved() const pure nothrow @safe @property
{ {
return _depth; return _solved;
} }
int solved() const pure nothrow @safe @property int opCmp(ref const State other) const pure nothrow @safe
{ {
return _solved; import core.bitop : bsf, popcnt;
}
int opCmp(ref const State other) const pure nothrow @safe if (cost < other.cost || (cost == other.cost && ((popcnt(breaks)
{ && popcnt(other.breaks) && bsf(breaks) > bsf(other.breaks))
import core.bitop : bsf , popcnt; || (_solved && !other.solved))))
if (cost < other.cost || (cost == other.cost && ((popcnt(breaks) {
&& popcnt(other.breaks) && bsf(breaks) > bsf(other.breaks)) || (_solved && !other.solved)))) return -1;
{ }
return -1; return other.cost > _cost;
} }
return other.cost > _cost;
}
bool opEquals(ref const State other) const pure nothrow @safe bool opEquals(ref const State other) const pure nothrow @safe
{ {
return other.breaks == breaks; return other.breaks == breaks;
} }
size_t toHash() const nothrow @safe size_t toHash() const pure nothrow @safe
{ {
return typeid(breaks).getHash(&breaks); return breaks;
} }
uint breaks;
uint breaks;
private: private:
int _cost; int _cost;
int _depth; bool _solved;
bool _solved;
} }
size_t[] chooseLineBreakTokens(size_t index, const Token[] tokens, immutable short[] depths, size_t[] chooseLineBreakTokens(size_t index, const Token[] tokens,
const Config* config, int currentLineLength, int indentLevel) immutable short[] depths, const Config* config, int currentLineLength, int indentLevel)
{ {
import std.container.rbtree : RedBlackTree; import std.container.rbtree : RedBlackTree;
import std.algorithm : filter, min; import std.algorithm : filter, min;
import core.bitop:popcnt; import core.bitop : popcnt;
static size_t[] genRetVal(uint breaks, size_t index) pure nothrow @safe static size_t[] genRetVal(uint breaks, size_t index) pure nothrow @safe
{ {
auto retVal = new size_t[](popcnt(breaks)); auto retVal = new size_t[](popcnt(breaks));
size_t j = 0; size_t j = 0;
foreach (uint i; 0 .. uint.sizeof * 8) foreach (uint i; 0 .. uint.sizeof * 8)
if ((1 << i) & breaks) if ((1 << i) & breaks)
retVal[j++] = index + i; retVal[j++] = index + i;
return retVal; return retVal;
} }
enum ALGORITHMIC_COMPLEXITY_SUCKS = uint.sizeof * 8; enum ALGORITHMIC_COMPLEXITY_SUCKS = uint.sizeof * 8;
immutable size_t tokensEnd = min(tokens.length, ALGORITHMIC_COMPLEXITY_SUCKS); immutable size_t tokensEnd = min(tokens.length, ALGORITHMIC_COMPLEXITY_SUCKS);
int depth = 0; auto open = new RedBlackTree!State;
auto open = new RedBlackTree!State; open.insert(State(0, tokens[0 .. tokensEnd], depths[0 .. tokensEnd],
open.insert(State(0, tokens[0 .. tokensEnd], config, currentLineLength, indentLevel));
depths[0 .. tokensEnd], depth, config, currentLineLength, indentLevel)); State lowest;
State lowest; while (!open.empty)
while (!open.empty) {
{ State current = open.front();
State current = open.front(); if (current.cost < lowest.cost)
if (current.cost < lowest.cost) lowest = current;
lowest = current; open.removeFront();
open.removeFront(); if (current.solved)
if (current.solved) {
{ return genRetVal(current.breaks, index);
return genRetVal(current.breaks, index); }
} validMoves!(typeof(open))(open, tokens[0 .. tokensEnd],
validMoves!(typeof(open))(open, tokens[0 .. tokensEnd], depths[0 .. tokensEnd], depths[0 .. tokensEnd], current.breaks, config, currentLineLength, indentLevel);
current.breaks, config, currentLineLength, indentLevel, depth); }
} if (open.empty)
if (open.empty) return genRetVal(lowest.breaks, index);
return genRetVal(lowest.breaks, index); foreach (r; open[].filter!(a => a.solved))
foreach (r; open[].filter!(a => a.solved)) return genRetVal(r.breaks, index);
return genRetVal(r.breaks, index); assert(false);
assert(false);
} }
void validMoves(OR)(auto ref OR output, const Token[] tokens, immutable short[] depths, void validMoves(OR)(auto ref OR output, const Token[] tokens,
uint current, const Config* config, int currentLineLength, int indentLevel, immutable short[] depths, uint current, const Config* config,
int depth) pure int currentLineLength, int indentLevel) pure
{ {
import std.algorithm : sort, canFind; import std.algorithm : sort, canFind;
import std.array : insertInPlace; import std.array : insertInPlace;
foreach (i, token; tokens) foreach (i, token; tokens)
{ {
if (!isBreakToken(token.type) || (((1 << i) & current) != 0)) if (!isBreakToken(token.type) || (((1 << i) & current) != 0))
continue; continue;
immutable uint breaks = current | (1 << i); immutable uint breaks = current | (1 << i);
output.insert(State(breaks, tokens, depths, depth + 1, config, output.insert(State(breaks, tokens, depths, config,
currentLineLength, indentLevel)); currentLineLength, indentLevel));
} }
} }