From 5790fc3db07a39d03e492165d44c47d139283890 Mon Sep 17 00:00:00 2001 From: Dan Olson Date: Wed, 29 Apr 2015 00:25:57 -0700 Subject: [PATCH 1/2] etags: add module, alias, and anonymous struct fix Added tags for module declaration and aliases. Fix tagging for anonymous struct members. Also fixed off-by-one error index a tag is on first line. --- src/etags.d | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/src/etags.d b/src/etags.d index 08497e4..02e2863 100644 --- a/src/etags.d +++ b/src/etags.d @@ -35,11 +35,12 @@ void printEtags(File output, string[] fileNames) auto tokens = getTokensForParser(bytes, config, &cache); Module m = parseModule(tokens.array, fileName, null, &doNothing); auto printer = new EtagsPrinter; + printer.moduleName = m.moduleFullName(fileName); // I think I like this enum useModuleContext = true; if (useModuleContext) - printer.context = m.moduleFullName(fileName) ~ "."; + printer.context = printer.moduleName ~ "."; printer.bytes = bytes.sansBOM; printer.visit(m); @@ -73,6 +74,16 @@ string moduleFullName(Module m, string fileName) 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 ClassDeclaration dec) { maketag(dec.name); @@ -81,6 +92,11 @@ class EtagsPrinter : ASTVisitor override void visit(const StructDeclaration dec) { + if (dec.name == tok!"") + { + dec.accept(this); + return; + } maketag(dec.name); acceptInContext(dec, dec.name.text); } @@ -180,6 +196,23 @@ 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); @@ -206,8 +239,7 @@ class EtagsPrinter : ASTVisitor // 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; @@ -229,6 +261,7 @@ class EtagsPrinter : ASTVisitor bool inUnittest; bool inFunc; ubyte[] bytes; + string moduleName; string tags; string context; } From 41b9ef708e9f8dc0279b8dd11dbe9770db3cd625 Mon Sep 17 00:00:00 2001 From: Dan Olson Date: Sat, 9 May 2015 15:23:10 -0700 Subject: [PATCH 2/2] 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. --- src/etags.d | 123 +++++++++++++++++++++++++++++++++++++++------------- src/main.d | 14 +++--- 2 files changed, 102 insertions(+), 35 deletions(-) diff --git a/src/etags.d b/src/etags.d index 02e2863..4fa8fb7 100644 --- a/src/etags.d +++ b/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,16 +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; printer.moduleName = m.moduleFullName(fileName); - - // I think I like this - enum useModuleContext = true; - if (useModuleContext) + 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); } @@ -51,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) @@ -59,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) @@ -72,7 +85,7 @@ 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) { @@ -84,9 +97,31 @@ class EtagsPrinter : ASTVisitor 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); } @@ -98,12 +133,16 @@ class EtagsPrinter : ASTVisitor 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); } @@ -116,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); } @@ -157,6 +192,8 @@ class EtagsPrinter : ASTVisitor return; } maketag(dec.name); + // union members default to public + visibility = Visibility.exposed; acceptInContext(dec, dec.name.text); } @@ -177,7 +214,7 @@ class EtagsPrinter : ASTVisitor dec.accept(this); inUnittest = was; } - + override void visit(const VariableDeclaration dec) { foreach (d; dec.declarators) @@ -218,15 +255,35 @@ class EtagsPrinter : ASTVisitor 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); @@ -234,8 +291,8 @@ 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; @@ -255,14 +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; + + // inputs ubyte[] bytes; string moduleName; - string tags; string context; -} + Visibility privateVisibility = Visibility.hidden; + // ouput + string tags; +} diff --git a/src/main.d b/src/main.d index d603c0a..cea527b 100644 --- a/src/main.d +++ b/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.