Extended ctags generation with Exuberant metadata used by Vim.
This commit is contained in:
parent
ae7df76154
commit
81bc94e873
256
src/ctags.d
256
src/ctags.d
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue