Extended ctags generation with Exuberant metadata used by Vim.

This commit is contained in:
Joakim Brannstrom 2015-05-08 21:29:01 +02:00
parent ae7df76154
commit 81bc94e873
1 changed files with 197 additions and 59 deletions

View File

@ -13,11 +13,13 @@ import std.range;
import std.stdio; import std.stdio;
import std.array; import std.array;
import std.conv; import std.conv;
import std.typecons;
/** /**
* Prints CTAGS information to the given file. * Prints CTAGS information to the given file.
* Includes metadata according to exuberant format used by Vim.
* Params: * Params:
* outpt = the file that CTAGS info is written to * outpt = the file that Exuberant TAGS info is written to
* fileNames = tags will be generated from these files * fileNames = tags will be generated from these files
*/ */
void printCtags(File output, string[] fileNames) void printCtags(File output, string[] fileNames)
@ -28,101 +30,143 @@ void printCtags(File output, string[] fileNames)
foreach (fileName; fileNames) foreach (fileName; fileNames)
{ {
File f = File(fileName); File f = File(fileName);
if (f.size == 0) continue; if (f.size == 0)
continue;
auto bytes = uninitializedArray!(ubyte[])(to!size_t(f.size)); auto bytes = uninitializedArray!(ubyte[])(to!size_t(f.size));
f.rawRead(bytes); f.rawRead(bytes);
auto tokens = getTokensForParser(bytes, config, &cache); auto tokens = getTokensForParser(bytes, config, &cache);
Module m = parseModule(tokens.array, fileName, null, &doNothing); Module m = parseModule(tokens.array, fileName, null, &doNothing);
auto printer = new CTagsPrinter; auto printer = new CTagsPrinter;
printer.fileName = fileName; printer.fileName = fileName;
printer.visit(m); printer.visit(m);
tags ~= printer.tagLines; tags ~= printer.tagLines;
} }
output.write("!_TAG_FILE_FORMAT\t2\n" output.write(
~ "!_TAG_FILE_SORTED\t1\n" "!_TAG_FILE_FORMAT\t2\n" ~ "!_TAG_FILE_SORTED\t1\n" ~ "!_TAG_FILE_AUTHOR\tBrian Schott\n" ~ "!_TAG_PROGRAM_URL\thttps://github.com/Hackerpilot/Dscanner/\n");
~ "!_TAG_FILE_AUTHOR\tBrian Schott\n"
~ "!_TAG_PROGRAM_URL\thttps://github.com/Hackerpilot/Dscanner/\n");
tags.sort().copy(output.lockingTextWriter); tags.sort().copy(output.lockingTextWriter);
} }
private: private:
void doNothing(string, size_t, size_t, string, bool) {} /// States determining how an access modifier affects the parsning.
/// Reset, when ascending the AST reset back to the previous access.
/// Keep, when ascending the AST keep the new access.
enum AccessState
{
Reset,
Keep
}
class CTagsPrinter : ASTVisitor alias ContextType = Tuple!(string, "c", string, "access");
void doNothing(string, size_t, size_t, string, bool)
{
}
string paramsToString(Dec)(const Dec dec)
{
import std.d.formatter : Formatter;
auto app = appender!string();
auto formatter = new Formatter!(typeof(app))(app);
static if (is(Dec == FunctionDeclaration))
{
formatter.format(dec.parameters);
}
else static if (is(Dec == TemplateDeclaration))
{
formatter.format(dec.templateParameters);
}
else static if (is(Dec == Constructor))
{
formatter.format(dec.parameters);
}
return app.data;
}
final class CTagsPrinter
: ASTVisitor
{ {
override void visit(const ClassDeclaration dec) override void visit(const ClassDeclaration dec)
{ {
tagLines ~= "%s\t%s\t%d;\"\tc\tline:%d%s\n".format(dec.name.text, fileName, dec.name.line, dec.name.line, context); tagLines ~= "%s\t%s\t%d;\"\tc\tline:%d%s%s\n".format(dec.name.text,
const c = context; fileName, dec.name.line, dec.name.line, context.c, context.access);
context = "\tclass:" ~ dec.name.text; auto c = context;
context = ContextType("\tclass:" ~ dec.name.text, "\taccess:public");
dec.accept(this); dec.accept(this);
context = c; context = c;
} }
override void visit(const StructDeclaration dec) override void visit(const StructDeclaration dec)
{ {
tagLines ~= "%s\t%s\t%d;\"\ts\tline:%d%s\n".format(dec.name.text, fileName, dec.name.line, dec.name.line, context); if (dec.name == tok!"")
const c = context; {
context = "\tstruct:" ~ dec.name.text; dec.accept(this);
return;
}
tagLines ~= "%s\t%s\t%d;\"\ts\tline:%d%s%s\n".format(dec.name.text,
fileName, dec.name.line, dec.name.line, context.c, context.access);
auto c = context;
context = ContextType("\tstruct:" ~ dec.name.text, "\taccess:public");
dec.accept(this); dec.accept(this);
context = c; context = c;
} }
override void visit(const InterfaceDeclaration dec) override void visit(const InterfaceDeclaration dec)
{ {
tagLines ~= "%s\t%s\t%d;\"\ti\tline:%d%s\n".format(dec.name.text, fileName, dec.name.line, dec.name.line, context); tagLines ~= "%s\t%s\t%d;\"\ti\tline:%d%s%s\n".format(dec.name.text,
const c = context; fileName, dec.name.line, dec.name.line, context.c, context.access);
context = "\tclass:" ~ dec.name.text; auto c = context;
context = ContextType("\tclass:" ~ dec.name.text, context.access);
dec.accept(this); dec.accept(this);
context = c; context = c;
} }
override void visit(const TemplateDeclaration dec) override void visit(const TemplateDeclaration dec)
{ {
tagLines ~= "%s\t%s\t%d;\"\tT\tline:%d%s\n".format(dec.name.text, fileName, dec.name.line, dec.name.line, context); auto params = paramsToString(dec);
const c = context;
context = "\ttemplate:" ~ dec.name.text; tagLines ~= "%s\t%s\t%d;\"\tT\tline:%d%s%s\tsignature:%s\n".format(
dec.name.text, fileName, dec.name.line, dec.name.line, context.c,
context.access, params);
auto c = context;
context = ContextType("\ttemplate:" ~ dec.name.text, context.access);
dec.accept(this); dec.accept(this);
context = c; context = c;
} }
override void visit(const FunctionDeclaration dec) override void visit(const FunctionDeclaration dec)
{ {
tagLines ~= "%s\t%s\t%d;\"\tf\tarity:%d\tline:%d%s\n".format(dec.name.text, fileName, auto params = paramsToString(dec);
dec.name.line, dec.parameters.parameters.length, dec.name.line, context);
const c = context; tagLines ~= "%s\t%s\t%d;\"\tf\tline:%d%s%s\tsignature:%s\n".format(
context = "\tfunction:" ~ dec.name.text; dec.name.text, fileName, dec.name.line, dec.name.line, context.c,
dec.accept(this); context.access, params);
context = c;
} }
override void visit(const Constructor dec) override void visit(const Constructor dec)
{ {
tagLines ~= "this\t%s\t%d;\"\tf\tarity:%d\tline:%d%s\n".format(fileName, auto params = paramsToString(dec);
dec.line, dec.parameters.parameters.length, dec.line, context);
const c = context; tagLines ~= "this\t%s\t%d;\"\tf\tline:%d%s\tsignature:%s%s\n".format(fileName,
context = "\tfunction: this"; dec.line, dec.line, context.c, params, context.access);
dec.accept(this);
context = c;
} }
override void visit(const Destructor dec) override void visit(const Destructor dec)
{ {
tagLines ~= "~this\t%s\t%d;\"\tf\tline:%d%s\n".format(fileName, dec.line, tagLines ~= "~this\t%s\t%d;\"\tf\tline:%d%s\n".format(fileName,
dec.line, context); dec.line, dec.line, context.c);
const c = context;
context = "\tfunction: this";
dec.accept(this);
context = c;
} }
override void visit(const EnumDeclaration dec) override void visit(const EnumDeclaration dec)
{ {
tagLines ~= "%s\t%s\t%d;\"\tg\tline:%d%s\n".format(dec.name.text, fileName, tagLines ~= "%s\t%s\t%d;\"\tg\tline:%d%s%s\n".format(dec.name.text,
dec.name.line, dec.name.line, context); fileName, dec.name.line, dec.name.line, context.c, context.access);
const c = context; auto c = context;
context = "\tenum:" ~ dec.name.text; context = ContextType("\tenum:" ~ dec.name.text, context.access);
dec.accept(this); dec.accept(this);
context = c; context = c;
} }
@ -134,32 +178,32 @@ class CTagsPrinter : ASTVisitor
dec.accept(this); dec.accept(this);
return; return;
} }
tagLines ~= "%s\t%s\t%d;\"\tu\tline:%d%s\n".format(dec.name.text, fileName, tagLines ~= "%s\t%s\t%d;\"\tu\tline:%d%s\n".format(dec.name.text,
dec.name.line, dec.name.line, context); fileName, dec.name.line, dec.name.line, context.c);
const c = context; auto c = context;
context = "\tunion:" ~ dec.name.text; context = ContextType("\tunion:" ~ dec.name.text, context.access);
dec.accept(this); dec.accept(this);
context = c; context = c;
} }
override void visit(const AnonymousEnumMember mem) override void visit(const AnonymousEnumMember mem)
{ {
tagLines ~= "%s\t%s\t%d;\"\te\tline:%d%s\n".format(mem.name.text, fileName, tagLines ~= "%s\t%s\t%d;\"\te\tline:%d%s\n".format(mem.name.text,
mem.name.line, mem.name.line, context); fileName, mem.name.line, mem.name.line, context.c);
} }
override void visit(const EnumMember mem) override void visit(const EnumMember mem)
{ {
tagLines ~= "%s\t%s\t%d;\"\te\tline:%d%s\n".format(mem.name.text, fileName, tagLines ~= "%s\t%s\t%d;\"\te\tline:%d%s\n".format(mem.name.text,
mem.name.line, mem.name.line, context); fileName, mem.name.line, mem.name.line, context.c);
} }
override void visit(const VariableDeclaration dec) override void visit(const VariableDeclaration dec)
{ {
foreach (d; dec.declarators) foreach (d; dec.declarators)
{ {
tagLines ~= "%s\t%s\t%d;\"\tv\tline:%d%s\n".format(d.name.text, fileName, tagLines ~= "%s\t%s\t%d;\"\tv\tline:%d%s%s\n".format(d.name.text,
d.name.line, d.name.line, context); fileName, d.name.line, d.name.line, context.c, context.access);
} }
dec.accept(this); dec.accept(this);
} }
@ -168,22 +212,116 @@ class CTagsPrinter : ASTVisitor
{ {
foreach (i; dec.identifiers) foreach (i; dec.identifiers)
{ {
tagLines ~= "%s\t%s\t%d;\"\tv\tline:%d%s\n".format(i.text, fileName, tagLines ~= "%s\t%s\t%d;\"\tv\tline:%d%s%s\n".format(i.text,
i.line, i.line, context); fileName, i.line, i.line, context.c, context.access);
} }
dec.accept(this); dec.accept(this);
} }
override void visit(const Invariant dec) override void visit(const Invariant dec)
{ {
tagLines ~= "invariant\t%s\t%d;\"\tv\tline:%d%s\n".format(fileName, dec.line, dec.line, context); tagLines ~= "invariant\t%s\t%d;\"\tv\tline:%d%s%s\n".format(fileName,
dec.line, dec.line, context.c, context.access);
}
override void visit(const ModuleDeclaration dec)
{
context = ContextType("", "\taccess:public");
dec.accept(this);
}
override void visit(const Attribute attribute)
{
if (attribute.attribute != tok!"")
{
switch (attribute.attribute.type)
{
case tok!"export":
context.access = "\taccess:public";
break;
case tok!"public":
context.access = "\taccess:public";
break;
case tok!"package":
context.access = "\taccess:protected";
break;
case tok!"protected":
context.access = "\taccess:protected";
break;
case tok!"private":
context.access = "\taccess:private";
break;
default:
}
}
attribute.accept(this);
}
override void visit(const AttributeDeclaration dec)
{
accessSt = AccessState.Keep;
dec.accept(this);
}
override void visit(const Declaration dec)
{
auto c = context;
dec.accept(this);
final switch (accessSt) with (AccessState)
{
case Reset:
context = c;
break;
case Keep:
break;
}
accessSt = AccessState.Reset;
}
override void visit(const Unittest dec)
{
// skipping symbols inside a unit test.
//TODO investigate if it would be useful to show the unittests that was
//found when std.experimental.unittest is released. Could be possible
//to then use the UDA to show the unittest with a "name".
}
override void visit(const AliasDeclaration dec)
{
// Old style alias
if (dec.identifierList)
{
foreach (i; dec.identifierList.identifiers)
{
tagLines ~= "%s\t%s\t%d;\"\ta\tline:%d%s%s\n".format(i.text,
fileName, i.line, i.line, context.c, context.access);
}
}
dec.accept(this);
}
override void visit(const AliasInitializer dec)
{
tagLines ~= "%s\t%s\t%d;\"\ta\tline:%d%s%s\n".format(dec.name.text,
fileName, dec.name.line, dec.name.line, context.c, context.access);
dec.accept(this);
}
override void visit(const AliasThisDeclaration dec)
{
auto name = dec.identifier;
tagLines ~= "%s\t%s\t%d;\"\ta\tline:%d%s%s\n".format(name.text,
fileName, name.line, name.line, context.c, context.access);
dec.accept(this);
} }
alias visit = ASTVisitor.visit; alias visit = ASTVisitor.visit;
string fileName; string fileName;
string[] tagLines; string[] tagLines;
int suppressDepth; ContextType context;
string context; AccessState accessSt;
} }