commit 2bc89bfbd6b51eee6d3dc46503c8e760fae4fcd8
parent 1b3644464b3d28b7863d846e279c47328b5f02aa
Author: Friedel Schön <[email protected]>
Date: Sat, 23 Dec 2023 21:33:20 +0100
fix merge-duplicates
Diffstat:
M | src/main.d | | | 2 | ++ |
M | src/sort.d | | | 92 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------ |
2 files changed, 73 insertions(+), 21 deletions(-)
diff --git a/src/main.d b/src/main.d
@@ -97,10 +97,12 @@ mixin CLI!(SortConfig).main!((SortConfig config) {
stderr.writeln("error: cannot use '--recursive' and specify no input");
exit(1);
}
+
if (config.inplace && config.inputs.empty) {
stderr.writeln("error: cannot use inplace and read from stdin");
exit(2);
}
+
if (!config.inputs.empty && (!config.inplace || !config.output.empty)) {
stderr.writeln(
"error: if you use inputs you must use inplace sorting or provide an output");
diff --git a/src/sort.d b/src/sort.d
@@ -3,27 +3,46 @@
module importsort.sort;
import importsort.main : SortConfig;
-import std.algorithm : findSplit, remove, sort;
+import std.algorithm : canFind, findSplit, remove, sort;
import std.algorithm.comparison : equal;
+import std.algorithm.searching : all;
import std.array : split;
import std.conv : to;
import std.file : DirEntry, rename;
import std.functional : unaryFun;
-import std.range : ElementType;
+import std.range : ElementType, empty;
import std.regex : ctRegex, matchFirst;
import std.stdio : File, stderr;
import std.string : strip, stripLeft;
import std.traits : isIterable;
import std.typecons : Nullable, Yes, nullable;
-import std.uni : asLowerCase;
+import std.uni : asLowerCase, isSpace, isWhite;
/// the pattern to determinate a line is an import or not
-enum PATTERN = ctRegex!`^(\s*)(?:(public|static)\s+)?import\s+(?:(\w+)\s*=\s*)?([a-zA-Z._]+)\s*(:\s*\w+(?:\s*=\s*\w+)?(?:\s*,\s*\w+(?:\s*=\s*\w+)?)*)?\s*;[ \t]*([\n\r]*)$`;
+enum PATTERN = ctRegex!`(?:(public|static)\s+)?import\s+(?:(\w+)\s*=\s*)?([a-zA-Z._]+)\s*(:\s*\w+(?:\s*=\s*\w+)?(?:\s*,\s*\w+(?:\s*=\s*\w+)?)*)?;`;
bool iterableOf(T, E)() {
return isIterable!T && is(ElementType!T == E);
}
+T[] uniq(T)(in T[] s) {
+ T[] result;
+ foreach (T c; s)
+ if (!result.canFind(c))
+ result ~= c;
+ return result;
+}
+
+string getLineEnding(string str) {
+ string ending = "";
+ foreach_reverse (chr; str) {
+ if (chr != '\n' && chr != '\r')
+ break;
+ ending = chr ~ ending;
+ }
+ return ending;
+}
+
/// helper-struct for identifiers and its bindings
struct Identifier {
/// SortConfig::byBinding
@@ -63,10 +82,10 @@ struct Import {
/// is a static-import
bool static_;
- /// origin of the import e. g. `import std.stdio : ...;`
+ /// origin of the import e. g. `import std.stdio : ...`
Identifier name;
- /// symbols of the import e. g. `import ... : File, stderr, in = stdin;`
+ /// symbols of the import e. g. `import ... : File, stderr, in = stdin`
Identifier[] idents;
/// spaces before the import (indentation)
@@ -87,7 +106,7 @@ bool less(SortConfig config, string a, string b) {
return config.ignoreCase ? a.asLowerCase.to!string < b.asLowerCase.to!string : a < b;
}
-void sortMatches(SortConfig config, Import[] matches) {
+Import[] sortMatches(SortConfig config, Import[] matches) {
if (config.merge) {
for (int i = 0; i < matches.length; i++) {
for (int j = i + 1; j < matches.length; j++) {
@@ -101,12 +120,17 @@ void sortMatches(SortConfig config, Import[] matches) {
}
}
}
+
+ foreach (ref match; matches)
+ match.idents = uniq(match.idents);
}
matches.sort!((a, b) => less(config, a.sortBy, b.sortBy));
foreach (m; matches)
m.idents.sort!((a, b) => less(config, a.sortBy, b.sortBy));
+
+ return matches;
}
bool checkChanged(SortConfig config, Import[] matches) {
@@ -115,7 +139,7 @@ bool checkChanged(SortConfig config, Import[] matches) {
auto original = matches.dup;
- sortMatches(config, matches);
+ matches = sortMatches(config, matches);
return !equal(original, matches);
}
@@ -125,7 +149,7 @@ void writeImports(File outfile, SortConfig config, Import[] matches) {
if (!matches)
return;
- sortMatches(config, matches);
+ matches = sortMatches(config, matches);
bool first;
@@ -189,6 +213,8 @@ bool sortImports(SortConfig config, File infile, Nullable!File outfile) {
foreach (line; infile.byLine(Yes.keepTerminator)) {
auto linestr = line.idup;
+
+ parse_match:
if (auto match = linestr.matchFirst(PATTERN)) { // is import
if (softEnd) {
if (!matches && !outfile.isNull)
@@ -197,21 +223,38 @@ bool sortImports(SortConfig config, File infile, Nullable!File outfile) {
}
auto im = Import(config.byAttribute, linestr);
- if (match[3]) {
- im.name = Identifier(config.byBinding, match[4], match[3]);
+
+ if (!match.pre.all!isSpace) {
+ if (matches) {
+ im.begin = matches[$ - 1].begin;
+
+ if (!outfile.isNull)
+ outfile.get().writeImports(config, matches);
+ else if (checkChanged(config, matches))
+ return true;
+
+ matches = [];
+ }
+
+ if (!outfile.isNull)
+ outfile.get().writeln(line);
} else {
- im.name = Identifier(config.byBinding, match[4]);
+ im.begin = match.pre;
}
- im.begin = match[1];
- im.end = match[6];
- if (match[2] == "static")
+ if (match[2]) {
+ im.name = Identifier(config.byBinding, match[3], match[2]);
+ } else {
+ im.name = Identifier(config.byBinding, match[3]);
+ }
+
+ if (match[1] == "static")
im.static_ = true;
- else if (match[2] == "public")
+ else if (match[1] == "public")
im.public_ = true;
- if (match[5]) {
- foreach (id; match[5][1 .. $].split(",")) {
+ if (match[4]) {
+ foreach (id; match[4][1 .. $].split(",")) {
if (auto pair = id.findSplit("=")) { // has alias
im.idents ~= Identifier(config.byBinding, pair[2].strip, pair[0].strip);
} else {
@@ -219,9 +262,16 @@ bool sortImports(SortConfig config, File infile, Nullable!File outfile) {
}
}
}
+
+ im.end = linestr.getLineEnding;
matches ~= im;
+
+ if (!match.post.all!isWhite) {
+ linestr = match.post.idup;
+ goto parse_match;
+ }
} else {
- if (!softEnd && linestr.stripLeft == "") {
+ if (!softEnd && linestr.all!isSpace) {
softEnd = linestr;
} else {
if (matches) {
@@ -247,8 +297,8 @@ bool sortImports(SortConfig config, File infile, Nullable!File outfile) {
if (!outfile.isNull)
outfile.get().writeImports(config, matches);
- else if (checkChanged(config, matches))
- return true;
+ else
+ return checkChanged(config, matches);
return false;
}