Update command-line parsing and add some configuration options.
This commit is contained in:
parent
a563865149
commit
34103ac999
|
@ -5,26 +5,102 @@
|
||||||
|
|
||||||
module dfmt.config;
|
module dfmt.config;
|
||||||
|
|
||||||
/// The only good brace styles
|
/// Brace styles
|
||||||
enum BraceStyle
|
enum BraceStyle
|
||||||
{
|
{
|
||||||
|
/// $(LINK https://en.wikipedia.org/wiki/Indent_style#Allman_style)
|
||||||
allman,
|
allman,
|
||||||
otbs
|
/// $(LINK https://en.wikipedia.org/wiki/Indent_style#Variant:_1TBS)
|
||||||
|
otbs,
|
||||||
|
/// $(LINK https://en.wikipedia.org/wiki/Indent_style#Variant:_Stroustrup)
|
||||||
|
stroustrup
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Newline styles
|
||||||
|
enum Newlines
|
||||||
|
{
|
||||||
|
/// Old Mac
|
||||||
|
cr,
|
||||||
|
/// UNIX, Linux, BSD, New Mac, iOS, Android, etc...
|
||||||
|
lf,
|
||||||
|
/// Windows
|
||||||
|
crlf
|
||||||
|
}
|
||||||
|
|
||||||
|
template getHelp(alias S)
|
||||||
|
{
|
||||||
|
enum getHelp = __traits(getAttributes, S)[0].text;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configuration options for formatting
|
/// Configuration options for formatting
|
||||||
struct FormatterConfig
|
struct Config
|
||||||
{
|
{
|
||||||
/// Number of spaces used for indentation
|
///
|
||||||
|
@Help("Number of spaces used for indentation")
|
||||||
uint indentSize = 4;
|
uint indentSize = 4;
|
||||||
/// Use tabs or spaces
|
|
||||||
|
///
|
||||||
|
@Help("Use tabs or spaces")
|
||||||
bool useTabs = false;
|
bool useTabs = false;
|
||||||
/// Size of a tab character
|
|
||||||
|
///
|
||||||
|
@Help("Size of a tab character")
|
||||||
uint tabSize = 4;
|
uint tabSize = 4;
|
||||||
/// Soft line wrap limit
|
|
||||||
|
///
|
||||||
|
@Help("Soft line wrap limit")
|
||||||
uint columnSoftLimit = 80;
|
uint columnSoftLimit = 80;
|
||||||
/// Hard line wrap limit
|
|
||||||
|
///
|
||||||
|
@Help("Hard line wrap limit")
|
||||||
uint columnHardLimit = 120;
|
uint columnHardLimit = 120;
|
||||||
/// Use the One True Brace Style
|
|
||||||
|
///
|
||||||
|
@Help("Brace style can be 'otbs', 'allman', or 'stroustrup'")
|
||||||
BraceStyle braceStyle = BraceStyle.allman;
|
BraceStyle braceStyle = BraceStyle.allman;
|
||||||
|
|
||||||
|
///
|
||||||
|
@Help("Align labels, cases, and defaults with their enclosing switch")
|
||||||
|
bool alignSwitchStatements = true;
|
||||||
|
|
||||||
|
///
|
||||||
|
@Help("Decrease the indentation of labels")
|
||||||
|
bool outdentLabels = true;
|
||||||
|
|
||||||
|
///
|
||||||
|
@Help("Decrease the indentation level of attributes")
|
||||||
|
bool outdentAttributes = true;
|
||||||
|
|
||||||
|
///
|
||||||
|
@Help("Place operators on the end of the previous line when splitting lines")
|
||||||
|
bool splitOperatorAtEnd = false;
|
||||||
|
|
||||||
|
///
|
||||||
|
@Help("Insert spaces after the closing paren of a cast expression")
|
||||||
|
bool spaceAfterCast = true;
|
||||||
|
|
||||||
|
///
|
||||||
|
@Help("Newline style can be 'cr', 'lf', or 'crlf'")
|
||||||
|
Newlines newlineType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns:
|
||||||
|
* true if the configuration is valid
|
||||||
|
*/
|
||||||
|
bool isValid()
|
||||||
|
{
|
||||||
|
import std.stdio : stderr;
|
||||||
|
|
||||||
|
if (columnSoftLimit > columnHardLimit)
|
||||||
|
{
|
||||||
|
stderr.writeln("Column hard limit must be greater than or equal to column soft limit");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct Help
|
||||||
|
{
|
||||||
|
string text;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,20 +24,34 @@ else
|
||||||
{
|
{
|
||||||
int main(string[] args)
|
int main(string[] args)
|
||||||
{
|
{
|
||||||
import std.getopt : getopt;
|
import std.getopt : getopt, defaultGetoptPrinter;
|
||||||
|
|
||||||
bool inplace = false;
|
bool inplace = false;
|
||||||
bool show_usage = false;
|
Config config;
|
||||||
FormatterConfig formatterConfig;
|
auto getOptResult = getopt(args,
|
||||||
getopt(args, "help|h", &show_usage, "inplace", &inplace, "tabs|t",
|
"inplace", "Modify files in-place", &inplace,
|
||||||
&formatterConfig.useTabs, "braces", &formatterConfig.braceStyle);
|
"tabs|t", getHelp!(Config.useTabs), &config.useTabs,
|
||||||
if (show_usage)
|
"braces", getHelp!(Config.braceStyle), &config.braceStyle,
|
||||||
{
|
"colSoft", getHelp!(Config.columnSoftLimit), &config.columnSoftLimit,
|
||||||
import std.path : baseName;
|
"colHard", getHelp!(Config.columnHardLimit), &config.columnHardLimit,
|
||||||
|
"tabSize", getHelp!(Config.tabSize), &config.tabSize,
|
||||||
|
"indentSize", getHelp!(Config.indentSize), &config.indentSize,
|
||||||
|
"alignSwitchCases", getHelp!(Config.alignSwitchStatements), &config.alignSwitchStatements,
|
||||||
|
"outdentLabels", getHelp!(Config.outdentLabels), &config.outdentLabels,
|
||||||
|
"outdentAttributes", getHelp!(Config.outdentAttributes), &config.outdentAttributes,
|
||||||
|
"splitOperatorAtEnd", getHelp!(Config.splitOperatorAtEnd), &config.splitOperatorAtEnd,
|
||||||
|
"spaceAfterCast", getHelp!(Config.spaceAfterCast), &config.spaceAfterCast,
|
||||||
|
"newlineType", getHelp!(Config.newlineType), &config.newlineType);
|
||||||
|
|
||||||
writef(USAGE, baseName(args[0]));
|
if (getOptResult.helpWanted)
|
||||||
|
{
|
||||||
|
defaultGetoptPrinter("dfmt 0.3.0-dev\nOptions:", getOptResult.options);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!config.isValid())
|
||||||
|
return 1;
|
||||||
|
|
||||||
File output = stdout;
|
File output = stdout;
|
||||||
ubyte[] buffer;
|
ubyte[] buffer;
|
||||||
args.popFront();
|
args.popFront();
|
||||||
|
@ -53,7 +67,7 @@ else
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
format("stdin", buffer, output.lockingTextWriter(), &formatterConfig);
|
format("stdin", buffer, output.lockingTextWriter(), &config);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -79,7 +93,7 @@ else
|
||||||
f.rawRead(buffer);
|
f.rawRead(buffer);
|
||||||
if (inplace)
|
if (inplace)
|
||||||
output = File(path, "wb");
|
output = File(path, "wb");
|
||||||
format(path, buffer, output.lockingTextWriter(), &formatterConfig);
|
format(path, buffer, output.lockingTextWriter(), &config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -88,19 +102,8 @@ else
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
immutable USAGE = "usage: %s [--inplace] [<path>...]
|
|
||||||
Formats D code.
|
|
||||||
|
|
||||||
--inplace Change file in-place instead of outputing to stdout
|
|
||||||
(implicit in case of multiple files)
|
|
||||||
--tabs | -t Use tabs instead of spaces for indentation
|
|
||||||
--braces=allman Use Allman indent style (default)
|
|
||||||
--braces=otbs Use the One True Brace Style
|
|
||||||
--help | -h Display this help and exit
|
|
||||||
";
|
|
||||||
|
|
||||||
void format(OutputRange)(string source_desc, ubyte[] buffer, OutputRange output,
|
void format(OutputRange)(string source_desc, ubyte[] buffer, OutputRange output,
|
||||||
FormatterConfig* formatterConfig)
|
Config* formatterConfig)
|
||||||
{
|
{
|
||||||
LexerConfig config;
|
LexerConfig config;
|
||||||
config.stringBehavior = StringBehavior.source;
|
config.stringBehavior = StringBehavior.source;
|
||||||
|
@ -160,7 +163,7 @@ struct TokenFormatter(OutputRange)
|
||||||
* decisions.
|
* decisions.
|
||||||
*/
|
*/
|
||||||
this(const(Token)[] tokens, immutable short[] depths, OutputRange output,
|
this(const(Token)[] tokens, immutable short[] depths, OutputRange output,
|
||||||
ASTInformation* astInformation, FormatterConfig* config)
|
ASTInformation* astInformation, Config* config)
|
||||||
{
|
{
|
||||||
this.tokens = tokens;
|
this.tokens = tokens;
|
||||||
this.depths = depths;
|
this.depths = depths;
|
||||||
|
@ -206,7 +209,7 @@ private:
|
||||||
IndentStack indents;
|
IndentStack indents;
|
||||||
|
|
||||||
/// Configuration
|
/// Configuration
|
||||||
const FormatterConfig* config;
|
const Config* config;
|
||||||
|
|
||||||
/// Keep track of whether or not an extra newline was just added because of
|
/// Keep track of whether or not an extra newline was just added because of
|
||||||
/// an import statement.
|
/// an import statement.
|
||||||
|
@ -601,7 +604,8 @@ private:
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!justAddedExtraNewline && !peekBackIsOneOf(false, tok!"{", tok!"}", tok!";", tok!";"))
|
if (!justAddedExtraNewline && !peekBackIsOneOf(false, tok!"{",
|
||||||
|
tok!"}", tok!";", tok!";"))
|
||||||
{
|
{
|
||||||
if (config.braceStyle == BraceStyle.otbs)
|
if (config.braceStyle == BraceStyle.otbs)
|
||||||
{
|
{
|
||||||
|
@ -611,7 +615,8 @@ private:
|
||||||
{
|
{
|
||||||
indents.popWrapIndents();
|
indents.popWrapIndents();
|
||||||
indents.push(tok!"{");
|
indents.push(tok!"{");
|
||||||
if (index == 1 || peekBackIsOneOf(true, tok!":", tok!"{", tok!"}", tok!")", tok!";"))
|
if (index == 1 || peekBackIsOneOf(true, tok!":", tok!"{",
|
||||||
|
tok!"}", tok!")", tok!";"))
|
||||||
{
|
{
|
||||||
indentLevel = indents.indentSize - 1;
|
indentLevel = indents.indentSize - 1;
|
||||||
}
|
}
|
||||||
|
@ -653,8 +658,8 @@ private:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Silly hack to format enums better.
|
// Silly hack to format enums better.
|
||||||
if ((peekBackIsLiteralOrIdent() || peekBackIsOneOf(true, tok!")", tok!","))
|
if ((peekBackIsLiteralOrIdent() || peekBackIsOneOf(true, tok!")",
|
||||||
&& !peekBackIsSlashSlash())
|
tok!",")) && !peekBackIsSlashSlash())
|
||||||
newline();
|
newline();
|
||||||
write("}");
|
write("}");
|
||||||
if (index + 1 < tokens.length
|
if (index + 1 < tokens.length
|
||||||
|
@ -950,7 +955,8 @@ private:
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
writeToken();
|
writeToken();
|
||||||
if (!currentIs(tok!")") && !currentIs(tok!"]") && !currentIs(tok!"}") && !currentIs(tok!"comment"))
|
if (!currentIs(tok!")") && !currentIs(tok!"]")
|
||||||
|
&& !currentIs(tok!"}") && !currentIs(tok!"comment"))
|
||||||
{
|
{
|
||||||
write(" ");
|
write(" ");
|
||||||
}
|
}
|
||||||
|
@ -1034,8 +1040,8 @@ private:
|
||||||
}
|
}
|
||||||
else if (currentIs(tok!"case") || currentIs(tok!"default"))
|
else if (currentIs(tok!"case") || currentIs(tok!"default"))
|
||||||
{
|
{
|
||||||
while (indents.length && (peekBackIs(tok!"}", true) || peekBackIs(tok!";", true))
|
while (indents.length && (peekBackIs(tok!"}", true)
|
||||||
&& isTempIndent(indents.top()))
|
|| peekBackIs(tok!";", true)) && isTempIndent(indents.top()))
|
||||||
{
|
{
|
||||||
indents.pop();
|
indents.pop();
|
||||||
}
|
}
|
||||||
|
@ -1049,7 +1055,8 @@ private:
|
||||||
{
|
{
|
||||||
indents.popWrapIndents();
|
indents.popWrapIndents();
|
||||||
indents.push(tok!"{");
|
indents.push(tok!"{");
|
||||||
if (index == 1 || peekBackIsOneOf(true, tok!":", tok!"{", tok!"}", tok!")", tok!";"))
|
if (index == 1 || peekBackIsOneOf(true, tok!":", tok!"{",
|
||||||
|
tok!"}", tok!")", tok!";"))
|
||||||
{
|
{
|
||||||
indentLevel = indents.indentSize - 1;
|
indentLevel = indents.indentSize - 1;
|
||||||
}
|
}
|
||||||
|
@ -1086,8 +1093,8 @@ private:
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
while (indents.length && (peekBackIsOneOf(true, tok!"}", tok!";")
|
while (indents.length && (peekBackIsOneOf(true, tok!"}",
|
||||||
&& indents.top != tok!";") && isTempIndent(indents.top()))
|
tok!";") && indents.top != tok!";") && isTempIndent(indents.top()))
|
||||||
{
|
{
|
||||||
indents.pop();
|
indents.pop();
|
||||||
}
|
}
|
||||||
|
@ -1216,8 +1223,7 @@ const pure @safe @nogc:
|
||||||
return tokenLength(tokens[i]);
|
return tokenLength(tokens[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ref current() nothrow
|
ref current() nothrow in
|
||||||
in
|
|
||||||
{
|
{
|
||||||
assert(index < tokens.length);
|
assert(index < tokens.length);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import dfmt.config;
|
||||||
struct State
|
struct State
|
||||||
{
|
{
|
||||||
this(size_t[] breaks, const Token[] tokens, immutable short[] depths, int depth,
|
this(size_t[] breaks, const Token[] tokens, immutable short[] depths, int depth,
|
||||||
const FormatterConfig* formatterConfig, int currentLineLength, int indentLevel) pure @safe
|
const Config* config, int currentLineLength, int indentLevel) pure @safe
|
||||||
{
|
{
|
||||||
import std.math : abs;
|
import std.math : abs;
|
||||||
|
|
||||||
|
@ -39,9 +39,9 @@ struct State
|
||||||
{
|
{
|
||||||
immutable int l = currentLineLength + tokens.map!(a => tokenLength(a)).sum();
|
immutable int l = currentLineLength + tokens.map!(a => tokenLength(a)).sum();
|
||||||
_cost = l;
|
_cost = l;
|
||||||
if (l > formatterConfig.columnSoftLimit)
|
if (l > config.columnSoftLimit)
|
||||||
{
|
{
|
||||||
immutable longPenalty = (l - formatterConfig.columnSoftLimit) * remainingCharsMultiplier;
|
immutable longPenalty = (l - config.columnSoftLimit) * remainingCharsMultiplier;
|
||||||
_cost += longPenalty;
|
_cost += longPenalty;
|
||||||
this._solved = longPenalty < newlinePenalty;
|
this._solved = longPenalty < newlinePenalty;
|
||||||
}
|
}
|
||||||
|
@ -54,15 +54,15 @@ struct State
|
||||||
{
|
{
|
||||||
immutable size_t j = breakIndex < breaks.length ? breaks[breakIndex] : tokens.length;
|
immutable size_t j = breakIndex < breaks.length ? breaks[breakIndex] : tokens.length;
|
||||||
ll += tokens[i .. j].map!(a => tokenLength(a)).sum();
|
ll += tokens[i .. j].map!(a => tokenLength(a)).sum();
|
||||||
if (ll > formatterConfig.columnHardLimit)
|
if (ll > config.columnHardLimit)
|
||||||
{
|
{
|
||||||
this._solved = false;
|
this._solved = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (ll > formatterConfig.columnSoftLimit)
|
else if (ll > config.columnSoftLimit)
|
||||||
_cost += (ll - formatterConfig.columnSoftLimit) * remainingCharsMultiplier;
|
_cost += (ll - config.columnSoftLimit) * remainingCharsMultiplier;
|
||||||
i = j;
|
i = j;
|
||||||
ll = indentLevel * formatterConfig.indentSize;
|
ll = indentLevel * config.indentSize;
|
||||||
breakIndex++;
|
breakIndex++;
|
||||||
}
|
}
|
||||||
while (i + 1 < tokens.length);
|
while (i + 1 < tokens.length);
|
||||||
|
@ -113,7 +113,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t[] chooseLineBreakTokens(size_t index, const Token[] tokens, immutable short[] depths,
|
size_t[] chooseLineBreakTokens(size_t index, const Token[] tokens, immutable short[] depths,
|
||||||
const FormatterConfig* formatterConfig, int currentLineLength, int indentLevel) pure
|
const Config* config, int currentLineLength, int indentLevel) pure
|
||||||
{
|
{
|
||||||
import std.container.rbtree : RedBlackTree;
|
import std.container.rbtree : RedBlackTree;
|
||||||
import std.algorithm : filter, min;
|
import std.algorithm : filter, min;
|
||||||
|
@ -123,7 +123,7 @@ size_t[] chooseLineBreakTokens(size_t index, const Token[] tokens, immutable sho
|
||||||
int depth = 0;
|
int depth = 0;
|
||||||
auto open = new RedBlackTree!State;
|
auto open = new RedBlackTree!State;
|
||||||
open.insert(State(cast(size_t[])[], tokens[0 .. tokensEnd],
|
open.insert(State(cast(size_t[])[], tokens[0 .. tokensEnd],
|
||||||
depths[0 .. tokensEnd], depth, formatterConfig, currentLineLength, indentLevel));
|
depths[0 .. tokensEnd], depth, config, currentLineLength, indentLevel));
|
||||||
State lowest;
|
State lowest;
|
||||||
while (!open.empty)
|
while (!open.empty)
|
||||||
{
|
{
|
||||||
|
@ -137,7 +137,7 @@ size_t[] chooseLineBreakTokens(size_t index, const Token[] tokens, immutable sho
|
||||||
return current.breaks;
|
return current.breaks;
|
||||||
}
|
}
|
||||||
foreach (next; validMoves(tokens[0 .. tokensEnd], depths[0 .. tokensEnd],
|
foreach (next; validMoves(tokens[0 .. tokensEnd], depths[0 .. tokensEnd],
|
||||||
current, formatterConfig, currentLineLength, indentLevel, depth))
|
current, config, currentLineLength, indentLevel, depth))
|
||||||
{
|
{
|
||||||
open.insert(next);
|
open.insert(next);
|
||||||
}
|
}
|
||||||
|
@ -156,7 +156,7 @@ size_t[] chooseLineBreakTokens(size_t index, const Token[] tokens, immutable sho
|
||||||
}
|
}
|
||||||
|
|
||||||
State[] validMoves(const Token[] tokens, immutable short[] depths, ref const State current,
|
State[] validMoves(const Token[] tokens, immutable short[] depths, ref const State current,
|
||||||
const FormatterConfig* formatterConfig, int currentLineLength, int indentLevel,
|
const Config* config, int currentLineLength, int indentLevel,
|
||||||
int depth) pure @safe
|
int depth) pure @safe
|
||||||
{
|
{
|
||||||
import std.algorithm : sort, canFind;
|
import std.algorithm : sort, canFind;
|
||||||
|
@ -171,7 +171,7 @@ State[] validMoves(const Token[] tokens, immutable short[] depths, ref const Sta
|
||||||
breaks ~= current.breaks;
|
breaks ~= current.breaks;
|
||||||
breaks ~= i;
|
breaks ~= i;
|
||||||
sort(breaks);
|
sort(breaks);
|
||||||
states ~= State(breaks, tokens, depths, depth + 1, formatterConfig,
|
states ~= State(breaks, tokens, depths, depth + 1, config,
|
||||||
currentLineLength, indentLevel);
|
currentLineLength, indentLevel);
|
||||||
}
|
}
|
||||||
return states;
|
return states;
|
||||||
|
|
Loading…
Reference in New Issue