Update command-line parsing and add some configuration options.

This commit is contained in:
Hackerpilot 2015-03-20 01:53:28 -07:00
parent a563865149
commit 34103ac999
3 changed files with 140 additions and 58 deletions

View File

@ -5,26 +5,102 @@
module dfmt.config;
/// The only good brace styles
/// Brace styles
enum BraceStyle
{
/// $(LINK https://en.wikipedia.org/wiki/Indent_style#Allman_style)
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
struct FormatterConfig
struct Config
{
/// Number of spaces used for indentation
///
@Help("Number of spaces used for indentation")
uint indentSize = 4;
/// Use tabs or spaces
///
@Help("Use tabs or spaces")
bool useTabs = false;
/// Size of a tab character
///
@Help("Size of a tab character")
uint tabSize = 4;
/// Soft line wrap limit
///
@Help("Soft line wrap limit")
uint columnSoftLimit = 80;
/// Hard line wrap limit
///
@Help("Hard line wrap limit")
uint columnHardLimit = 120;
/// Use the One True Brace Style
///
@Help("Brace style can be 'otbs', 'allman', or 'stroustrup'")
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;
}

View File

@ -24,20 +24,34 @@ else
{
int main(string[] args)
{
import std.getopt : getopt;
import std.getopt : getopt, defaultGetoptPrinter;
bool inplace = false;
bool show_usage = false;
FormatterConfig formatterConfig;
getopt(args, "help|h", &show_usage, "inplace", &inplace, "tabs|t",
&formatterConfig.useTabs, "braces", &formatterConfig.braceStyle);
if (show_usage)
{
import std.path : baseName;
Config config;
auto getOptResult = getopt(args,
"inplace", "Modify files in-place", &inplace,
"tabs|t", getHelp!(Config.useTabs), &config.useTabs,
"braces", getHelp!(Config.braceStyle), &config.braceStyle,
"colSoft", getHelp!(Config.columnSoftLimit), &config.columnSoftLimit,
"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;
}
if (!config.isValid())
return 1;
File output = stdout;
ubyte[] buffer;
args.popFront();
@ -53,7 +67,7 @@ else
else
break;
}
format("stdin", buffer, output.lockingTextWriter(), &formatterConfig);
format("stdin", buffer, output.lockingTextWriter(), &config);
}
else
{
@ -79,7 +93,7 @@ else
f.rawRead(buffer);
if (inplace)
output = File(path, "wb");
format(path, buffer, output.lockingTextWriter(), &formatterConfig);
format(path, buffer, output.lockingTextWriter(), &config);
}
}
return 0;
@ -88,19 +102,8 @@ else
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,
FormatterConfig* formatterConfig)
Config* formatterConfig)
{
LexerConfig config;
config.stringBehavior = StringBehavior.source;
@ -160,7 +163,7 @@ struct TokenFormatter(OutputRange)
* decisions.
*/
this(const(Token)[] tokens, immutable short[] depths, OutputRange output,
ASTInformation* astInformation, FormatterConfig* config)
ASTInformation* astInformation, Config* config)
{
this.tokens = tokens;
this.depths = depths;
@ -206,7 +209,7 @@ private:
IndentStack indents;
/// Configuration
const FormatterConfig* config;
const Config* config;
/// Keep track of whether or not an extra newline was just added because of
/// an import statement.
@ -601,7 +604,8 @@ private:
}
else
{
if (!justAddedExtraNewline && !peekBackIsOneOf(false, tok!"{", tok!"}", tok!";", tok!";"))
if (!justAddedExtraNewline && !peekBackIsOneOf(false, tok!"{",
tok!"}", tok!";", tok!";"))
{
if (config.braceStyle == BraceStyle.otbs)
{
@ -611,7 +615,8 @@ private:
{
indents.popWrapIndents();
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;
}
@ -653,8 +658,8 @@ private:
else
{
// Silly hack to format enums better.
if ((peekBackIsLiteralOrIdent() || peekBackIsOneOf(true, tok!")", tok!","))
&& !peekBackIsSlashSlash())
if ((peekBackIsLiteralOrIdent() || peekBackIsOneOf(true, tok!")",
tok!",")) && !peekBackIsSlashSlash())
newline();
write("}");
if (index + 1 < tokens.length
@ -950,7 +955,8 @@ private:
else
{
writeToken();
if (!currentIs(tok!")") && !currentIs(tok!"]") && !currentIs(tok!"}") && !currentIs(tok!"comment"))
if (!currentIs(tok!")") && !currentIs(tok!"]")
&& !currentIs(tok!"}") && !currentIs(tok!"comment"))
{
write(" ");
}
@ -1034,8 +1040,8 @@ private:
}
else if (currentIs(tok!"case") || currentIs(tok!"default"))
{
while (indents.length && (peekBackIs(tok!"}", true) || peekBackIs(tok!";", true))
&& isTempIndent(indents.top()))
while (indents.length && (peekBackIs(tok!"}", true)
|| peekBackIs(tok!";", true)) && isTempIndent(indents.top()))
{
indents.pop();
}
@ -1049,7 +1055,8 @@ private:
{
indents.popWrapIndents();
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;
}
@ -1086,8 +1093,8 @@ private:
}
else
{
while (indents.length && (peekBackIsOneOf(true, tok!"}", tok!";")
&& indents.top != tok!";") && isTempIndent(indents.top()))
while (indents.length && (peekBackIsOneOf(true, tok!"}",
tok!";") && indents.top != tok!";") && isTempIndent(indents.top()))
{
indents.pop();
}
@ -1216,8 +1223,7 @@ const pure @safe @nogc:
return tokenLength(tokens[i]);
}
ref current() nothrow
in
ref current() nothrow in
{
assert(index < tokens.length);
}

View File

@ -12,7 +12,7 @@ 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
const Config* config, int currentLineLength, int indentLevel) pure @safe
{
import std.math : abs;
@ -39,9 +39,9 @@ struct State
{
immutable int l = currentLineLength + tokens.map!(a => tokenLength(a)).sum();
_cost = l;
if (l > formatterConfig.columnSoftLimit)
if (l > config.columnSoftLimit)
{
immutable longPenalty = (l - formatterConfig.columnSoftLimit) * remainingCharsMultiplier;
immutable longPenalty = (l - config.columnSoftLimit) * remainingCharsMultiplier;
_cost += longPenalty;
this._solved = longPenalty < newlinePenalty;
}
@ -54,15 +54,15 @@ struct State
{
immutable size_t j = breakIndex < breaks.length ? breaks[breakIndex] : tokens.length;
ll += tokens[i .. j].map!(a => tokenLength(a)).sum();
if (ll > formatterConfig.columnHardLimit)
if (ll > config.columnHardLimit)
{
this._solved = false;
break;
}
else if (ll > formatterConfig.columnSoftLimit)
_cost += (ll - formatterConfig.columnSoftLimit) * remainingCharsMultiplier;
else if (ll > config.columnSoftLimit)
_cost += (ll - config.columnSoftLimit) * remainingCharsMultiplier;
i = j;
ll = indentLevel * formatterConfig.indentSize;
ll = indentLevel * config.indentSize;
breakIndex++;
}
while (i + 1 < tokens.length);
@ -113,7 +113,7 @@ private:
}
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.algorithm : filter, min;
@ -123,7 +123,7 @@ size_t[] chooseLineBreakTokens(size_t index, const Token[] tokens, immutable sho
int depth = 0;
auto open = new RedBlackTree!State;
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;
while (!open.empty)
{
@ -137,7 +137,7 @@ size_t[] chooseLineBreakTokens(size_t index, const Token[] tokens, immutable sho
return current.breaks;
}
foreach (next; validMoves(tokens[0 .. tokensEnd], depths[0 .. tokensEnd],
current, formatterConfig, currentLineLength, indentLevel, depth))
current, config, currentLineLength, indentLevel, depth))
{
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,
const FormatterConfig* formatterConfig, int currentLineLength, int indentLevel,
const Config* config, int currentLineLength, int indentLevel,
int depth) pure @safe
{
import std.algorithm : sort, canFind;
@ -171,7 +171,7 @@ State[] validMoves(const Token[] tokens, immutable short[] depths, ref const Sta
breaks ~= current.breaks;
breaks ~= i;
sort(breaks);
states ~= State(breaks, tokens, depths, depth + 1, formatterConfig,
states ~= State(breaks, tokens, depths, depth + 1, config,
currentLineLength, indentLevel);
}
return states;