diff --git a/analysis/linespan.d b/analysis/linespan.d new file mode 100644 index 0000000..cedbef5 --- /dev/null +++ b/analysis/linespan.d @@ -0,0 +1,81 @@ +// Copyright Brian Schott (Sir Alaran) 2014. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +module analysis.linespan; + + +/** + * Used for determining which lines to include as context in the generated HTML + * report. + */ +struct LineSpans +{ +public: + /** + * Returns: true if any of the spans contain the given line number + */ + bool containsLine(size_t line) pure const nothrow @safe + { + foreach (span; spans) + { + if (span.low <= line && span.high >= line) + return true; + } + return false; + } + + /** + * Params: line = the line to add + */ + void addLine(size_t line) pure nothrow @safe + { + immutable size_t low = line >= context ? line - context : 0; + immutable size_t high = line + context; + foreach (ref span; spans) + { + if (low <= span.low && high >= span.low && high <= span.high) + { + span.low = low; + return; + } + else if (high >= span.high && low <= span.high && low >= span.low) + { + span.high = high; + return; + } + } + spans ~= Span(low, high); + } + +private: + + static struct Span + { + size_t low; + size_t high; + } + + Span[] spans; + + enum context = 3; +} + +unittest +{ + import std.stdio; + LineSpans l; + l.addLine(4); + foreach (i; 2 .. 7) + assert (l.containsLine(i)); + assert (!l.containsLine(1)); + assert (!l.containsLine(7)); + l.addLine(5); + assert (l.containsLine(7)); + l.addLine(40); + l.addLine(35); + foreach (i; 33 .. 43) + assert (l.containsLine(i)); + stderr.writeln("Unit tests for LineSpans passed"); +} diff --git a/analysis/output.d b/analysis/output.d new file mode 100644 index 0000000..8bc2fcb --- /dev/null +++ b/analysis/output.d @@ -0,0 +1,251 @@ +module analysis.output; + +import std.stdio; +import std.algorithm; +import stdx.d.lexer; +import analysis.base; +import analysis.linespan; +import highlighter; + +void writeWhitespace(File file, string text, ref uint line, MessageSet messages, + ref const LineSpans spans) +{ + foreach (char c; text) + { + if (c == '\r') + continue; + else if (c == '\n') + { + if (spans.containsLine(line)) + file.write("\n"); + foreach (message; messages[].filter!(a => a.line == line - 1)) + writeMessage(file, message); + bool prevWasVisible = false; + if (spans.containsLine(line)) + { + prevWasVisible = true; + file.writef("%d", line); + } + line++; + if (!spans.containsLine(line) && prevWasVisible) + file.writeln("
"); + } + else if (spans.containsLine(line)) + { + if (c == '\t') + file.write(" "); + else + file.write(c); + } + } +} + +void writeStrOrCom(File file, string text, string cssClass, ref uint line, + ref const LineSpans spans) +{ + file.write(""); + foreach (char c; text) + { + if (c == '\r') + continue; + else if (c == '\n') + { + bool prevWasVisible = false; + if (spans.containsLine(line)) + { + prevWasVisible = true; + file.writef("\n%d", line, cssClass); + } + line++; + if (!spans.containsLine(line) && prevWasVisible) + file.writeln(""); + } + else if (spans.containsLine(line)) + { + if (c == '<') + file.write("<"); + else if (c == '&') + file.write("&"); + else + file.write(c); + } + } + file.write(""); +} + +void writeToken(File file, ref const Token t, ref uint line, MessageSet messages, + ref const LineSpans spans) +{ + if (t == tok!"whitespace") + writeWhitespace(file, t.text, line, messages, spans); + else if (t.type == tok!"comment") + writeStrOrCom(file, t.text, "com", line, spans); + else if (isStringLiteral(t.type) || t.type == tok!"characterLiteral") + writeStrOrCom(file, t.text, "str", line, spans); + else if (spans.containsLine(line)) + { + if (isBasicType(t.type)) + file.writeSpan("type", str(t.type)); + else if (isKeyword(t.type)) + file.writeSpan("kwrd", str(t.type)); + else if (isNumberLiteral(t.type)) + file.writeSpan("num", t.text); + else if (isOperator(t.type)) + file.writeSpan("op", str(t.type)); + else + file.write(t.text); + } +} + +void writeMessage(File file, ref const Message message) +{ + file.write("");
+ uint currentLine = 2;
+ LineSpans ls = generateLineSpans(messageSet);
+ if (ls.containsLine(1))
+ file.write("1");
+ foreach (token; tokens)
+ {
+ writeToken(file, token, currentLine, messageSet, ls);
+ }
+ file.writeln("
");
+ file.writeln("File | Warning Count |
---|---|
", fileName, " | ", warningCount, " |