commit
f0f0110307
166
src/etags.d
166
src/etags.d
|
@ -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;
|
||||
}
|
||||
|
|
14
src/main.d
14
src/main.d
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue