Don't etag private/package by default

Declarations hidden from other packages (private or package protection)
will not be tagged.  New option --etagAll to tag private/package too.
This commit is contained in:
Dan Olson 2015-05-09 15:23:10 -07:00
parent 5790fc3db0
commit 41b9ef708e
2 changed files with 102 additions and 35 deletions

View File

@ -16,13 +16,24 @@ import std.path;
import std.array; import std.array;
import std.conv; 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. * Prints ETAGS information to the given file.
* Params: * Params:
* outpt = the file that ETAGS info is written to * 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 * fileNames = tags will be generated from these files
*/ */
void printEtags(File output, string[] fileNames) void printEtags(File output, bool tagAll, string[] fileNames)
{ {
LexerConfig config; LexerConfig config;
StringCache cache = StringCache(StringCache.defaultBucketCount); StringCache cache = StringCache(StringCache.defaultBucketCount);
@ -34,16 +45,17 @@ void printEtags(File output, string[] fileNames)
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 EtagsPrinter; auto printer = new EtagsPrinter;
printer.moduleName = m.moduleFullName(fileName); printer.moduleName = m.moduleFullName(fileName);
version(UseModuleContext)
// I think I like this
enum useModuleContext = true;
if (useModuleContext)
printer.context = printer.moduleName ~ "."; printer.context = printer.moduleName ~ ".";
printer.privateVisibility = tagAll?
Visibility.exposed :
Visibility.hidden;
printer.bytes = bytes.sansBOM; printer.bytes = bytes.sansBOM;
printer.visit(m); printer.visit(m);
output.writef("\f\n%s,%u\n", fileName, printer.tags.length); output.writef("\f\n%s,%u\n", fileName, printer.tags.length);
printer.tags.copy(output.lockingTextWriter); printer.tags.copy(output.lockingTextWriter);
} }
@ -51,6 +63,8 @@ void printEtags(File output, string[] fileNames)
private: private:
enum Visibility {exposed, hidden}
void doNothing(string, size_t, size_t, string, bool) {} void doNothing(string, size_t, size_t, string, bool) {}
ubyte[] sansBOM(ubyte[] bytes) ubyte[] sansBOM(ubyte[] bytes)
@ -59,7 +73,6 @@ ubyte[] sansBOM(ubyte[] bytes)
return (bytes.length >= 3 && return (bytes.length >= 3 &&
bytes[0] == 0xef && bytes[1] == 0xbb && bytes[2] == 0xbf) bytes[0] == 0xef && bytes[1] == 0xbb && bytes[2] == 0xbf)
? bytes[3 .. $] : bytes; ? bytes[3 .. $] : bytes;
} }
string moduleFullName(Module m, string fileName) string moduleFullName(Module m, string fileName)
@ -72,7 +85,7 @@ string moduleFullName(Module m, string fileName)
return m.moduleDeclaration.moduleName.identifiers.map!(i=>i.text).join("."); return m.moduleDeclaration.moduleName.identifiers.map!(i=>i.text).join(".");
} }
class EtagsPrinter : ASTVisitor final class EtagsPrinter : ASTVisitor
{ {
override void visit(const ModuleDeclaration dec) override void visit(const ModuleDeclaration dec)
{ {
@ -84,9 +97,31 @@ class EtagsPrinter : ASTVisitor
dec.accept(this); 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) override void visit(const ClassDeclaration dec)
{ {
maketag(dec.name); maketag(dec.name);
// class members default to public
visibility = Visibility.exposed;
acceptInContext(dec, dec.name.text); acceptInContext(dec, dec.name.text);
} }
@ -98,12 +133,16 @@ class EtagsPrinter : ASTVisitor
return; return;
} }
maketag(dec.name); maketag(dec.name);
// struct members default to public
visibility = Visibility.exposed;
acceptInContext(dec, dec.name.text); acceptInContext(dec, dec.name.text);
} }
override void visit(const InterfaceDeclaration dec) override void visit(const InterfaceDeclaration dec)
{ {
maketag(dec.name); maketag(dec.name);
// interface members default to public
visibility = Visibility.exposed;
acceptInContext(dec, dec.name.text); acceptInContext(dec, dec.name.text);
} }
@ -116,36 +155,32 @@ class EtagsPrinter : ASTVisitor
override void visit(const FunctionDeclaration dec) override void visit(const FunctionDeclaration dec)
{ {
maketag(dec.name); maketag(dec.name);
bool was = inFunc; // don't tag declarations in a function like thing
inFunc = true; visibility = Visibility.hidden;
auto c = context;
acceptInContext(dec, dec.name.text); acceptInContext(dec, dec.name.text);
inFunc = was;
} }
override void visit(const Constructor dec) override void visit(const Constructor dec)
{ {
maketag("this", dec.location, dec.line); maketag("this", dec.location, dec.line);
bool was = inFunc; // don't tag declarations in a function like thing
inFunc = true; visibility = Visibility.hidden;
auto c = context;
acceptInContext(dec, "this"); acceptInContext(dec, "this");
inFunc = was;
} }
override void visit(const Destructor dec) override void visit(const Destructor dec)
{ {
maketag("~this", dec.index, dec.line); maketag("~this", dec.index, dec.line);
bool was = inFunc; // don't tag declarations in a function like thing
inFunc = true; visibility = Visibility.hidden;
auto c = context;
acceptInContext(dec, "~this"); acceptInContext(dec, "~this");
inFunc = was;
} }
override void visit(const EnumDeclaration dec) override void visit(const EnumDeclaration dec)
{ {
maketag(dec.name); maketag(dec.name);
// enum members default to public
visibility = Visibility.exposed;
acceptInContext(dec, dec.name.text); acceptInContext(dec, dec.name.text);
} }
@ -157,6 +192,8 @@ class EtagsPrinter : ASTVisitor
return; return;
} }
maketag(dec.name); maketag(dec.name);
// union members default to public
visibility = Visibility.exposed;
acceptInContext(dec, dec.name.text); acceptInContext(dec, dec.name.text);
} }
@ -177,7 +214,7 @@ class EtagsPrinter : ASTVisitor
dec.accept(this); dec.accept(this);
inUnittest = was; inUnittest = was;
} }
override void visit(const VariableDeclaration dec) override void visit(const VariableDeclaration dec)
{ {
foreach (d; dec.declarators) foreach (d; dec.declarators)
@ -218,15 +255,35 @@ class EtagsPrinter : ASTVisitor
maketag("invariant", dec.index, dec.line); 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 // nest context before journeying on
auto c = context; auto c = context;
context ~= name ~ "."; context ~= name ~ ".";
dec.accept(this); dec.accept(this);
context = c; context = c;
} }
void maketag(Token name) void maketag(Token name)
{ {
maketag(name.text, name.index, name.line); maketag(name.text, name.index, name.line);
@ -234,8 +291,8 @@ class EtagsPrinter : ASTVisitor
void maketag(string text, size_t index, ulong line) void maketag(string text, size_t index, ulong line)
{ {
// skip declaration in unittests and funcs // skip unittests and hidden declarations
if (inUnittest || inFunc) return; if (inUnittest || visibility == Visibility.hidden) return;
// tag is a searchable string from beginning of line // tag is a searchable string from beginning of line
size_t b = index; size_t b = index;
@ -255,14 +312,20 @@ class EtagsPrinter : ASTVisitor
line, line,
b); b);
} }
alias visit = ASTVisitor.visit; alias visit = ASTVisitor.visit;
// state
// visibility of declarations (i.e. should we tag)
Visibility visibility = Visibility.exposed;
bool inUnittest; bool inUnittest;
bool inFunc;
// inputs
ubyte[] bytes; ubyte[] bytes;
string moduleName; string moduleName;
string tags;
string context; string context;
} Visibility privateVisibility = Visibility.hidden;
// ouput
string tags;
}

View File

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