1 module dmdtags.main;
2 
3 int main(string[] args)
4 {
5 	import std.stdio: stderr;
6 
7 	try tryMain(args);
8 	catch (Exception e) {
9 		stderr.writeln("dmdtags: ", e.message);
10 		return 1;
11 	}
12 	return 0;
13 }
14 
15 void printUsage()
16 {
17 	import std.stdio: stderr, writeln;
18 
19 	stderr.writeln("Usage: dmdtags [-R] [-a] [-f|-o tagfile] [path...]");
20 }
21 
22 void tryMain(string[] args)
23 {
24 	import dmdtags.generate: SymbolTagger;
25 	import dmdtags.appender: Appender;
26 	import dmdtags.span;
27 
28 	import dmd.frontend: initDMD, parseModule;
29 	import dmd.errors: DiagnosticHandler;
30 
31 	import std.algorithm: sort, uniq, each;
32 	import std.getopt: getopt;
33 	import std.stdio: File, stdout;
34 	import std.file: exists, isDir, isFile, dirEntries, SpanMode;
35 
36 	bool recurse;
37 	string tagfile = "tags";
38 	bool append;
39 
40 	auto result = args.getopt(
41 		"recurse|R", &recurse,
42 		"f|o", &tagfile,
43 		"append|a", &append,
44 	);
45 
46 	if (result.helpWanted) {
47 		printUsage();
48 		return;
49 	}
50 
51 	string[] paths;
52 	if (args.length > 1) {
53 		paths = args[1 .. $];
54 	} else if (recurse) {
55 		paths = ["."];
56 	} else {
57 		printUsage();
58 		return;
59 	}
60 
61 	DiagnosticHandler ignoreErrors = (ref _1, _2, _3, _4, _5, _6, _7) => true;
62 	initDMD(ignoreErrors);
63 
64 	Appender!(Span!(const(char))) tags;
65 	scope tagger = new SymbolTagger(tags);
66 
67 	void processSourceFile(string path)
68 	{
69 		auto parseResult = parseModule(path);
70 		parseResult.module_.accept(tagger);
71 	}
72 
73 	foreach (path; paths) {
74 		if (path.isFile) {
75 			processSourceFile(path);
76 		} else if (recurse && path.isDir) {
77 			foreach (entry; dirEntries(path, "*.{d,di}", SpanMode.depth)) {
78 				if (entry.isFile) {
79 					processSourceFile(entry.name);
80 				}
81 			}
82 		}
83 	}
84 
85 	if (append && tagfile.exists) {
86 		import std.algorithm: filter, startsWith, map, copy;
87 
88 		File(tagfile, "r")
89 			.byLineCopy
90 			.filter!(line => !line.startsWith("!_TAG_"))
91 			.map!((const(char)[] line) => line.span.headMutable)
92 			.copy(tags);
93 	}
94 
95 	sort(tags[]);
96 
97 	File output;
98 	if (tagfile == "-") {
99 		output = stdout;
100 	} else {
101 		output = File(tagfile, "w");
102 	}
103 
104 	output.writeln("!_TAG_FILE_SORTED\t1\t/0=unsorted, 1=sorted, 2=foldcase/");
105 	tags[].uniq.each!(tag => output.writeln(tag));
106 }