Merge pull request #248 from smolt/etags-update

Etags updates
This commit is contained in:
Brian Schott 2015-05-09 18:39:50 -07:00
commit f0f0110307
2 changed files with 140 additions and 40 deletions

View File

@ -16,13 +16,24 @@ import std.path;
import std.array;
import std.conv;
// Prefix tags with their module name. Seems like correct behavior, but just
// in case, make it an option.
version = UseModuleContext;
// Could not find "official" definition of protection (public/private/etc).
// Behavior modeled here was reversed engineered based on dmd 2.067.
// class/interface and non-anonymous struct/union/enum members default to
// public, regardless of the enclosing declaration. template and anonymous
// struct/union/enum members default to the enclosing protection.
/**
* Prints ETAGS information to the given file.
* Params:
* outpt = the file that ETAGS info is written to
* tagAll = if set, tag private/package declaration too
* fileNames = tags will be generated from these files
*/
void printEtags(File output, string[] fileNames)
void printEtags(File output, bool tagAll, string[] fileNames)
{
LexerConfig config;
StringCache cache = StringCache(StringCache.defaultBucketCount);
@ -34,15 +45,17 @@ void printEtags(File output, string[] fileNames)
f.rawRead(bytes);
auto tokens = getTokensForParser(bytes, config, &cache);
Module m = parseModule(tokens.array, fileName, null, &doNothing);
auto printer = new EtagsPrinter;
// I think I like this
enum useModuleContext = true;
if (useModuleContext)
printer.context = m.moduleFullName(fileName) ~ ".";
auto printer = new EtagsPrinter;
printer.moduleName = m.moduleFullName(fileName);
version(UseModuleContext)
printer.context = printer.moduleName ~ ".";
printer.privateVisibility = tagAll?
Visibility.exposed :
Visibility.hidden;
printer.bytes = bytes.sansBOM;
printer.visit(m);
output.writef("\f\n%s,%u\n", fileName, printer.tags.length);
printer.tags.copy(output.lockingTextWriter);
}
@ -50,6 +63,8 @@ void printEtags(File output, string[] fileNames)
private:
enum Visibility {exposed, hidden}
void doNothing(string, size_t, size_t, string, bool) {}
ubyte[] sansBOM(ubyte[] bytes)
@ -58,7 +73,6 @@ ubyte[] sansBOM(ubyte[] bytes)
return (bytes.length >= 3 &&
bytes[0] == 0xef && bytes[1] == 0xbb && bytes[2] == 0xbf)
? bytes[3 .. $] : bytes;
}
string moduleFullName(Module m, string fileName)
@ -71,23 +85,64 @@ string moduleFullName(Module m, string fileName)
return m.moduleDeclaration.moduleName.identifiers.map!(i=>i.text).join(".");
}
class EtagsPrinter : ASTVisitor
final class EtagsPrinter : ASTVisitor
{
override void visit(const ModuleDeclaration dec)
{
auto tok0 = dec.moduleName.identifiers[0];
auto was = context;
context = "";
maketag(moduleName, tok0.index, tok0.line);
context = was;
dec.accept(this);
}
override void visit(const Declaration dec)
{
// Update value of visibility based on this 'dec'.
if (dec.attributeDeclaration)
{
auto attr = dec.attributeDeclaration.attribute;
updateVisibility(attr.attribute.type);
}
// visibility needs to be restored to what it was when changed by
// attribute.
auto was = visibility;
foreach (attr; dec.attributes) {
updateVisibility(attr.attribute.type);
}
dec.accept(this);
visibility = was;
}
override void visit(const ClassDeclaration dec)
{
maketag(dec.name);
// class members default to public
visibility = Visibility.exposed;
acceptInContext(dec, dec.name.text);
}
override void visit(const StructDeclaration dec)
{
if (dec.name == tok!"")
{
dec.accept(this);
return;
}
maketag(dec.name);
// struct members default to public
visibility = Visibility.exposed;
acceptInContext(dec, dec.name.text);
}
override void visit(const InterfaceDeclaration dec)
{
maketag(dec.name);
// interface members default to public
visibility = Visibility.exposed;
acceptInContext(dec, dec.name.text);
}
@ -100,36 +155,32 @@ class EtagsPrinter : ASTVisitor
override void visit(const FunctionDeclaration dec)
{
maketag(dec.name);
bool was = inFunc;
inFunc = true;
auto c = context;
// don't tag declarations in a function like thing
visibility = Visibility.hidden;
acceptInContext(dec, dec.name.text);
inFunc = was;
}
override void visit(const Constructor dec)
{
maketag("this", dec.location, dec.line);
bool was = inFunc;
inFunc = true;
auto c = context;
// don't tag declarations in a function like thing
visibility = Visibility.hidden;
acceptInContext(dec, "this");
inFunc = was;
}
override void visit(const Destructor dec)
{
maketag("~this", dec.index, dec.line);
bool was = inFunc;
inFunc = true;
auto c = context;
// don't tag declarations in a function like thing
visibility = Visibility.hidden;
acceptInContext(dec, "~this");
inFunc = was;
}
override void visit(const EnumDeclaration dec)
{
maketag(dec.name);
// enum members default to public
visibility = Visibility.exposed;
acceptInContext(dec, dec.name.text);
}
@ -141,6 +192,8 @@ class EtagsPrinter : ASTVisitor
return;
}
maketag(dec.name);
// union members default to public
visibility = Visibility.exposed;
acceptInContext(dec, dec.name.text);
}
@ -161,7 +214,7 @@ class EtagsPrinter : ASTVisitor
dec.accept(this);
inUnittest = was;
}
override void visit(const VariableDeclaration dec)
{
foreach (d; dec.declarators)
@ -180,20 +233,57 @@ class EtagsPrinter : ASTVisitor
dec.accept(this);
}
override void visit(const AliasDeclaration dec)
{
// Old style alias
if (dec.identifierList)
{
foreach (i; dec.identifierList.identifiers)
maketag(i);
}
dec.accept(this);
}
override void visit(const AliasInitializer dec)
{
maketag(dec.name);
dec.accept(this);
}
override void visit(const Invariant dec)
{
maketag("invariant", dec.index, dec.line);
}
void acceptInContext(Dec)(Dec dec, string name)
private:
void updateVisibility(IdType type) {
// maybe change visibility based on attribute 'type'
switch (type)
{
case tok!"export":
case tok!"public":
case tok!"protected":
visibility = Visibility.exposed;
break;
case tok!"package":
case tok!"private":
visibility = privateVisibility;
break;
default:
// no change
break;
}
}
void acceptInContext(const ASTNode dec, string name)
{
// nest context before journeying on
auto c = context;
context ~= name ~ ".";
dec.accept(this);
context = c;
context = c;
}
void maketag(Token name)
{
maketag(name.text, name.index, name.line);
@ -201,13 +291,12 @@ class EtagsPrinter : ASTVisitor
void maketag(string text, size_t index, ulong line)
{
// skip declaration in unittests and funcs
if (inUnittest || inFunc) return;
// skip unittests and hidden declarations
if (inUnittest || visibility == Visibility.hidden) return;
// tag is a searchable string from beginning of line
size_t b = index;
while (b > 0 && bytes[--b] != '\n') {}
++b;
while (b > 0 && bytes[b-1] != '\n') --b;
// tag end is one char beyond tag name
size_t e = index + text.length;
@ -223,13 +312,20 @@ class EtagsPrinter : ASTVisitor
line,
b);
}
alias visit = ASTVisitor.visit;
// state
// visibility of declarations (i.e. should we tag)
Visibility visibility = Visibility.exposed;
bool inUnittest;
bool inFunc;
ubyte[] bytes;
string tags;
string context;
}
// inputs
ubyte[] bytes;
string moduleName;
string context;
Visibility privateVisibility = Visibility.hidden;
// ouput
string tags;
}

View File

@ -49,6 +49,7 @@ int run(string[] args)
bool highlight;
bool ctags;
bool etags;
bool etagsAll;
bool help;
bool tokenCount;
bool syntaxCheck;
@ -69,7 +70,7 @@ int run(string[] args)
{
getopt(args, std.getopt.config.caseSensitive, "sloc|l", &sloc,
"highlight", &highlight, "ctags|c", &ctags, "help|h", &help,
"etags|e", &etags,
"etags|e", &etags, "etagsAll", &etagsAll,
"tokenCount|t", &tokenCount, "syntaxCheck|s", &syntaxCheck,
"ast|xml", &ast, "imports|i", &imports, "outline|o", &outline,
"tokenDump", &tokenDump, "styleCheck|S", &styleCheck,
@ -120,9 +121,9 @@ int run(string[] args)
return 0;
}
auto optionCount = count!"a"([sloc, highlight, ctags, etags, tokenCount,
auto optionCount = count!"a"([sloc, highlight, ctags, tokenCount,
syntaxCheck, ast, imports, outline, tokenDump, styleCheck, defaultConfig,
report, symbolName !is null]);
report, symbolName !is null, etags, etagsAll]);
if (optionCount > 1)
{
stderr.writeln("Too many options specified");
@ -177,9 +178,9 @@ int run(string[] args)
{
stdout.printCtags(expandArgs(args));
}
else if (etags)
else if (etags || etagsAll)
{
stdout.printEtags(expandArgs(args));
stdout.printEtags(etagsAll, expandArgs(args));
}
else if (styleCheck)
{
@ -369,6 +370,9 @@ options:
etags information requires a filename, so stdin cannot be used in place
of a filename.
--etagsAll sourceFile
Same as --etags except private and package declarations are tagged too.
--ast | --xml sourceFile
Generates an XML representation of the source files abstract syntax
tree. If no files are specified, input is read from stdin.