dexed/dastworx/src/todos.d

129 lines
3.2 KiB
D

module todos;
import
std.stdio, std.string, std.algorithm, std.array, std.conv, std.traits,
std.ascii, std.range, std.file;
import
dparse.lexer;
import
common;
private __gshared Appender!string stream;
void getTodos(string[] files)
{
mixin(logCall);
stream.reserve(32 + 256 * files.length);
stream.put("object TTodoItems\ritems=<");
foreach(fname; files)
{
StringCache cache = StringCache(StringCache.defaultBucketCount);
LexerConfig config = LexerConfig(fname, StringBehavior.source);
ubyte[] source = cast(ubyte[]) std.file.read(fname);
foreach(ref token; DLexer(source, config, &cache).array
.filter!((a) => a.type == tok!"comment"))
analyze(token, fname);
}
stream.put(">end");
writeln(stream.data);
}
private void analyze(const(Token) token, string fname)
{
string text = token.text.strip.patchPascalString;
string identifier;
mixin(logCall);
// always comment
text.popFrontN(2);
if (text.empty)
return;
// ddoc suffix
if (text.front.among('/', '*', '+'))
{
text.popFront;
if (text.empty)
return;
}
// leading whites
while (text.front.isWhite)
{
text.popFront;
if (text.empty)
return;
}
// "TODO|FIXME|NOTE"
bool isTodoComment;
while (!text.empty)
{
identifier ~= std.ascii.toUpper(text.front);
text.popFront;
if (identifier.among("TODO", "FIXME", "NOTE"))
{
isTodoComment = true;
break;
}
}
if (!isTodoComment) return;
identifier = "";
// splits "fields" and "description"
bool isWellFormed;
string fields;
while (!text.empty)
{
auto front = text.front;
identifier ~= front;
text.popFront;
if (front == ':')
{
if (identifier.length) fields = identifier;
isWellFormed = text.length > 0;
break;
}
}
if (!isWellFormed) return;
identifier = "";
// parses "fields"
string a, c, p, s;
while (!fields.empty)
{
const dchar front = fields.front;
fields.popFront;
if ((front == '-' || fields.empty) && identifier.length > 2)
{
const string fieldContent = identifier[2..$].strip;
switch(identifier[0..2].toUpper)
{
default: break;
case "-A": a = fieldContent; break;
case "-C": c = fieldContent; break;
case "-P": p = fieldContent; break;
case "-S": s = fieldContent; break;
}
identifier = "";
}
identifier ~= front;
}
if (text.length > 1 && text[$-2..$].among("*/", "+/"))
text.length -=2;
stream.put("\ritem\r");
stream.put(format("filename='%s'\r", fname));
stream.put(format("line='%s'\r", token.line));
stream.put(format("text='%s'\r", text));
if (c.length)
stream.put(format("category='%s'\r", c));
if (a.length)
stream.put(format("assignee='%s'\r", a));
if (p.length)
stream.put(format("priority='%s'\r", p));
if (s.length)
stream.put(format("status='%s'\r", s));
stream.put("end");
}