Merge bf1b396bb1
into 0dd4c78985
This commit is contained in:
commit
6406a8fb5d
|
@ -85,6 +85,11 @@ final class FirstPass : ASTVisitor
|
||||||
|
|
||||||
override void visit(const Unittest u)
|
override void visit(const Unittest u)
|
||||||
{
|
{
|
||||||
|
if (previousSymbol && previousSymbol.acSymbol)
|
||||||
|
makeExampleDocumentation(u, previousSymbol.acSymbol.doc);
|
||||||
|
|
||||||
|
auto associated = previousSymbol;
|
||||||
|
scope(exit) previousSymbol = associated;
|
||||||
// Create a dummy symbol because we don't want unit test symbols leaking
|
// Create a dummy symbol because we don't want unit test symbols leaking
|
||||||
// into the symbol they're declared in.
|
// into the symbol they're declared in.
|
||||||
pushSymbol(UNITTEST_SYMBOL_NAME,
|
pushSymbol(UNITTEST_SYMBOL_NAME,
|
||||||
|
@ -130,7 +135,7 @@ final class FirstPass : ASTVisitor
|
||||||
dec.name.index, dec.returnType);
|
dec.name.index, dec.returnType);
|
||||||
scope (exit) popSymbol();
|
scope (exit) popSymbol();
|
||||||
currentSymbol.acSymbol.protection = protection.current;
|
currentSymbol.acSymbol.protection = protection.current;
|
||||||
currentSymbol.acSymbol.doc = makeDocumentation(dec.comment);
|
makeDocumentation(currentSymbol.acSymbol.doc, dec.comment);
|
||||||
|
|
||||||
istring lastComment = this.lastComment;
|
istring lastComment = this.lastComment;
|
||||||
this.lastComment = istring.init;
|
this.lastComment = istring.init;
|
||||||
|
@ -250,7 +255,7 @@ final class FirstPass : ASTVisitor
|
||||||
addTypeToLookups(symbol.typeLookups, dec.type);
|
addTypeToLookups(symbol.typeLookups, dec.type);
|
||||||
symbol.parent = currentSymbol;
|
symbol.parent = currentSymbol;
|
||||||
symbol.acSymbol.protection = protection.current;
|
symbol.acSymbol.protection = protection.current;
|
||||||
symbol.acSymbol.doc = makeDocumentation(declarator.comment);
|
makeDocumentation(symbol.acSymbol.doc, declarator.comment);
|
||||||
currentSymbol.addChild(symbol, true);
|
currentSymbol.addChild(symbol, true);
|
||||||
currentScope.addSymbol(symbol.acSymbol, false);
|
currentScope.addSymbol(symbol.acSymbol, false);
|
||||||
|
|
||||||
|
@ -272,7 +277,7 @@ final class FirstPass : ASTVisitor
|
||||||
symbol.parent = currentSymbol;
|
symbol.parent = currentSymbol;
|
||||||
populateInitializer(symbol, part.initializer);
|
populateInitializer(symbol, part.initializer);
|
||||||
symbol.acSymbol.protection = protection.current;
|
symbol.acSymbol.protection = protection.current;
|
||||||
symbol.acSymbol.doc = makeDocumentation(dec.comment);
|
makeDocumentation(symbol.acSymbol.doc, dec.comment);
|
||||||
currentSymbol.addChild(symbol, true);
|
currentSymbol.addChild(symbol, true);
|
||||||
currentScope.addSymbol(symbol.acSymbol, false);
|
currentScope.addSymbol(symbol.acSymbol, false);
|
||||||
|
|
||||||
|
@ -301,7 +306,7 @@ final class FirstPass : ASTVisitor
|
||||||
currentSymbol.addChild(symbol, true);
|
currentSymbol.addChild(symbol, true);
|
||||||
currentScope.addSymbol(symbol.acSymbol, false);
|
currentScope.addSymbol(symbol.acSymbol, false);
|
||||||
symbol.acSymbol.protection = protection.current;
|
symbol.acSymbol.protection = protection.current;
|
||||||
symbol.acSymbol.doc = makeDocumentation(aliasDeclaration.comment);
|
makeDocumentation(symbol.acSymbol.doc, aliasDeclaration.comment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -317,7 +322,7 @@ final class FirstPass : ASTVisitor
|
||||||
currentSymbol.addChild(symbol, true);
|
currentSymbol.addChild(symbol, true);
|
||||||
currentScope.addSymbol(symbol.acSymbol, false);
|
currentScope.addSymbol(symbol.acSymbol, false);
|
||||||
symbol.acSymbol.protection = protection.current;
|
symbol.acSymbol.protection = protection.current;
|
||||||
symbol.acSymbol.doc = makeDocumentation(aliasDeclaration.comment);
|
makeDocumentation(symbol.acSymbol.doc, aliasDeclaration.comment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -399,7 +404,7 @@ final class FirstPass : ASTVisitor
|
||||||
symbol.parent = currentSymbol;
|
symbol.parent = currentSymbol;
|
||||||
currentSymbol.addChild(symbol, true);
|
currentSymbol.addChild(symbol, true);
|
||||||
currentScope.addSymbol(symbol.acSymbol, false);
|
currentScope.addSymbol(symbol.acSymbol, false);
|
||||||
symbol.acSymbol.doc = makeDocumentation(dec.comment);
|
makeDocumentation(symbol.acSymbol.doc, dec.comment);
|
||||||
|
|
||||||
istring lastComment = this.lastComment;
|
istring lastComment = this.lastComment;
|
||||||
this.lastComment = istring.init;
|
this.lastComment = istring.init;
|
||||||
|
@ -415,6 +420,7 @@ final class FirstPass : ASTVisitor
|
||||||
}
|
}
|
||||||
|
|
||||||
currentSymbol = currentSymbol.parent;
|
currentSymbol = currentSymbol.parent;
|
||||||
|
previousSymbol = symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
mixin visitEnumMember!EnumMember;
|
mixin visitEnumMember!EnumMember;
|
||||||
|
@ -847,11 +853,15 @@ private:
|
||||||
currentSymbol.addChild(symbol, true);
|
currentSymbol.addChild(symbol, true);
|
||||||
currentScope.addSymbol(symbol.acSymbol, false);
|
currentScope.addSymbol(symbol.acSymbol, false);
|
||||||
currentSymbol = symbol;
|
currentSymbol = symbol;
|
||||||
|
pushedSymbolsStack.assumeSafeAppend ~= symbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
void popSymbol()
|
void popSymbol()
|
||||||
{
|
{
|
||||||
|
assert(pushedSymbolsStack.length, "called popSymbol without pushSymbol");
|
||||||
currentSymbol = currentSymbol.parent;
|
currentSymbol = currentSymbol.parent;
|
||||||
|
previousSymbol = pushedSymbolsStack[$ - 1];
|
||||||
|
pushedSymbolsStack.length--;
|
||||||
}
|
}
|
||||||
|
|
||||||
template visitEnumMember(T)
|
template visitEnumMember(T)
|
||||||
|
@ -861,7 +871,7 @@ private:
|
||||||
pushSymbol(member.name.text, CompletionKind.enumMember, symbolFile,
|
pushSymbol(member.name.text, CompletionKind.enumMember, symbolFile,
|
||||||
member.name.index, member.type);
|
member.name.index, member.type);
|
||||||
scope(exit) popSymbol();
|
scope(exit) popSymbol();
|
||||||
currentSymbol.acSymbol.doc = makeDocumentation(member.comment);
|
makeDocumentation(currentSymbol.acSymbol.doc, member.comment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -881,7 +891,7 @@ private:
|
||||||
else
|
else
|
||||||
currentSymbol.acSymbol.addChildren(aggregateSymbols[], false);
|
currentSymbol.acSymbol.addChildren(aggregateSymbols[], false);
|
||||||
currentSymbol.acSymbol.protection = protection.current;
|
currentSymbol.acSymbol.protection = protection.current;
|
||||||
currentSymbol.acSymbol.doc = makeDocumentation(dec.comment);
|
makeDocumentation(currentSymbol.acSymbol.doc, dec.comment);
|
||||||
|
|
||||||
istring lastComment = this.lastComment;
|
istring lastComment = this.lastComment;
|
||||||
this.lastComment = istring.init;
|
this.lastComment = istring.init;
|
||||||
|
@ -910,7 +920,7 @@ private:
|
||||||
currentSymbol.addChild(symbol, true);
|
currentSymbol.addChild(symbol, true);
|
||||||
processParameters(symbol, null, THIS_SYMBOL_NAME, parameters, templateParameters);
|
processParameters(symbol, null, THIS_SYMBOL_NAME, parameters, templateParameters);
|
||||||
symbol.acSymbol.protection = protection.current;
|
symbol.acSymbol.protection = protection.current;
|
||||||
symbol.acSymbol.doc = makeDocumentation(doc);
|
makeDocumentation(symbol.acSymbol.doc, doc);
|
||||||
|
|
||||||
istring lastComment = this.lastComment;
|
istring lastComment = this.lastComment;
|
||||||
this.lastComment = istring.init;
|
this.lastComment = istring.init;
|
||||||
|
@ -923,6 +933,7 @@ private:
|
||||||
currentSymbol = symbol;
|
currentSymbol = symbol;
|
||||||
functionBody.accept(this);
|
functionBody.accept(this);
|
||||||
currentSymbol = currentSymbol.parent;
|
currentSymbol = currentSymbol.parent;
|
||||||
|
previousSymbol = symbol;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -934,7 +945,7 @@ private:
|
||||||
currentSymbol.addChild(symbol, true);
|
currentSymbol.addChild(symbol, true);
|
||||||
symbol.acSymbol.callTip = internString("~this()");
|
symbol.acSymbol.callTip = internString("~this()");
|
||||||
symbol.acSymbol.protection = protection.current;
|
symbol.acSymbol.protection = protection.current;
|
||||||
symbol.acSymbol.doc = makeDocumentation(doc);
|
makeDocumentation(symbol.acSymbol.doc, doc);
|
||||||
|
|
||||||
istring lastComment = this.lastComment;
|
istring lastComment = this.lastComment;
|
||||||
this.lastComment = istring.init;
|
this.lastComment = istring.init;
|
||||||
|
@ -947,6 +958,7 @@ private:
|
||||||
currentSymbol = symbol;
|
currentSymbol = symbol;
|
||||||
functionBody.accept(this);
|
functionBody.accept(this);
|
||||||
currentSymbol = currentSymbol.parent;
|
currentSymbol = currentSymbol.parent;
|
||||||
|
previousSymbol = symbol;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1189,14 +1201,85 @@ private:
|
||||||
lookups.insert(lookup);
|
lookups.insert(lookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
DocString makeDocumentation(string documentation)
|
void makeDocumentation(ref DocString into, string documentation)
|
||||||
{
|
{
|
||||||
if (documentation.isDitto)
|
if (documentation.isDitto)
|
||||||
return DocString(lastComment, true);
|
{
|
||||||
|
into = DocString(lastComment, true);
|
||||||
|
into.dittoOf = lastDocStringInstance;
|
||||||
|
lastDocStringInstance = &into;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
lastComment = internString(documentation);
|
lastComment = internString(documentation);
|
||||||
return DocString(lastComment, false);
|
into = DocString(lastComment, false);
|
||||||
|
lastDocStringInstance = &into;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static makeExampleDocumentation(const Unittest block, ref DocString doc)
|
||||||
|
{
|
||||||
|
import dparse.trivia;
|
||||||
|
import std.algorithm;
|
||||||
|
import std.array;
|
||||||
|
import std.string;
|
||||||
|
|
||||||
|
auto tokens = block.tokens;
|
||||||
|
if (tokens.length)
|
||||||
|
{
|
||||||
|
if (block.comment !is null)
|
||||||
|
{
|
||||||
|
auto data = appender!string;
|
||||||
|
data ~= "Examples:\n\n";
|
||||||
|
if (block.comment.length)
|
||||||
|
{
|
||||||
|
data ~= block.comment;
|
||||||
|
data ~= "\n\n";
|
||||||
|
}
|
||||||
|
data ~= "---\n";
|
||||||
|
assert(tokens.length >= 3);
|
||||||
|
auto unittestTok = tokens.countUntil!(t => t.type == tok!"unittest");
|
||||||
|
assert(unittestTok != -1);
|
||||||
|
auto openingTok = tokens[unittestTok .. $].countUntil!(t => t.type == tok!"{");
|
||||||
|
assert(openingTok != -1);
|
||||||
|
openingTok += unittestTok;
|
||||||
|
assert(tokens[$ - 1].type == tok!"}");
|
||||||
|
|
||||||
|
auto codeData = appender!string;
|
||||||
|
foreach (trailingStart; tokens[openingTok].trailingTrivia)
|
||||||
|
codeData ~= trailingStart.text;
|
||||||
|
|
||||||
|
size_t currentLine = size_t.max;
|
||||||
|
foreach (token; tokens[openingTok + 1 .. $ - 1])
|
||||||
|
{
|
||||||
|
currentLine = token.line;
|
||||||
|
|
||||||
|
foreach (leading; token.leadingTrivia)
|
||||||
|
codeData ~= leading.text;
|
||||||
|
|
||||||
|
if (token.text.length)
|
||||||
|
codeData ~= token.text;
|
||||||
|
else
|
||||||
|
codeData ~= str(token.type);
|
||||||
|
|
||||||
|
foreach (trailing; token.trailingTrivia)
|
||||||
|
codeData ~= trailing.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (leadingEnd; tokens[$ - 1].leadingTrivia)
|
||||||
|
codeData ~= leadingEnd.text;
|
||||||
|
|
||||||
|
data ~= codeData.data.outdent.chompPrefix("\n").chomp("\n");
|
||||||
|
|
||||||
|
data ~= "\n---\n";
|
||||||
|
|
||||||
|
DocString* s = &doc;
|
||||||
|
while (s)
|
||||||
|
{
|
||||||
|
s.examples ~= istring(data.data);
|
||||||
|
s = s.dittoOf;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1207,7 +1290,10 @@ private:
|
||||||
Scope* currentScope;
|
Scope* currentScope;
|
||||||
|
|
||||||
/// Current symbol
|
/// Current symbol
|
||||||
SemanticSymbol* currentSymbol;
|
SemanticSymbol* currentSymbol, previousSymbol;
|
||||||
|
|
||||||
|
/// Stack of semantic symbols, for referencing the previousSymbol from popSymbol
|
||||||
|
SemanticSymbol*[] pushedSymbolsStack;
|
||||||
|
|
||||||
/// Path to the file being converted
|
/// Path to the file being converted
|
||||||
istring symbolFile;
|
istring symbolFile;
|
||||||
|
@ -1224,6 +1310,8 @@ private:
|
||||||
/// Last comment for ditto-ing
|
/// Last comment for ditto-ing
|
||||||
istring lastComment;
|
istring lastComment;
|
||||||
|
|
||||||
|
DocString* lastDocStringInstance;
|
||||||
|
|
||||||
const Module mod;
|
const Module mod;
|
||||||
|
|
||||||
Rebindable!(const ExpressionNode) feExpression;
|
Rebindable!(const ExpressionNode) feExpression;
|
||||||
|
|
|
@ -149,10 +149,15 @@ class SimpleParser : Parser
|
||||||
{
|
{
|
||||||
override Unittest parseUnittest()
|
override Unittest parseUnittest()
|
||||||
{
|
{
|
||||||
|
auto start = index;
|
||||||
expect(tok!"unittest");
|
expect(tok!"unittest");
|
||||||
if (currentIs(tok!"{"))
|
if (currentIs(tok!"{"))
|
||||||
skipBraces();
|
skipBraces();
|
||||||
return allocator.make!Unittest;
|
auto ret = allocator.make!Unittest;
|
||||||
|
ret.tokens = tokens[start .. index];
|
||||||
|
ret.comment = comment;
|
||||||
|
comment = null;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
override MissingFunctionBody parseMissingFunctionBody()
|
override MissingFunctionBody parseMissingFunctionBody()
|
||||||
|
|
|
@ -524,22 +524,40 @@ struct DocString
|
||||||
/// Creates a non-ditto comment.
|
/// Creates a non-ditto comment.
|
||||||
this(istring content)
|
this(istring content)
|
||||||
{
|
{
|
||||||
this.content = content;
|
this.rawContent = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a comment which may have been ditto, but has been resolved.
|
/// Creates a comment which may have been ditto, but has been resolved.
|
||||||
this(istring content, bool ditto)
|
this(istring content, bool ditto)
|
||||||
{
|
{
|
||||||
this.content = content;
|
this.rawContent = content;
|
||||||
this.ditto = ditto;
|
this.ditto = ditto;
|
||||||
}
|
}
|
||||||
|
|
||||||
alias content this;
|
alias toString this;
|
||||||
|
|
||||||
|
deprecated("use toString to get a full formatted doc string or rawContent for just what is applied directly (or ditto'd) on the function") alias content = rawContent;
|
||||||
|
|
||||||
/// Contains the documentation string associated with this symbol, resolves ditto to the previous comment with correct scope.
|
/// Contains the documentation string associated with this symbol, resolves ditto to the previous comment with correct scope.
|
||||||
istring content;
|
istring rawContent;
|
||||||
/// `true` if the documentation was just a "ditto" comment copying from the previous comment.
|
/// `true` if the documentation was just a "ditto" comment copying from the previous comment.
|
||||||
bool ditto;
|
bool ditto;
|
||||||
|
/// Contains the source code + docstring for each documented unittest example associated with this symbol.
|
||||||
|
istring[] examples;
|
||||||
|
|
||||||
|
// package-private because we don't want to overcomplicate the lifetime in
|
||||||
|
// the public API. (This might point to a broken address later)
|
||||||
|
package(dsymbol) DocString* dittoOf;
|
||||||
|
|
||||||
|
string toString() const @safe pure
|
||||||
|
{
|
||||||
|
import std.algorithm;
|
||||||
|
import std.array;
|
||||||
|
|
||||||
|
return examples.length
|
||||||
|
? (rawContent ~ "\n\n" ~ examples.map!"a.data".join("\n"))
|
||||||
|
: rawContent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UpdatePair
|
struct UpdatePair
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Does foo stuff.\n\nExamples:\n\n---\n// usable with ints\nfoo(1);\n// and with strings!\nif (auto line = readln())\n foo(line);\n\n// or here\nfoo( 1+2 );\n---\n\nExamples:\n\nsecond usage works too\n\n---\nfoo();\n---\n
|
|
@ -0,0 +1,30 @@
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
foo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Does foo stuff.
|
||||||
|
template foo()
|
||||||
|
{
|
||||||
|
void foo(int a) {}
|
||||||
|
void foo(string b) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
// usable with ints
|
||||||
|
foo(1);
|
||||||
|
// and with strings!
|
||||||
|
if (auto line = readln())
|
||||||
|
foo(line);
|
||||||
|
|
||||||
|
// or here
|
||||||
|
foo( 1+2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/// second usage works too
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
foo();
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
set -e
|
||||||
|
set -u
|
||||||
|
|
||||||
|
../../bin/dcd-client $1 file.d -d -c20 > actual1.txt
|
||||||
|
diff actual1.txt expected1.txt
|
Loading…
Reference in New Issue