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}