misc/importsort-d

src/sort.d in v0.3.0
Repositories | Summary | Log | Files | README.md

sort.d (4368B) download


  1module importsort.sort;
  2
  3import std.algorithm : findSplit, remove, sort;
  4import std.array : split;
  5import std.file : DirEntry, rename;
  6import std.functional : unaryFun;
  7import std.range : ElementType;
  8import std.regex : ctRegex, matchFirst;
  9import std.stdio : File, stderr;
 10import std.string : strip, stripLeft;
 11import std.traits : isIterable;
 12import std.typecons : Yes;
 13
 14enum 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]*)$`;
 15
 16struct SortConfig {
 17	bool keepLine = false;
 18	bool byAttribute = false;
 19	bool byBinding = false;
 20	bool verbose = false;
 21	bool merge = false;
 22}
 23
 24struct Identifier {
 25	bool byBinding;
 26	string original;
 27	string binding;
 28
 29	string sortBy() {
 30		if (byBinding)
 31			return hasBinding ? binding : original;
 32		else
 33			return original;
 34	}
 35
 36	bool hasBinding() {
 37		return binding != null;
 38	}
 39}
 40
 41struct Import {
 42	bool byAttribute;
 43	string line;
 44
 45	bool public_;
 46	bool static_;
 47	Identifier name;
 48	Identifier[] idents;
 49	string begin;
 50	string end;
 51
 52	string sortBy() {
 53		if (byAttribute && (public_ || static_))
 54			return '\0' ~ name.sortBy;
 55		return name.sortBy;
 56	}
 57}
 58
 59void writeImports(File outfile, SortConfig config, Import[] matches) {
 60	if (!matches)
 61		return;
 62
 63	if (config.merge) {
 64		for (int i = 0; i < matches.length; i++) {
 65			for (int j = i + 1; j < matches.length; j++) {
 66				if (matches[i].name.original == matches[j].name.original
 67					&& matches[i].name.binding == matches[j].name.binding) {
 68
 69					matches[i].line = null;
 70					matches[i].idents ~= matches[j].idents;
 71					matches = matches.remove(j);
 72					j--;
 73				}
 74			}
 75		}
 76	}
 77
 78	matches.sort!((a, b) => a.sortBy < b.sortBy);
 79	bool first;
 80
 81	foreach (m; matches) {
 82		if (config.keepLine && m.line.length > 0) {
 83			outfile.write(m.line);
 84		} else {
 85			outfile.write(m.begin);
 86			if (m.public_)
 87				outfile.write("public ");
 88			if (m.static_)
 89				outfile.write("static ");
 90			if (m.name.hasBinding) {
 91				outfile.writef("import %s = %s", m.name.binding, m.name.original);
 92			} else {
 93				outfile.write("import " ~ m.name.original);
 94			}
 95			first = true;
 96			foreach (ident; m.idents) {
 97				auto begin = first ? " : " : ", ";
 98				first = false;
 99				if (ident.hasBinding) { // hasBinding
100					outfile.writef("%s%s = %s", begin, ident.binding, ident.original);
101				} else {
102					outfile.write(begin ~ ident.original);
103				}
104			}
105			outfile.writef(";%s", m.end);
106		}
107	}
108}
109
110void sortImports(alias P = "true", R)(R entries, SortConfig config)
111		if (isIterable!R && is(ElementType!R == DirEntry)) {
112	alias postFunc = unaryFun!P;
113
114	File infile, outfile;
115	foreach (entry; entries) {
116		stderr.writef("\033[34msorting \033[0;1m%s\033[0m\n", entry.name);
117
118		infile = File(entry.name);
119		outfile = File(entry.name ~ ".new", "w");
120
121		sortImports(infile, outfile, config);
122
123		infile.close();
124		outfile.close();
125
126		rename(entry.name ~ ".new", entry.name);
127
128		cast(void) postFunc(entry.name);
129	}
130}
131
132void sortImports(File infile, File outfile, SortConfig config) {
133	string softEnd = null;
134	Import[] matches;
135
136	foreach (line; infile.byLine(Yes.keepTerminator)) {
137		auto linestr = line.idup;
138		if (auto match = linestr.matchFirst(PATTERN)) { // is import
139			if (softEnd) {
140				if (!matches)
141					outfile.write(softEnd);
142				softEnd = null;
143			}
144
145			auto im = Import(config.byAttribute, linestr);
146			if (match[3]) {
147				im.name = Identifier(config.byBinding, match[4], match[3]);
148			} else {
149				im.name = Identifier(config.byBinding, match[4]);
150			}
151			im.begin = match[1];
152			im.end = match[6];
153
154			if (match[2] == "static")
155				im.static_ = true;
156			else if (match[2] == "public")
157				im.public_ = true;
158
159			if (match[5]) {
160				foreach (id; match[5][1 .. $].split(",")) {
161					if (auto pair = id.findSplit("=")) { // has alias
162						im.idents ~= Identifier(config.byBinding, pair[2].strip, pair[0].strip);
163					} else {
164						im.idents ~= Identifier(config.byBinding, id.strip);
165					}
166				}
167				im.idents.sort!((a, b) => a.sortBy < b.sortBy);
168			}
169			matches ~= im;
170		} else {
171			if (!softEnd && linestr.stripLeft == "") {
172				softEnd = linestr;
173			} else {
174				if (matches) {
175					outfile.writeImports(config, matches);
176					matches = [];
177				}
178				if (softEnd) {
179					outfile.write(softEnd);
180					softEnd = null;
181				}
182				outfile.write(line);
183			}
184		}
185	}
186	outfile.writeImports(config, matches);
187}