Editorconfig support
This commit is contained in:
parent
854569fded
commit
d0e255e62d
|
@ -1,12 +1,21 @@
|
|||
[*.d]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_size = 4
|
||||
tab_width = 8
|
||||
trim_trailing_whitespace = true
|
||||
indent_style = space
|
||||
|
||||
[*]
|
||||
trim_trailing_whitespace = true
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
[*.d]
|
||||
dfmt_align_switch_statements = true
|
||||
dfmt_brace_style = otbs
|
||||
dfmt_outdent_attributes = true
|
||||
dfmt_outdent_labels = true
|
||||
dfmt_soft_max_line_length = 80
|
||||
dfmt_space_after_cast = true
|
||||
dfmt_space_after_keywords = true
|
||||
dfmt_split_operator_at_line_end = false
|
||||
end_of_line = lf
|
||||
indent_size = 8
|
||||
indent_style = tab
|
||||
insert_final_newline = true
|
||||
max_line_length = 120
|
||||
tab_width = 8
|
||||
trim_trailing_whitespace = true
|
||||
|
|
26
README.md
26
README.md
|
@ -25,6 +25,32 @@ file instead, and output will be written to ```stdout```.
|
|||
placing opening braces on their own line. This is the default.
|
||||
* ```--tabs```: Use tabs for indentation instead of spaces.
|
||||
|
||||
## Configuration
|
||||
**dfmt** uses [EditorConfig](http://editorconfig.org/) configuration files.
|
||||
**dfmt**-specific properties are prefixed with *dfmt_*.
|
||||
### Standard EditorConfig properties
|
||||
Property Name | Allowed Values | Default Value | Description
|
||||
--------------|----------------|---------------|------------
|
||||
end_of_line | | TODO | Not yet supported
|
||||
insert_final_newline | | `true` | Not supported. **dfmt** always inserts a final newline
|
||||
charset | | `UTf-8` | Not supported. **dfmt** only works correctly on UTF-8.
|
||||
indent_style | `tab`, `space` | `space` | Supported
|
||||
indent_size | positive integers | `4` | Supported
|
||||
tab_width | positive integers | `8` | Supported
|
||||
trim_trailing_whitespace | | `true` | Not supported. **dfmt** does not emit trailing whitespace
|
||||
max_line_length | positive integers | `120` | Supported
|
||||
### dfmt-specific properties
|
||||
Property Name | Allowed Values | Default Value | Description
|
||||
--------------|----------------|---------------|------------
|
||||
dfmt_brace_style | `allman`, `otbs`, or `stroustrup` | https://en.wikipedia.org/wiki/Brace_style
|
||||
dfmt_soft_max_line_length | positive integers | The formatting process will usually keep lines below this length, but they may be up to max_line_length columns long.
|
||||
dfmt_outdent_labels (Not yet implemented) | `true`, `false` | Decrease the indentation of labels
|
||||
dfmt_align_switch_statements (Not yet implemented) | `true`, `false` | Align labels, cases, and defaults with their enclosing switch
|
||||
dfmt_outdent_attributes (Not yet implemented) | `true`, `false` | Decrease the indentation level of attributes
|
||||
dfmt_split_operator_at_line_end (Not yet implemented) | `true`, `false` | Place operators on the end of the previous line when splitting lines
|
||||
dfmt_space_after_cast (Not yet implemented) | `true`, `false` | Insert space after the closing paren of a `cast` expression
|
||||
dfmt_space_after_keywords (Not yet implemented) | `true`, `false` | Insert space after `if`, `while`, `foreach`, etc, and before the `(`
|
||||
|
||||
## Terminology
|
||||
* Braces - `{` and `}`
|
||||
* Brackets - `[` and `]`
|
||||
|
|
|
@ -5,9 +5,12 @@
|
|||
|
||||
module dfmt.config;
|
||||
|
||||
import dfmt.editorconfig;
|
||||
|
||||
/// Brace styles
|
||||
enum BraceStyle
|
||||
{
|
||||
unspecified,
|
||||
/// $(LINK https://en.wikipedia.org/wiki/Indent_style#Allman_style)
|
||||
allman,
|
||||
/// $(LINK https://en.wikipedia.org/wiki/Indent_style#Variant:_1TBS)
|
||||
|
@ -16,76 +19,42 @@ enum BraceStyle
|
|||
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 Config
|
||||
{
|
||||
///
|
||||
@Help("Number of spaces used for indentation")
|
||||
uint indentSize = 4;
|
||||
|
||||
OptionalBoolean dfmt_align_switch_statements = OptionalBoolean.t;
|
||||
///
|
||||
@Help("Use tabs or spaces")
|
||||
bool useTabs = false;
|
||||
|
||||
BraceStyle dfmt_brace_style = BraceStyle.allman;
|
||||
///
|
||||
@Help("Size of a tab character")
|
||||
uint tabSize = 4;
|
||||
|
||||
OptionalBoolean dfmt_outdent_attributes = OptionalBoolean.t;
|
||||
///
|
||||
@Help("Soft line wrap limit")
|
||||
uint columnSoftLimit = 80;
|
||||
|
||||
OptionalBoolean dfmt_outdent_labels = OptionalBoolean.t;
|
||||
///
|
||||
@Help("Hard line wrap limit")
|
||||
uint columnHardLimit = 120;
|
||||
|
||||
int dfmt_soft_max_line_length = 80;
|
||||
///
|
||||
@Help("Brace style can be 'otbs', 'allman', or 'stroustrup'")
|
||||
BraceStyle braceStyle = BraceStyle.allman;
|
||||
|
||||
OptionalBoolean dfmt_space_after_cast = OptionalBoolean.t;
|
||||
///
|
||||
@Help("Align labels, cases, and defaults with their enclosing switch")
|
||||
bool alignSwitchStatements = true;
|
||||
|
||||
OptionalBoolean dfmt_space_after_keywords = OptionalBoolean.t;
|
||||
///
|
||||
@Help("Decrease the indentation of labels")
|
||||
bool outdentLabels = true;
|
||||
OptionalBoolean dfmt_split_operator_at_line_end = OptionalBoolean.f;
|
||||
|
||||
///
|
||||
@Help("Decrease the indentation level of attributes")
|
||||
bool outdentAttributes = true;
|
||||
mixin StandardEditorConfigFields;
|
||||
|
||||
///
|
||||
@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;
|
||||
|
||||
///
|
||||
@Help("Insert spaces after 'if', 'while', 'foreach', etc, and before the '('")
|
||||
bool spaceAfterBlockKeywords;
|
||||
/**
|
||||
* Initializes the standard EditorConfig properties with default values that
|
||||
* make sense for D code.
|
||||
*/
|
||||
void initializeWithDefaults()
|
||||
{
|
||||
pattern = "*.d";
|
||||
end_of_line = EOL.lf;
|
||||
indent_style = IndentStyle.space;
|
||||
indent_size = 4;
|
||||
tab_width = 4;
|
||||
max_line_length = 120;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns:
|
||||
|
@ -95,7 +64,7 @@ struct Config
|
|||
{
|
||||
import std.stdio : stderr;
|
||||
|
||||
if (columnSoftLimit > columnHardLimit)
|
||||
if (dfmt_soft_max_line_length > max_line_length)
|
||||
{
|
||||
stderr.writeln("Column hard limit must be greater than or equal to column soft limit");
|
||||
return false;
|
||||
|
@ -103,30 +72,3 @@ struct Config
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads arguments from a file at the given path into the given string array
|
||||
*/
|
||||
void readConfig(string path, ref string[] args)
|
||||
{
|
||||
import std.stdio : File;
|
||||
import std.file : exists;
|
||||
import std.array : empty, RefAppender;
|
||||
|
||||
if (!exists(path))
|
||||
return;
|
||||
auto f = File(path);
|
||||
|
||||
auto app = RefAppender!(string[])(&args);
|
||||
|
||||
import std.algorithm : map, copy, sort, uniq, filter;
|
||||
|
||||
foreach (a; f.byLine().filter!(a => !a.empty).map!(a => a.idup))
|
||||
app.put(a);
|
||||
app.data[1 .. $].sort();
|
||||
}
|
||||
|
||||
private struct Help
|
||||
{
|
||||
string text;
|
||||
}
|
||||
|
|
|
@ -7,63 +7,63 @@ private auto commentRe = ctRegex!(`^\s*[#;].*$`);
|
|||
|
||||
enum OptionalBoolean : ubyte
|
||||
{
|
||||
unspecified = 3,
|
||||
t = 1,
|
||||
f = 0
|
||||
unspecified = 3,
|
||||
t = 1,
|
||||
f = 0
|
||||
}
|
||||
|
||||
enum IndentStyle : ubyte
|
||||
{
|
||||
unspecified,
|
||||
tab,
|
||||
space
|
||||
unspecified,
|
||||
tab,
|
||||
space
|
||||
}
|
||||
|
||||
enum EOL : ubyte
|
||||
{
|
||||
unspecified,
|
||||
lf,
|
||||
cr,
|
||||
crlf
|
||||
unspecified,
|
||||
lf,
|
||||
cr,
|
||||
crlf
|
||||
}
|
||||
|
||||
mixin template StandardEditorConfigFields()
|
||||
{
|
||||
string pattern;
|
||||
OptionalBoolean root;
|
||||
EOL end_of_line;
|
||||
OptionalBoolean insert_final_newline;
|
||||
string charset;
|
||||
IndentStyle indent_style;
|
||||
int indent_size = -1;
|
||||
int tab_width = -1;
|
||||
OptionalBoolean trim_trailing_whitespace;
|
||||
int max_line_length = -1;
|
||||
string pattern;
|
||||
OptionalBoolean root;
|
||||
EOL end_of_line;
|
||||
OptionalBoolean insert_final_newline;
|
||||
string charset;
|
||||
IndentStyle indent_style;
|
||||
int indent_size = -1;
|
||||
int tab_width = -1;
|
||||
OptionalBoolean trim_trailing_whitespace;
|
||||
int max_line_length = -1;
|
||||
|
||||
void merge(ref const typeof(this) other, const string fileName)
|
||||
{
|
||||
import std.path : globMatch;
|
||||
import std.traits : FieldNameTuple;
|
||||
void merge(ref const typeof(this) other, const string fileName)
|
||||
{
|
||||
import std.path : globMatch;
|
||||
import std.traits : FieldNameTuple;
|
||||
|
||||
if (other.pattern is null || !fileName.globMatch(other.pattern))
|
||||
return;
|
||||
foreach (N; FieldNameTuple!(typeof(this)))
|
||||
{
|
||||
alias T = typeof(mixin(N));
|
||||
const otherN = mixin("other." ~ N);
|
||||
auto thisN = &mixin("this." ~ N);
|
||||
static if (N == "pattern")
|
||||
continue;
|
||||
else static if (is (T == enum))
|
||||
*thisN = otherN != T.unspecified ? otherN : *thisN;
|
||||
else static if (is (T == int))
|
||||
*thisN = otherN != -1 ? otherN : *thisN;
|
||||
else static if (is (T == string))
|
||||
*thisN = otherN !is null ? otherN : *thisN;
|
||||
else
|
||||
static assert(false);
|
||||
}
|
||||
}
|
||||
if (other.pattern is null || !fileName.globMatch(other.pattern))
|
||||
return;
|
||||
foreach (N; FieldNameTuple!(typeof(this)))
|
||||
{
|
||||
alias T = typeof(mixin(N));
|
||||
const otherN = mixin("other." ~ N);
|
||||
auto thisN = &mixin("this." ~ N);
|
||||
static if (N == "pattern")
|
||||
continue;
|
||||
else static if (is (T == enum))
|
||||
*thisN = otherN != T.unspecified ? otherN : *thisN;
|
||||
else static if (is (T == int))
|
||||
*thisN = otherN != -1 ? otherN : *thisN;
|
||||
else static if (is (T == string))
|
||||
*thisN = otherN !is null ? otherN : *thisN;
|
||||
else
|
||||
static assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -74,94 +74,84 @@ mixin template StandardEditorConfigFields()
|
|||
*/
|
||||
EC getConfigFor(EC)(string path)
|
||||
{
|
||||
import std.stdio : File;
|
||||
import std.regex : regex, match;
|
||||
import std.path : globMatch, dirName, baseName, pathSplitter, buildPath;
|
||||
import std.algorithm : reverse, map, filter, each;
|
||||
import std.array : array;
|
||||
import std.stdio : File;
|
||||
import std.regex : regex, match;
|
||||
import std.path : globMatch, dirName, baseName, pathSplitter, buildPath;
|
||||
import std.algorithm : reverse, map, filter, each;
|
||||
import std.array : array;
|
||||
|
||||
EC result;
|
||||
EC[][] configs;
|
||||
string dir = dirName(path);
|
||||
immutable string fileName = baseName(path);
|
||||
string[] pathParts = cast(string[]) pathSplitter(dir).array();
|
||||
for (size_t i = pathParts.length; i > 1; i--)
|
||||
{
|
||||
EC[] sections = parseConfig!EC(buildPath(pathParts[0 .. i]));
|
||||
if (sections.length)
|
||||
configs ~= sections;
|
||||
if (!sections.map!(a => a.root).filter!(a => a == OptionalBoolean.t).empty)
|
||||
break;
|
||||
}
|
||||
reverse(configs);
|
||||
configs.each!(a => a.each!(b => result.merge(b, fileName)))();
|
||||
return result;
|
||||
EC result;
|
||||
EC[][] configs;
|
||||
string dir = dirName(path);
|
||||
immutable string fileName = baseName(path);
|
||||
string[] pathParts = cast(string[]) pathSplitter(dir).array();
|
||||
for (size_t i = pathParts.length; i > 1; i--)
|
||||
{
|
||||
EC[] sections = parseConfig!EC(buildPath(pathParts[0 .. i]));
|
||||
if (sections.length)
|
||||
configs ~= sections;
|
||||
if (!sections.map!(a => a.root).filter!(a => a == OptionalBoolean.t).empty)
|
||||
break;
|
||||
}
|
||||
reverse(configs);
|
||||
configs.each!(a => a.each!(b => result.merge(b, fileName)))();
|
||||
return result;
|
||||
}
|
||||
|
||||
private EC[] parseConfig(EC)(string dir)
|
||||
{
|
||||
import std.stdio : File;
|
||||
import std.file : exists;
|
||||
import std.path : buildPath;
|
||||
import std.regex : matchAll;
|
||||
import std.traits : FieldNameTuple;
|
||||
import std.conv : to;
|
||||
import std.uni : toLower;
|
||||
import std.stdio : File;
|
||||
import std.file : exists;
|
||||
import std.path : buildPath;
|
||||
import std.regex : matchAll;
|
||||
import std.traits : FieldNameTuple;
|
||||
import std.conv : to;
|
||||
import std.uni : toLower;
|
||||
|
||||
EC section;
|
||||
EC[] sections;
|
||||
immutable string path = buildPath(dir, ".editorconfig");
|
||||
if (!exists(path))
|
||||
return sections;
|
||||
EC section;
|
||||
EC[] sections;
|
||||
immutable string path = buildPath(dir, ".editorconfig");
|
||||
if (!exists(path))
|
||||
return sections;
|
||||
|
||||
File f = File(path);
|
||||
foreach (line; f.byLineCopy())
|
||||
{
|
||||
auto headerMatch = line.matchAll(headerRe);
|
||||
if (headerMatch)
|
||||
{
|
||||
sections ~= section;
|
||||
section = EC.init;
|
||||
auto c = headerMatch.captures;
|
||||
c.popFront();
|
||||
section.pattern = c.front();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto propertyMatch = line.matchAll(propertyRe);
|
||||
if (propertyMatch)
|
||||
{
|
||||
auto c = propertyMatch.captures;
|
||||
c.popFront();
|
||||
immutable string propertyName = c.front();
|
||||
c.popFront();
|
||||
immutable string propertyValue = toLower(c.front());
|
||||
foreach (F; FieldNameTuple!EC)
|
||||
{
|
||||
enum configDot = "section." ~ F;
|
||||
alias FieldType = typeof(mixin(configDot));
|
||||
if (F == propertyName)
|
||||
{
|
||||
static if (is(FieldType == OptionalBoolean))
|
||||
mixin(configDot) = propertyValue == "true" ? OptionalBoolean.t
|
||||
: OptionalBoolean.f;
|
||||
else
|
||||
mixin(configDot) = to!(FieldType)(propertyValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sections ~= section;
|
||||
return sections;
|
||||
}
|
||||
|
||||
version (editorconfig_main) void main()
|
||||
{
|
||||
import std.stdio : writeln;
|
||||
|
||||
static struct EditorConfig { mixin StandardEditorConfigFields; }
|
||||
|
||||
auto c = getConfigFor!EditorConfig("/home/brian/src/aias/src/dummy.d");
|
||||
writeln(c);
|
||||
File f = File(path);
|
||||
foreach (line; f.byLineCopy())
|
||||
{
|
||||
auto headerMatch = line.matchAll(headerRe);
|
||||
if (headerMatch)
|
||||
{
|
||||
sections ~= section;
|
||||
section = EC.init;
|
||||
auto c = headerMatch.captures;
|
||||
c.popFront();
|
||||
section.pattern = c.front();
|
||||
}
|
||||
else
|
||||
{
|
||||
auto propertyMatch = line.matchAll(propertyRe);
|
||||
if (propertyMatch)
|
||||
{
|
||||
auto c = propertyMatch.captures;
|
||||
c.popFront();
|
||||
immutable string propertyName = c.front();
|
||||
c.popFront();
|
||||
immutable string propertyValue = toLower(c.front());
|
||||
foreach (F; FieldNameTuple!EC)
|
||||
{
|
||||
enum configDot = "section." ~ F;
|
||||
alias FieldType = typeof(mixin(configDot));
|
||||
if (F == propertyName)
|
||||
{
|
||||
static if (is(FieldType == OptionalBoolean))
|
||||
mixin(configDot) = propertyValue == "true" ? OptionalBoolean.t
|
||||
: OptionalBoolean.f;
|
||||
else
|
||||
mixin(configDot) = to!(FieldType)(propertyValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sections ~= section;
|
||||
return sections;
|
||||
}
|
||||
|
|
|
@ -321,7 +321,7 @@ private:
|
|||
}
|
||||
assert(lengthOfNextChunk > 0);
|
||||
writeToken();
|
||||
if (currentLineLength + 1 + lengthOfNextChunk >= config.columnSoftLimit)
|
||||
if (currentLineLength + 1 + lengthOfNextChunk >= config.dfmt_soft_max_line_length)
|
||||
{
|
||||
pushWrapIndent(tok!",");
|
||||
newline();
|
||||
|
@ -359,7 +359,7 @@ private:
|
|||
}
|
||||
else if (!currentIs(tok!")") && !currentIs(tok!"]")
|
||||
&& (linebreakHints.canFindIndex(index - 1) || (linebreakHints.length == 0
|
||||
&& currentLineLength > config.columnHardLimit)))
|
||||
&& currentLineLength > config.max_line_length)))
|
||||
{
|
||||
pushWrapIndent(p);
|
||||
newline();
|
||||
|
@ -455,7 +455,7 @@ private:
|
|||
{
|
||||
if ((parenDepth > 0 && sBraceDepth == 0) || (sBraceDepth > 0 && niBraceDepth > 0))
|
||||
{
|
||||
if (currentLineLength > config.columnSoftLimit)
|
||||
if (currentLineLength > config.dfmt_soft_max_line_length)
|
||||
{
|
||||
writeToken();
|
||||
pushWrapIndent(tok!";");
|
||||
|
@ -488,7 +488,7 @@ private:
|
|||
auto e = expressionEndIndex(index);
|
||||
immutable int l = currentLineLength + tokens[index .. e].map!(a => tokenLength(a)).sum();
|
||||
writeToken();
|
||||
if (l > config.columnSoftLimit)
|
||||
if (l > config.dfmt_soft_max_line_length)
|
||||
{
|
||||
indents.push(tok!"{");
|
||||
newline();
|
||||
|
@ -504,7 +504,7 @@ private:
|
|||
auto e = expressionEndIndex(index);
|
||||
immutable int l = currentLineLength + tokens[index .. e].map!(a => tokenLength(a)).sum();
|
||||
writeToken();
|
||||
if (l > config.columnSoftLimit)
|
||||
if (l > config.dfmt_soft_max_line_length)
|
||||
{
|
||||
indents.push(tok!"{");
|
||||
newline();
|
||||
|
@ -520,7 +520,7 @@ private:
|
|||
if (!justAddedExtraNewline && !peekBackIsOneOf(false, tok!"{",
|
||||
tok!"}", tok!";", tok!";"))
|
||||
{
|
||||
if (config.braceStyle != BraceStyle.allman)
|
||||
if (config.dfmt_brace_style != BraceStyle.allman)
|
||||
{
|
||||
if (!astInformation.structInitStartLocations.canFindIndex(tokens[index].index)
|
||||
&& !astInformation.funLitStartLocations.canFindIndex(
|
||||
|
@ -583,7 +583,7 @@ private:
|
|||
currentLineLength = 0;
|
||||
justAddedExtraNewline = true;
|
||||
}
|
||||
if (config.braceStyle == BraceStyle.otbs && currentIs(tok!"else"))
|
||||
if (config.dfmt_brace_style == BraceStyle.otbs && currentIs(tok!"else"))
|
||||
write(" ");
|
||||
if (!peekIs(tok!",") && !peekIs(tok!")") && !peekIs(tok!";"))
|
||||
{
|
||||
|
@ -788,7 +788,7 @@ private:
|
|||
break;
|
||||
case tok!".":
|
||||
if (linebreakHints.canFind(index) || (linebreakHints.length == 0
|
||||
&& currentLineLength + nextTokenLength() > config.columnHardLimit))
|
||||
&& currentLineLength + nextTokenLength() > config.max_line_length))
|
||||
{
|
||||
pushWrapIndent();
|
||||
newline();
|
||||
|
@ -869,7 +869,7 @@ private:
|
|||
newline();
|
||||
}
|
||||
else if (!peekIs(tok!"}") && (linebreakHints.canFind(index)
|
||||
|| (linebreakHints.length == 0 && currentLineLength > config.columnSoftLimit)))
|
||||
|| (linebreakHints.length == 0 && currentLineLength > config.dfmt_soft_max_line_length)))
|
||||
{
|
||||
writeToken();
|
||||
pushWrapIndent(tok!",");
|
||||
|
@ -1072,15 +1072,16 @@ private:
|
|||
|
||||
void indent()
|
||||
{
|
||||
if (config.useTabs)
|
||||
import dfmt.editorconfig : IndentStyle;
|
||||
if (config.indent_style == IndentStyle.tab)
|
||||
foreach (i; 0 .. indentLevel)
|
||||
{
|
||||
currentLineLength += config.tabSize;
|
||||
currentLineLength += config.tab_width;
|
||||
output.put("\t");
|
||||
}
|
||||
else
|
||||
foreach (i; 0 .. indentLevel)
|
||||
foreach (j; 0 .. config.indentSize)
|
||||
foreach (j; 0 .. config.indent_size)
|
||||
{
|
||||
output.put(" ");
|
||||
currentLineLength++;
|
||||
|
|
155
src/dfmt/main.d
155
src/dfmt/main.d
|
@ -12,93 +12,57 @@ else
|
|||
{
|
||||
import std.array : front, popFront;
|
||||
import std.stdio : stdout, stdin, stderr, writeln, File;
|
||||
import dfmt.config : Config, getHelp, readConfig;
|
||||
import dfmt.config : Config;
|
||||
import dfmt.formatter : format;
|
||||
import std.path : buildPath, expandTilde;
|
||||
import dfmt.editorconfig : getConfigFor;
|
||||
import std.getopt : getopt;
|
||||
|
||||
int main(string[] args)
|
||||
{
|
||||
bool inplace = false;
|
||||
Config optConfig;
|
||||
optConfig.pattern = "*.d";
|
||||
bool showHelp;
|
||||
getopt(args,
|
||||
"align_switch_statements", &optConfig.dfmt_align_switch_statements,
|
||||
"brace_style", &optConfig.dfmt_brace_style,
|
||||
"end_of_line", &optConfig.end_of_line,
|
||||
"help|h", &showHelp,
|
||||
"indent_size", &optConfig.indent_size,
|
||||
"indent_style|t", &optConfig.indent_style,
|
||||
"inplace", &inplace,
|
||||
"max_line_length", &optConfig.max_line_length,
|
||||
"max_line_length", &optConfig.max_line_length,
|
||||
"outdent_attributes", &optConfig.dfmt_outdent_attributes,
|
||||
"outdent_labels", &optConfig.dfmt_outdent_labels,
|
||||
"space_after_cast", &optConfig.dfmt_space_after_cast,
|
||||
"split_operator_at_line_end", &optConfig.dfmt_split_operator_at_line_end,
|
||||
"tab_width", &optConfig.tab_width);
|
||||
|
||||
if (showHelp)
|
||||
{
|
||||
printHelp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
args.popFront();
|
||||
immutable bool readFromStdin = args.length == 0;
|
||||
immutable string filePath = createFilePath(readFromStdin, readFromStdin ? null : args[0]);
|
||||
Config config;
|
||||
string configPath = expandTilde("~/.config/dfmt/dfmtrc");
|
||||
readConfig(configPath, args);
|
||||
|
||||
static if (__VERSION__ >= 2067)
|
||||
{
|
||||
import std.getopt : getopt, defaultGetoptPrinter;
|
||||
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,
|
||||
"spaceAfterBlockKeywords", getHelp!(Config.spaceAfterBlockKeywords), &config.spaceAfterBlockKeywords);
|
||||
|
||||
if (getOptResult.helpWanted)
|
||||
{
|
||||
defaultGetoptPrinter("dfmt 0.3.0-dev\n\nOptions:", getOptResult.options);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
import std.getopt : getopt;
|
||||
bool showHelp;
|
||||
getopt(args,
|
||||
"help|h", &showHelp,
|
||||
"inplace", &inplace,
|
||||
"tabs|t", &config.useTabs,
|
||||
"braces", &config.braceStyle,
|
||||
"colSoft", &config.columnSoftLimit,
|
||||
"colHard", &config.columnHardLimit,
|
||||
"tabSize", &config.tabSize,
|
||||
"indentSize", &config.indentSize,
|
||||
"alignSwitchCases", &config.alignSwitchStatements,
|
||||
"outdentLabels", &config.outdentLabels,
|
||||
"outdentAttributes", &config.outdentAttributes,
|
||||
"splitOperatorAtEnd", &config.splitOperatorAtEnd,
|
||||
"spaceAfterCast", &config.spaceAfterCast,
|
||||
"newlineType", &config.newlineType);
|
||||
if (showHelp)
|
||||
{
|
||||
writeln(`dfmt 0.3.0-dev
|
||||
|
||||
Options:
|
||||
--help | -h Print this help message
|
||||
--inplace Edit files in place
|
||||
--tabs | -t Use tabs instead of spaces
|
||||
--braces Brace style can be 'otbs', 'allman', or 'stroustrup'
|
||||
--colSoft Column soft limit
|
||||
--colHard Column hard limit
|
||||
--tabSize Size of tabs
|
||||
--indentSize Number of spaces used for indentation
|
||||
--alignSwitchCases Align cases, defaults, and labels with enclosing
|
||||
switches
|
||||
--outdentLabels Outdent labels
|
||||
--outdentAttributes Outdent attribute declarations
|
||||
--splitOperatorAtEnd Place operators at the end of the previous line when
|
||||
wrapping
|
||||
--spaceAfterCast Insert spaces after cast expressions
|
||||
--newlineType Newline type can be 'cr', 'lf', or 'crlf'`);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
config.initializeWithDefaults();
|
||||
Config fileConfig = getConfigFor!Config(filePath);
|
||||
fileConfig.pattern = "*.d";
|
||||
config.merge(fileConfig, filePath);
|
||||
config.merge(optConfig, filePath);
|
||||
|
||||
if (!config.isValid())
|
||||
return 1;
|
||||
|
||||
File output = stdout;
|
||||
ubyte[] buffer;
|
||||
args.popFront();
|
||||
if (args.length == 0)
|
||||
|
||||
if (readFromStdin)
|
||||
{
|
||||
ubyte[4096] inputBuffer;
|
||||
ubyte[] b;
|
||||
|
@ -142,3 +106,46 @@ Options:
|
|||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void printHelp()
|
||||
{
|
||||
writeln(`dfmt 0.3.0-dev
|
||||
|
||||
Options:
|
||||
--help | -h Print this help message
|
||||
--inplace Edit files in place
|
||||
|
||||
Formatting Options:
|
||||
--align_switch_statements
|
||||
--brace_style
|
||||
--end_of_line
|
||||
--help|h
|
||||
--indent_size
|
||||
--indent_style|t
|
||||
--inplace
|
||||
--max_line_length
|
||||
--max_line_length
|
||||
--outdent_attributes
|
||||
--outdent_labels
|
||||
--space_after_cast
|
||||
--split_operator_at_line_end`);
|
||||
}
|
||||
|
||||
private string createFilePath(bool readFromStdin, string fileName)
|
||||
//out (result)
|
||||
//{
|
||||
// stderr.writeln(__FUNCTION__, ": ", result);
|
||||
//}
|
||||
//body
|
||||
{
|
||||
import std.file : getcwd;
|
||||
import std.path : isRooted;
|
||||
|
||||
immutable string cwd = getcwd();
|
||||
if (readFromStdin)
|
||||
return buildPath(cwd, "dummy.d");
|
||||
if (isRooted(fileName))
|
||||
return fileName;
|
||||
else
|
||||
return buildPath(cwd, fileName);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ struct State
|
|||
import core.bitop : popcnt, bsf;
|
||||
import std.algorithm : min, map, sum;
|
||||
|
||||
immutable int remainingCharsMultiplier = config.columnHardLimit - config.columnSoftLimit;
|
||||
immutable int remainingCharsMultiplier = config.max_line_length - config.dfmt_soft_max_line_length;
|
||||
immutable int newlinePenalty = remainingCharsMultiplier * 20;
|
||||
|
||||
this.breaks = breaks;
|
||||
|
@ -29,9 +29,9 @@ struct State
|
|||
if (breaks == 0)
|
||||
{
|
||||
immutable int l = currentLineLength + tokens.map!(a => tokenLength(a)).sum();
|
||||
if (l > config.columnSoftLimit)
|
||||
if (l > config.dfmt_soft_max_line_length)
|
||||
{
|
||||
immutable int longPenalty = (l - config.columnSoftLimit) * remainingCharsMultiplier;
|
||||
immutable int longPenalty = (l - config.dfmt_soft_max_line_length) * remainingCharsMultiplier;
|
||||
this._cost += longPenalty;
|
||||
this._solved = longPenalty < newlinePenalty;
|
||||
}
|
||||
|
@ -58,18 +58,18 @@ struct State
|
|||
immutable uint bits = b ? 0 : bsf(k);
|
||||
immutable size_t j = min(i + bits + 1, tokens.length);
|
||||
ll += tokens[i .. j].map!(a => tokenLength(a)).sum();
|
||||
if (ll > config.columnSoftLimit)
|
||||
if (ll > config.dfmt_soft_max_line_length)
|
||||
{
|
||||
immutable int longPenalty = (ll - config.columnSoftLimit) * remainingCharsMultiplier;
|
||||
immutable int longPenalty = (ll - config.dfmt_soft_max_line_length) * remainingCharsMultiplier;
|
||||
this._cost += longPenalty;
|
||||
}
|
||||
if (ll > config.columnHardLimit)
|
||||
if (ll > config.max_line_length)
|
||||
{
|
||||
this._solved = false;
|
||||
break;
|
||||
}
|
||||
i = j;
|
||||
ll = indentLevel * config.indentSize;
|
||||
ll = indentLevel * config.indent_size;
|
||||
if (b)
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ do
|
|||
for source in *.d
|
||||
do
|
||||
echo "${source}.ref" "${braceStyle}/${source}.out"
|
||||
../bin/dfmt --braces=${braceStyle} "${source}" > "${braceStyle}/${source}.out"
|
||||
../bin/dfmt --brace_style=${braceStyle} "${source}" > "${braceStyle}/${source}.out"
|
||||
diff -u "${braceStyle}/${source}.ref" "${braceStyle}/${source}.out"
|
||||
done
|
||||
done
|
||||
|
|
Loading…
Reference in New Issue