importsort-d

Sort and format imports in DLang
Log | Files | Refs | README

commit 38dada38189e29ded7fa8b301a26b026aa68aed9
parent 9acb077b41c59053143f0ca2394249fbfb678303
Author: Friedel Schoen <[email protected]>
Date:   Sat, 22 Oct 2022 23:23:23 +0200

some refactoring

Diffstat:
Msrc/main.d | 172+++----------------------------------------------------------------------------
Asrc/sort.d | 168+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 173 insertions(+), 167 deletions(-)

diff --git a/src/main.d b/src/main.d @@ -1,59 +1,15 @@ // (c) 2022 Friedel Schon <[email protected]> -module test.main; +module importsort.main; import core.stdc.stdlib : exit; -import core.thread : Thread; -import core.time : Duration = dur; -import std.algorithm : canFind, each, endsWith, filter, findSplit, map, sort; -import std.array : array, replace; +import importsort.sort : Import, SortConfig, sortImports; +import std.array : replace; import std.conv : ConvException, parse; -import std.datetime : SysTime; -import std.file : DirEntry, SpanMode, dirEntries, exists, isDir, isFile, rename, timeLastModified; +import std.file : DirEntry, SpanMode, dirEntries, exists, isDir, isFile; import std.functional : unaryFun; -import std.range : ElementType, empty; -import std.regex : ctRegex, matchFirst; import std.stdio : File, stderr, stdin, stdout; -import std.string : format, indexOf, split, strip, stripLeft; -import std.traits : isIterable; -import std.typecons : Tuple, Yes, tuple; - -struct Identifier { - bool byBinding; - string original; - string binding; - - string sortBy() { - if (byBinding) - return hasBinding ? binding : original; - else - return original; - } - - bool hasBinding() { - return binding != null; - } -} - -struct Import { - bool byAttribute; - string line; - - bool public_; - bool static_; - Identifier name; - Identifier[] idents; - string begin; - string end; - - string sortBy() { - if (byAttribute && (public_ || static_)) - return '\0' ~ name.sortBy; - return name.sortBy; - } -} - -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]*)$`; +import std.string : endsWith; enum BINARY = "importsort-d"; enum VERSION = "0.1.0"; @@ -61,124 +17,6 @@ enum HELP = import("help.txt") .replace("{binary}", BINARY) .replace("{version}", VERSION); -struct SortConfig { - bool keepLine = false; - bool byAttribute = false; - bool byBinding = false; - bool verbose = false; -} - -void writeImports(File outfile, SortConfig config, Import[] matches) { - if (!matches) - return; - - matches.sort!((a, b) => a.sortBy < b.sortBy); - foreach (m; matches) { - if (config.keepLine) { - outfile.write(m.line); - } else { - outfile.write(m.begin); - if (m.public_) - outfile.write("public "); - if (m.static_) - outfile.write("static "); - if (m.name.hasBinding) { - outfile.writef("import %s = %s", m.name.binding, m.name.original); - } else { - outfile.write("import " ~ m.name.original); - } - foreach (i, ident; m.idents) { - auto begin = i == 0 ? " : " : ", "; - if (ident.hasBinding) { // hasBinding - outfile.writef("%s%s = %s", begin, ident.binding, ident.original); - } else { - outfile.write(begin ~ ident.original); - } - } - outfile.writef(";%s", m.end); - } - } -} - -void sortImports(alias P = "true", R)(R entries, SortConfig config) - if (isIterable!R && is(ElementType!R == DirEntry)) { - alias postFunc = unaryFun!P; - - File infile, outfile; - foreach (entry; entries) { - stderr.writef("\033[34msorting \033[0;1m%s\033[0m\n", entry.name); - - infile = File(entry.name); - outfile = File(entry.name ~ ".new", "w"); - - sortImports(infile, outfile, config); - - infile.close(); - outfile.close(); - - rename(entry.name ~ ".new", entry.name); - - cast(void) postFunc(entry.name); - } -} - -void sortImports(File infile, File outfile, SortConfig config) { - string softEnd = null; - Import[] matches; - - foreach (line; infile.byLine(Yes.keepTerminator)) { - auto linestr = line.idup; - if (auto match = linestr.matchFirst(PATTERN)) { // is import - if (softEnd) { - if (!matches) - outfile.write(softEnd); - softEnd = null; - } - - auto im = Import(config.byAttribute, linestr); - if (match[3]) { - im.name = Identifier(config.byBinding, match[4], match[3]); - } else { - im.name = Identifier(config.byBinding, match[4]); - } - im.begin = match[1]; - im.end = match[6]; - - if (match[2] == "static") - im.static_ = true; - else if (match[2] == "public") - im.public_ = true; - - if (match[5]) { - foreach (id; match[5][1 .. $].split(",")) { - if (auto pair = id.findSplit("=")) { // has alias - im.idents ~= Identifier(config.byBinding, pair[2].strip, pair[0].strip); - } else { - im.idents ~= Identifier(config.byBinding, id.strip); - } - } - im.idents.sort!((a, b) => a.sortBy < b.sortBy); - } - matches ~= im; - } else { - if (!softEnd && linestr.stripLeft == "") { - softEnd = linestr; - } else { - if (matches) { - outfile.writeImports(config, matches); - matches = []; - } - if (softEnd) { - outfile.write(softEnd); - softEnd = null; - } - outfile.write(line); - } - } - } - outfile.writeImports(config, matches); -} - DirEntry[] listEntries(alias F = "true")(string[] input, bool recursive) { alias filterFunc = unaryFun!F; diff --git a/src/sort.d b/src/sort.d @@ -0,0 +1,168 @@ +module importsort.sort; + +import std.algorithm : findSplit, sort; +import std.array : split; +import std.file : DirEntry, rename; +import std.functional : unaryFun; +import std.range : ElementType; +import std.regex : ctRegex, matchFirst; +import std.stdio : File, stderr; +import std.string : strip, stripLeft; +import std.traits : isIterable; +import std.typecons : Yes; + +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]*)$`; + +struct SortConfig { + bool keepLine = false; + + bool byAttribute = false; + bool byBinding = false; + bool verbose = false; +} + +struct Identifier { + bool byBinding; + string original; + string binding; + + string sortBy() { + if (byBinding) + return hasBinding ? binding : original; + else + return original; + } + + bool hasBinding() { + return binding != null; + } +} + +struct Import { + bool byAttribute; + string line; + + bool public_; + bool static_; + Identifier name; + Identifier[] idents; + string begin; + string end; + + string sortBy() { + if (byAttribute && (public_ || static_)) + return '\0' ~ name.sortBy; + return name.sortBy; + } +} + +void writeImports(File outfile, SortConfig config, Import[] matches) { + if (!matches) + return; + + matches.sort!((a, b) => a.sortBy < b.sortBy); + foreach (m; matches) { + if (config.keepLine) { + outfile.write(m.line); + } else { + outfile.write(m.begin); + if (m.public_) + outfile.write("public "); + if (m.static_) + outfile.write("static "); + if (m.name.hasBinding) { + outfile.writef("import %s = %s", m.name.binding, m.name.original); + } else { + outfile.write("import " ~ m.name.original); + } + foreach (i, ident; m.idents) { + auto begin = i == 0 ? " : " : ", "; + if (ident.hasBinding) { // hasBinding + outfile.writef("%s%s = %s", begin, ident.binding, ident.original); + } else { + outfile.write(begin ~ ident.original); + } + } + outfile.writef(";%s", m.end); + } + } +} + +void sortImports(alias P = "true", R)(R entries, SortConfig config) + if (isIterable!R && is(ElementType!R == DirEntry)) { + alias postFunc = unaryFun!P; + + File infile, outfile; + foreach (entry; entries) { + stderr.writef("\033[34msorting \033[0;1m%s\033[0m\n", entry.name); + + infile = File(entry.name); + outfile = File(entry.name ~ ".new", "w"); + + sortImports(infile, outfile, config); + + infile.close(); + outfile.close(); + + rename(entry.name ~ ".new", entry.name); + + cast(void) postFunc(entry.name); + } +} + +void sortImports(File infile, File outfile, SortConfig config) { + string softEnd = null; + Import[] matches; + + foreach (line; infile.byLine(Yes.keepTerminator)) { + auto linestr = line.idup; + if (auto match = linestr.matchFirst(PATTERN)) { // is import + if (softEnd) { + if (!matches) + outfile.write(softEnd); + softEnd = null; + } + + auto im = Import(config.byAttribute, linestr); + if (match[3]) { + im.name = Identifier(config.byBinding, match[4], match[3]); + } else { + im.name = Identifier(config.byBinding, match[4]); + } + im.begin = match[1]; + im.end = match[6]; + + if (match[2] == "static") + im.static_ = true; + else if (match[2] == "public") + im.public_ = true; + + if (match[5]) { + foreach (id; match[5][1 .. $].split(",")) { + if (auto pair = id.findSplit("=")) { // has alias + im.idents ~= Identifier(config.byBinding, pair[2].strip, pair[0].strip); + } else { + im.idents ~= Identifier(config.byBinding, id.strip); + } + } + im.idents.sort!((a, b) => a.sortBy < b.sortBy); + } + matches ~= im; + } else { + if (!softEnd && linestr.stripLeft == "") { + softEnd = linestr; + } else { + if (matches) { + outfile.writeImports(config, matches); + matches = []; + } + if (softEnd) { + outfile.write(softEnd); + softEnd = null; + } + outfile.write(line); + } + } + } + outfile.writeImports(config, matches); +}