From a1cf475fcf2c81420bfa90307585ec645cfb4dc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Techet?= Date: Sat, 6 Apr 2019 04:14:30 +0200 Subject: [PATCH] Sync ctags with upstream so that most parsers can be copied from uctags (#2018) * Use latest version of htable * Use latest version of mio * Use latest version of objpool * Use latest version of ptrarray * Use latest version of vstring This also requires adding trashbox.c/h which is now used by vstring and inline macros from inline.h. * Rename fieldSpec to fieldDefinition See b56bd065123d69087acd6f202499d71a86a7ea7a upstream. * Rename kindOption to kindDefinition See e112e8ab6e0933b5bd7922e0dfb969b1f28c60fa upstream * Rename kinds field in parserDefinition to kindTable See 09ae690face8b5cde940e2d7cf40f8860381067b upstream. * Rename structure fields about field in parserDefinition See a739fa5fb790bc349a66b2bee0bf42cf289994e8 upstream. * Use kindIndex instead of kindDefinition This patch replaces kindDefinition related entries from sTagEntryInfo with kindIndex so kinds are referenced indirectly using the index. For more info please refer to commits: 16a2541c0698bd8ee03c1be8172ef3191f6e695a f92e6bf2aeb21fd6b04756487f98d0eefa16d9ce Some other changes had to be made to make the sources compile (without bringing all the diffs from upstream). At some places, which aren't used by Geany, only stub implementations have been created. In particular, the regex parser has been disabled (for now?) because its current implementation doesn't allow accessing kindDefinitions using index and allowing this would require big changes in its implementation. The affected parsers are Cobol, ActionScript and HTML. For HTML we can use the token-based parser from upstream, and we should consider whether Cobol and ActionScript are worth the effort to maintain a separate regex implementation using GRegex (IMO these languages are dead enough not to justify the extra effort). The patch also disables tests for languages using regex parsers. * Rename roleDesc to roleDefinition See 1345725842c196cc0523ff60231192bcd588961b upstream. Since we don't care about roles in Geany, we don't have to do the additional stuff the upstream patch does. * Add XTAG_ANONYMOUS used by jscript See 0e4c5d4a0461bc8d9616fe3b97d75b91d014246e upstream. * Include stdint.h in entry.h * Don't use hash value as an Anonymous field identifier Instead of something like "Anonymous0ab283cd9402" use sequential integer values like "Anonymous1". * Call anonReset in main part See 3c91b1ea509df238feb86c9cbd552b621e462653 upstream. * Use upstream javascript parser * Use upstream css parser * Create correctly sized MIO for 0 size See https://github.com/universal-ctags/ctags/pull/1951 * Always enable promise API and subparsers for Geany * Support subparsers in Geany and add HTML parser demonstrating this feature This feature requires several changes: 1. Propagating language of the tag from ctags to Geany so we know whether the tag comes from a master parser or a subparser. 2. We need to address the problem that tag types from a subparsers can clash with tag types from master parsers or other subparsers used by the master parser. For instance, HTML and both its css and javascript subparsers use tm_tag_class_t but HTML uses it for

headings, and css and javascript for classes. Representing all of them using tm_tag_class_t would lead to complete mess where all of these types would for instance be listed in the same branch of the tree in the sidebar. To avoid this problem, this patch adds another mapping for subparsers where each tag type can be mapped to another tag type (which isn't used neither by master parser or other subparsers). To avoid unwanted clashes with other parsers, only tags explicitly mentioned in such mappings are added to tag manager; other subparser tags are discarded. For HTML this patch introduces mapping only for tm_tag_function_t (which in this case maps to the same type) to mimick the previous HTML parser behavior but other javascript and css tag types can be added this way in the future too. 3. Since in most of the code Geany and tag manager assume that tags from one file use the same language, subparser's tags are modified to have the same language like the master parser. 4. HTML parser itself was copied from upstream without any modifications. Tests were fixed as the parser now correctly ignores comments. * Rename truncateLine field of tagEntryInfo See 0e70b22791877322598f03ecbe3eb26a6b661001 upstream. Needed for Fortran parser. * Add dummy mbcs.h and trace.h Included by javascript parser. * Introduce an accessor to `locate' field of `Option' See fb5ef68859f71ff2949f1d9a7cab7515f523532f upstream. Needed for Fortran. * Add numarray.c/h Needed by various parsers. * Add getLanguageForFilename() and getLanguageForCommand() See 416c5e6b8807feaec318d7f8addbb4107370c187 334e072f9d6d9954ebd3eb89bbceb252c20ae9dd upstream. Needed for Sh parser. * txt2tags: Fix scope separator definition and re-enable tests * Rename rest.c to rst.c to match upstream filename * Use upstream asciidoc and rst parsers * Add asciidoc and rst unit tests * Rename conf.c to iniconf.c to match upstream filename * Add tests of conf, diff, md parsers from universal ctags * Add more ctags unit tests This patch adds unit tests for: nsis, docbook, haskell, haxe, abaqus, vala, abc. The only missing unit tests are for GLSL and Ferite parsers which however share the implementation with the C parser and should be reasonably well covered by other C-like language tests. The tests were put together from various tutorials and help of the languages in order to cover the tags these parsers generate. No guarantee they'd compile with real parsers. * Rename latex.c to tex.c to match upstream filename * Rename entry points of parsers to match upstream names * Initialize trashbox * Add newline to the end of file --- ctags/Makefile.am | 13 +- ctags/main/ctags-api.c | 13 +- ctags/main/ctags-api.h | 1 + ctags/main/dependency.c | 12 +- ctags/main/entry.c | 86 +- ctags/main/entry.h | 25 +- ctags/main/field.c | 63 +- ctags/main/field.h | 6 +- ctags/main/htable.c | 71 +- ctags/main/htable.h | 24 +- ctags/main/inline.h | 26 + ctags/main/kind.c | 27 +- ctags/main/kind.h | 27 +- ctags/main/lcpp.c | 16 +- ctags/main/lcpp.h | 2 +- ctags/main/lregex.c | 34 +- ctags/main/lxcmd.c | 36 +- ctags/main/lxpath.c | 15 +- ctags/main/mbcs.h | 1 + ctags/main/mio.c | 32 +- ctags/main/mio.h | 2 +- ctags/main/numarray.c | 175 +++ ctags/main/numarray.h | 48 + ctags/main/objpool.c | 7 +- ctags/main/objpool.h | 5 +- ctags/main/options.c | 11 +- ctags/main/options.h | 5 +- ctags/main/parse.c | 215 +-- ctags/main/parse.h | 26 +- ctags/main/parsers.h | 8 +- ctags/main/promise.c | 4 + ctags/main/ptrarray.c | 28 + ctags/main/ptrarray.h | 6 +- ctags/main/read.c | 12 +- ctags/main/read.h | 6 +- ctags/main/trace.h | 1 + ctags/main/trashbox.c | 246 ++++ ctags/main/trashbox.h | 41 + ctags/main/types.h | 8 +- ctags/main/vstring.c | 59 +- ctags/main/vstring.h | 31 +- ctags/main/xtag.c | 2 + ctags/main/xtag.h | 1 + ctags/parsers/abaqus.c | 6 +- ctags/parsers/abc.c | 6 +- ctags/parsers/asciidoc.c | 276 +++- ctags/parsers/asm.c | 12 +- ctags/parsers/basic.c | 14 +- ctags/parsers/c.c | 50 +- ctags/parsers/css.c | 47 +- ctags/parsers/diff.c | 6 +- ctags/parsers/docbook.c | 6 +- ctags/parsers/erlang.c | 12 +- ctags/parsers/fortran.c | 14 +- ctags/parsers/go.c | 8 +- ctags/parsers/haskell.c | 8 +- ctags/parsers/haxe.c | 16 +- ctags/parsers/html.c | 532 ++++++- ctags/parsers/{conf.c => iniconf.c} | 10 +- ctags/parsers/jscript.c | 1221 +++++++++++++---- ctags/parsers/json.c | 8 +- ctags/parsers/lua.c | 6 +- ctags/parsers/make.c | 8 +- ctags/parsers/markdown.c | 6 +- ctags/parsers/matlab.c | 10 +- ctags/parsers/nsis.c | 10 +- ctags/parsers/objc.c | 8 +- ctags/parsers/pascal.c | 10 +- ctags/parsers/perl.c | 12 +- ctags/parsers/php.c | 12 +- ctags/parsers/powershell.c | 8 +- ctags/parsers/python.c | 28 +- ctags/parsers/r.c | 6 +- ctags/parsers/rest.c | 207 --- ctags/parsers/rst.c | 379 +++++ ctags/parsers/ruby.c | 14 +- ctags/parsers/rust.c | 8 +- ctags/parsers/sh.c | 6 +- ctags/parsers/sql.c | 8 +- ctags/parsers/tcl.c | 6 +- ctags/parsers/{latex.c => tex.c} | 12 +- ctags/parsers/txt2tags.c | 9 +- ctags/parsers/verilog.c | 8 +- ctags/parsers/vhdl.c | 10 +- src/tagmanager/tm_parser.c | 108 +- src/tagmanager/tm_parser.h | 2 + src/tagmanager/tm_source_file.c | 10 +- tests/ctags/Makefile.am | 16 +- tests/ctags/complex-return.js.tags | 2 + tests/ctags/geany.nsi | 470 +++++++ tests/ctags/geany.nsi.tags | 21 + .../js-class-related-unterminated.js.tags | 3 +- tests/ctags/js-let.js.tags | 2 +- tests/ctags/js-string-continuation.js.tags | 2 +- tests/ctags/jsFunc_tutorial.js.tags | 10 +- tests/ctags/simple.abc | 78 ++ tests/ctags/simple.abc.tags | 7 + tests/ctags/simple.asciidoc | 59 + tests/ctags/simple.asciidoc.tags | 13 + tests/ctags/simple.conf | 33 + tests/ctags/simple.conf.tags | 24 + tests/ctags/simple.diff | 170 +++ tests/ctags/simple.diff.tags | 5 + tests/ctags/simple.docbook | 45 + tests/ctags/simple.docbook.tags | 7 + tests/ctags/simple.hs | 8 + tests/ctags/simple.hs.tags | 9 + tests/ctags/simple.html.tags | 4 +- tests/ctags/simple.hx | 23 + tests/ctags/simple.hx.tags | 10 + tests/ctags/simple.inp | 106 ++ tests/ctags/simple.inp.tags | 4 + tests/ctags/simple.js.tags | 3 +- tests/ctags/simple.md | 51 + tests/ctags/simple.md.tags | 28 + tests/ctags/simple.rst | 39 + tests/ctags/simple.rst.tags | 9 + tests/ctags/simple.vala | 46 + tests/ctags/simple.vala.tags | 22 + 119 files changed, 4789 insertions(+), 1180 deletions(-) create mode 100644 ctags/main/inline.h create mode 100644 ctags/main/mbcs.h create mode 100644 ctags/main/numarray.c create mode 100644 ctags/main/numarray.h create mode 100644 ctags/main/trace.h create mode 100644 ctags/main/trashbox.c create mode 100644 ctags/main/trashbox.h rename ctags/parsers/{conf.c => iniconf.c} (91%) delete mode 100644 ctags/parsers/rest.c create mode 100644 ctags/parsers/rst.c rename ctags/parsers/{latex.c => tex.c} (95%) create mode 100644 tests/ctags/geany.nsi create mode 100644 tests/ctags/geany.nsi.tags create mode 100644 tests/ctags/simple.abc create mode 100644 tests/ctags/simple.abc.tags create mode 100644 tests/ctags/simple.asciidoc create mode 100644 tests/ctags/simple.asciidoc.tags create mode 100644 tests/ctags/simple.conf create mode 100644 tests/ctags/simple.conf.tags create mode 100644 tests/ctags/simple.diff create mode 100644 tests/ctags/simple.diff.tags create mode 100644 tests/ctags/simple.docbook create mode 100644 tests/ctags/simple.docbook.tags create mode 100644 tests/ctags/simple.hs create mode 100644 tests/ctags/simple.hs.tags create mode 100644 tests/ctags/simple.hx create mode 100644 tests/ctags/simple.hx.tags create mode 100644 tests/ctags/simple.inp create mode 100644 tests/ctags/simple.inp.tags create mode 100644 tests/ctags/simple.md create mode 100644 tests/ctags/simple.md.tags create mode 100644 tests/ctags/simple.rst create mode 100644 tests/ctags/simple.rst.tags create mode 100644 tests/ctags/simple.vala create mode 100644 tests/ctags/simple.vala.tags diff --git a/ctags/Makefile.am b/ctags/Makefile.am index f71f94b9..806bff00 100644 --- a/ctags/Makefile.am +++ b/ctags/Makefile.am @@ -17,7 +17,7 @@ parsers = \ parsers/basic.c \ parsers/c.c \ parsers/cobol.c \ - parsers/conf.c \ + parsers/iniconf.c \ parsers/css.c \ parsers/diff.c \ parsers/docbook.c \ @@ -29,7 +29,6 @@ parsers = \ parsers/html.c \ parsers/jscript.c \ parsers/json.c \ - parsers/latex.c \ parsers/lua.c \ parsers/make.c \ parsers/markdown.c \ @@ -42,12 +41,13 @@ parsers = \ parsers/powershell.c \ parsers/python.c \ parsers/r.c \ - parsers/rest.c \ + parsers/rst.c \ parsers/ruby.c \ parsers/rust.c \ parsers/sh.c \ parsers/sql.c \ parsers/tcl.c \ + parsers/tex.c \ parsers/txt2tags.c \ parsers/verilog.c \ parsers/vhdl.c @@ -77,6 +77,7 @@ libctags_la_SOURCES = \ main/general.h \ main/htable.c \ main/htable.h \ + main/inline.h \ main/keyword.c \ main/keyword.h \ main/kind.c \ @@ -88,10 +89,13 @@ libctags_la_SOURCES = \ main/lxpath.c \ main/main.c \ main/main.h \ + main/mbcs.h \ main/mio.c \ main/mio.h \ main/nestlevel.c \ main/nestlevel.h \ + main/numarray.c \ + main/numarray.h \ main/objpool.c \ main/objpool.h \ main/options.c \ @@ -121,6 +125,9 @@ libctags_la_SOURCES = \ main/sort.h \ main/strlist.c \ main/strlist.h \ + main/trace.h \ + main/trashbox.c \ + main/trashbox.h \ main/types.h \ main/vstring.c \ main/vstring.h \ diff --git a/ctags/main/ctags-api.c b/ctags/main/ctags-api.c index a86c4636..20b6898e 100644 --- a/ctags/main/ctags-api.c +++ b/ctags/main/ctags-api.c @@ -18,6 +18,7 @@ #include "output.h" #include "parse.h" #include "options.h" +#include "trashbox.h" #include #include @@ -51,6 +52,8 @@ extern void ctagsInit(void) initializeParsing (); initOptions (); + initDefaultTrashBox (); + /* make sure all parsers are initialized */ initializeParser (LANG_AUTO); } @@ -92,7 +95,7 @@ extern const char *ctagsGetLangKinds(int lang) static char kinds[257]; for (i = 0; i < def->kindCount; i++) - kinds[i] = def->kinds[i].letter; + kinds[i] = def->kindTable[i].letter; kinds[i] = '\0'; return kinds; @@ -106,8 +109,8 @@ extern const char *ctagsGetKindName(char kind, int lang) for (i = 0; i < def->kindCount; i++) { - if (def->kinds[i].letter == kind) - return def->kinds[i].name; + if (def->kindTable[i].letter == kind) + return def->kindTable[i].name; } return "unknown"; } @@ -120,8 +123,8 @@ extern char ctagsGetKindFromName(const char *name, int lang) for (i = 0; i < def->kindCount; i++) { - if (strcmp(def->kinds[i].name, name) == 0) - return def->kinds[i].letter; + if (strcmp(def->kindTable[i].name, name) == 0) + return def->kindTable[i].letter; } return '-'; } diff --git a/ctags/main/ctags-api.h b/ctags/main/ctags-api.h index 816f9980..f973ef07 100644 --- a/ctags/main/ctags-api.h +++ b/ctags/main/ctags-api.h @@ -27,6 +27,7 @@ typedef struct { char kindLetter; bool isFileScope; unsigned long lineNumber; + int lang; } ctagsTag; /* Callback invoked for every tag found by the parser. The return value is diff --git a/ctags/main/dependency.c b/ctags/main/dependency.c index 0b57e89a..a78b5535 100644 --- a/ctags/main/dependency.c +++ b/ctags/main/dependency.c @@ -18,9 +18,9 @@ #include -static void linkKinds (kindOption *masterKind, kindOption *slaveKind) +static void linkKinds (kindDefinition *masterKind, kindDefinition *slaveKind) { - kindOption *tail; + kindDefinition *tail; slaveKind->master = masterKind; @@ -39,16 +39,16 @@ static void linkKindDependency (parserDefinition *const masterParser, parserDefinition *const slaveParser) { unsigned int k_slave, k_master; - kindOption *kind_slave, *kind_master; + kindDefinition *kind_slave, *kind_master; for (k_slave = 0; k_slave < slaveParser->kindCount; k_slave++) { - if (slaveParser->kinds [k_slave].syncWith == LANG_AUTO) + if (slaveParser->kindTable [k_slave].syncWith == LANG_AUTO) { - kind_slave = slaveParser->kinds + k_slave; + kind_slave = slaveParser->kindTable + k_slave; for (k_master = 0; k_master < masterParser->kindCount; k_master++) { - kind_master = masterParser->kinds + k_master; + kind_master = masterParser->kindTable + k_master; if ((kind_slave->letter == kind_master->letter) && (strcmp (kind_slave->name, kind_master->name) == 0)) { diff --git a/ctags/main/entry.c b/ctags/main/entry.c index 8d2c6421..b2ea525e 100644 --- a/ctags/main/entry.c +++ b/ctags/main/entry.c @@ -188,7 +188,7 @@ extern void makeFileTag (const char *const fileName) if (xtag != XTAG_UNKNOWN) { tagEntryInfo tag; - kindOption *kind; + kindDefinition *kind; kind = getInputLanguageFileKind(); Assert (kind); @@ -196,7 +196,7 @@ extern void makeFileTag (const char *const fileName) /* TODO: you can return here if enabled == false. */ - initTagEntry (&tag, baseFilename (fileName), kind); + initTagEntry (&tag, baseFilename (fileName), KIND_FILE_INDEX); tag.isFileEntry = true; tag.lineNumberEntry = true; @@ -736,7 +736,8 @@ extern void truncateTagLine ( static char* getFullQualifiedScopeNameFromCorkQueue (const tagEntryInfo * inner_scope) { - const kindOption *kind = NULL; + int kindIndex = KIND_GHOST_INDEX; + langType lang; const tagEntryInfo *scope = inner_scope; stringList *queue = stringListNew (); vString *v; @@ -748,16 +749,17 @@ static char* getFullQualifiedScopeNameFromCorkQueue (const tagEntryInfo * inner_ { if (!scope->placeholder) { - if (kind) + if (kindIndex != KIND_GHOST_INDEX) { - sep = scopeSeparatorFor (kind, scope->kind->letter); + sep = scopeSeparatorFor (lang, kindIndex, scope->kindIndex); v = vStringNewInit (sep); stringListAdd (queue, v); } /* TODO: scope field of SCOPE can be used for optimization. */ v = vStringNewInit (scope->name); stringListAdd (queue, v); - kind = scope->kind; + kindIndex = scope->kindIndex; + lang = scope->langType; } scope = getEntryInCorkQueue (scope->extensionFields.scopeIndex); } @@ -783,7 +785,7 @@ extern void getTagScopeInformation (tagEntryInfo *const tag, if (name) *name = NULL; - if (tag->extensionFields.scopeKind == NULL + if (tag->extensionFields.scopeKindIndex == KIND_GHOST_INDEX && tag->extensionFields.scopeName == NULL && tag->extensionFields.scopeIndex != CORK_NIL && TagFile.corkQueue.count > 0) @@ -796,15 +798,23 @@ extern void getTagScopeInformation (tagEntryInfo *const tag, Assert (full_qualified_scope_name); /* Make the information reusable to generate full qualified entry, and xformat output*/ - tag->extensionFields.scopeKind = scope->kind; + tag->extensionFields.scopeLangType = scope->langType; + tag->extensionFields.scopeKindIndex = scope->kindIndex; tag->extensionFields.scopeName = full_qualified_scope_name; } - if (tag->extensionFields.scopeKind != NULL && + if (tag->extensionFields.scopeKindIndex != KIND_GHOST_INDEX && tag->extensionFields.scopeName != NULL) { if (kind) - *kind = tag->extensionFields.scopeKind->name; + { + langType lang = (tag->extensionFields.scopeLangType == LANG_AUTO) + ? tag->langType + : tag->extensionFields.scopeLangType; + kindDefinition *kdef = getLanguageKind (lang, + tag->extensionFields.scopeKindIndex); + *kind = kdef->name; + } if (name) *name = tag->extensionFields.scopeName; } @@ -831,21 +841,21 @@ extern int makePatternStringCommon (const tagEntryInfo *const tag, static vString *cached_pattern; static MIOPos cached_location; if (TagFile.patternCacheValid - && (! tag->truncateLine) + && (! tag->truncateLineAfterTag) && (memcmp (&tag->filePosition, &cached_location, sizeof(MIOPos)) == 0)) return puts_func (vStringValue (cached_pattern), output); line = readLineFromBypass (TagFile.vLine, tag->filePosition, NULL); if (line == NULL) error (FATAL, "could not read tag line from %s at line %lu", getInputFileName (),tag->lineNumber); - if (tag->truncateLine) + if (tag->truncateLineAfterTag) truncateTagLine (line, tag->name, false); line_len = strlen (line); searchChar = Option.backward ? '?' : '/'; terminator = (bool) (line [line_len - 1] == '\n') ? "$": ""; - if (!tag->truncateLine) + if (!tag->truncateLineAfterTag) { making_cache = true; cached_pattern = vStringNewOrClear (cached_pattern); @@ -960,8 +970,6 @@ static void recordTagEntryInQueue (const tagEntryInfo *const tag, tagEntryInfo* slot->extensionFields.xpath = eStrdup (slot->extensionFields.xpath); #endif - if (slot->sourceLanguage) - slot->sourceLanguage = eStrdup (slot->sourceLanguage); if (slot->sourceFileName) slot->sourceFileName = eStrdup (slot->sourceFileName); @@ -999,8 +1007,6 @@ static void clearTagEntryInQueue (tagEntryInfo* slot) eFree ((char *)slot->extensionFields.implementation); if (slot->extensionFields.inheritance) eFree ((char *)slot->extensionFields.inheritance); - if (slot->extensionFields.scopeKind) - slot->extensionFields.scopeKind = NULL; if (slot->extensionFields.scopeName) eFree ((char *)slot->extensionFields.scopeName); if (slot->extensionFields.signature) @@ -1018,8 +1024,6 @@ static void clearTagEntryInQueue (tagEntryInfo* slot) eFree ((char *)slot->extensionFields.xpath); #endif - if (slot->sourceLanguage) - eFree ((char *)slot->sourceLanguage); if (slot->sourceFileName) eFree ((char *)slot->sourceFileName); @@ -1098,9 +1102,10 @@ static void initCtagsTag(ctagsTag *tag, const tagEntryInfo *info) tag->varType = info->extensionFields.varType; tag->access = info->extensionFields.access; tag->implementation = info->extensionFields.implementation; - tag->kindLetter = info->kind->letter; + tag->kindLetter = getLanguageKind(info->langType, info->kindIndex)->letter; tag->isFileScope = info->isFileScope; tag->lineNumber = info->lineNumber; + tag->lang = info->langType; } #endif @@ -1194,7 +1199,7 @@ extern void uncorkTagFile(void) writeTagEntry (tag); if (doesInputLanguageRequestAutomaticFQTag () && isXtagEnabled (XTAG_QUALIFIED_TAGS) - && tag->extensionFields.scopeKind + && (tag->extensionFields.scopeKindIndex != KIND_GHOST_INDEX) && tag->extensionFields.scopeName && tag->extensionFields.scopeIndex) makeQualifiedTagEntry (tag); @@ -1281,15 +1286,15 @@ extern int makeQualifiedTagEntry (const tagEntryInfo *const e) if (e->extensionFields.scopeName) { vStringCatS (fqn, e->extensionFields.scopeName); - xk = e->extensionFields.scopeKind->letter; - sep = scopeSeparatorFor (e->kind, xk); + xk = e->extensionFields.scopeKindIndex; + sep = scopeSeparatorFor (e->langType, e->kindIndex, xk); vStringCatS (fqn, sep); } else { /* This is an top level item. prepend a root separator if the kind of the item has. */ - sep = scopeSeparatorFor (e->kind, KIND_NULL); + sep = scopeSeparatorFor (e->langType, e->kindIndex, KIND_GHOST_INDEX); if (sep == NULL) { /* No root separator. The name of the @@ -1316,59 +1321,64 @@ extern int makeQualifiedTagEntry (const tagEntryInfo *const e) } extern void initTagEntry (tagEntryInfo *const e, const char *const name, - const kindOption *kind) + int kindIndex) { initTagEntryFull(e, name, getInputLineNumber (), - getInputLanguageName (), + getInputLanguage (), getInputFilePosition (), getInputFileTagPath (), - kind, + kindIndex, ROLE_INDEX_DEFINITION, getSourceFileTagPath(), - getSourceLanguageName(), + getSourceLanguage(), getSourceLineNumber() - getInputLineNumber ()); } extern void initRefTagEntry (tagEntryInfo *const e, const char *const name, - const kindOption *kind, int roleIndex) + int kindIndex, int roleIndex) { initTagEntryFull(e, name, getInputLineNumber (), - getInputLanguageName (), + getInputLanguage (), getInputFilePosition (), getInputFileTagPath (), - kind, + kindIndex, roleIndex, getSourceFileTagPath(), - getSourceLanguageName(), + getSourceLanguage(), getSourceLineNumber() - getInputLineNumber ()); } extern void initTagEntryFull (tagEntryInfo *const e, const char *const name, unsigned long lineNumber, - const char* language, + langType langType_, MIOPos filePosition, const char *inputFileName, - const kindOption *kind, + int kindIndex, int roleIndex, const char *sourceFileName, - const char* sourceLanguage, + langType sourceLangType, long sourceLineNumberDifference) { int i; + Assert (getInputFileName() != NULL); memset (e, 0, sizeof (tagEntryInfo)); e->lineNumberEntry = (bool) (Option.locate == EX_LINENUM); e->lineNumber = lineNumber; e->boundaryInfo = getNestedInputBoundaryInfo (lineNumber); - e->language = language; + e->langType = langType_; e->filePosition = filePosition; e->inputFileName = inputFileName; e->name = name; + e->extensionFields.scopeLangType = LANG_AUTO; + e->extensionFields.scopeKindIndex = KIND_GHOST_INDEX; e->extensionFields.scopeIndex = CORK_NIL; - e->kind = kind; + + Assert (kindIndex < 0 || kindIndex < (int)countLanguageKinds(langType_)); + e->kindIndex = kindIndex; Assert (roleIndex >= ROLE_INDEX_DEFINITION); Assert (kind == NULL || roleIndex < kind->nRoles); @@ -1376,7 +1386,7 @@ extern void initTagEntryFull (tagEntryInfo *const e, const char *const name, if (roleIndex > ROLE_INDEX_DEFINITION) markTagExtraBit (e, XTAG_REFERENCE_TAGS); - e->sourceLanguage = sourceLanguage; + e->sourceLangType = sourceLangType; e->sourceFileName = sourceFileName; e->sourceLineNumberDifference = sourceLineNumberDifference; diff --git a/ctags/main/entry.h b/ctags/main/entry.h index 6d12878e..c938c43f 100644 --- a/ctags/main/entry.h +++ b/ctags/main/entry.h @@ -15,6 +15,7 @@ #include "general.h" /* must always come first */ #include "types.h" +#include #include #include "field.h" @@ -45,7 +46,7 @@ struct sTagEntryInfo { unsigned int lineNumberEntry:1; /* pattern or line number entry */ unsigned int isFileScope :1; /* is tag visible only within input file? */ unsigned int isFileEntry :1; /* is this just an entry for a file name? */ - unsigned int truncateLine :1; /* truncate tag line at end of tag name? */ + unsigned int truncateLineAfterTag :1; /* truncate tag line at end of tag name? */ unsigned int placeholder :1; /* This is just a part of scope context. Put this entry to cork queue but don't print it to tags file. */ @@ -55,10 +56,10 @@ struct sTagEntryInfo { * (may be NULL if not present) *//* */ unsigned int boundaryInfo; /* info about nested input stream */ MIOPos filePosition; /* file position of line containing tag */ - const char* language; /* language of input file */ + langType langType; /* language of input file */ const char *inputFileName; /* name of input file */ const char *name; /* name of the tag */ - const kindOption *kind; /* kind descriptor */ + int kindIndex; /* kind descriptor */ unsigned char extra[ ((XTAG_COUNT) / 8) + 1 ]; struct { @@ -67,7 +68,11 @@ struct sTagEntryInfo { const char* implementation; const char* inheritance; - const kindOption* scopeKind; + /* Which scopeKindIndex belong to. If the value is LANG_AUTO, + the value for langType field of this structure is used as default value. + LANG_AUTO is set automatically in initTagEntryInfo. */ + langType scopeLangType; + int scopeKindIndex; const char* scopeName; int scopeIndex; /* cork queue entry for upper scope tag. This field is meaningful if the value @@ -99,7 +104,7 @@ struct sTagEntryInfo { /* Following source* fields are used only when #line is found in input and --line-directive is given in ctags command line. */ - const char* sourceLanguage; + langType sourceLangType; const char *sourceFileName; unsigned long sourceLineNumberDifference; }; @@ -121,18 +126,18 @@ extern void setupWriter (void); extern void teardownWriter (const char *inputFilename); extern int makeTagEntry (const tagEntryInfo *const tag); extern void initTagEntry (tagEntryInfo *const e, const char *const name, - const kindOption *kind); + int kindIndex); extern void initRefTagEntry (tagEntryInfo *const e, const char *const name, - const kindOption *kind, int roleIndex); + int kindIndex, int roleIndex); extern void initTagEntryFull (tagEntryInfo *const e, const char *const name, unsigned long lineNumber, - const char* language, + langType langType_, MIOPos filePosition, const char *inputFileName, - const kindOption *kind, + int kindIndex, int roleIndex, const char *sourceFileName, - const char* sourceLanguage, + langType sourceLangType, long sourceLineNumberDifference); extern int makeQualifiedTagEntry (const tagEntryInfo *const e); diff --git a/ctags/main/field.c b/ctags/main/field.c index ae2bf6f3..bb26ad65 100644 --- a/ctags/main/field.c +++ b/ctags/main/field.c @@ -27,7 +27,7 @@ struct sFieldDesc { - fieldSpec *spec; + fieldDefinition *spec; unsigned int fixed: 1; /* fields which cannot be disabled. */ vString *buffer; const char* nameWithPrefix; @@ -84,7 +84,7 @@ static bool isEndFieldAvailable (const tagEntryInfo *const tag); #define WITH_DEFUALT_VALUE(str) ((str)?(str):"-") -static fieldSpec fieldSpecsFixed [] = { +static fieldDefinition fieldDefinitionsFixed [] = { /* FIXED FIELDS */ DEFINE_FIELD_SPEC ('N', "name", true, "tag name (fixed field)", @@ -97,7 +97,7 @@ static fieldSpec fieldSpecsFixed [] = { renderFieldPattern), }; -static fieldSpec fieldSpecsExuberant [] = { +static fieldDefinition fieldDefinitionsExuberant [] = { DEFINE_FIELD_SPEC ('C', "compact", false, "compact input line (fixed field, only used in -x option)", renderFieldCompactInputLine), @@ -143,7 +143,7 @@ static fieldSpec fieldSpecsExuberant [] = { renderFieldKindName), }; -static fieldSpec fieldSpecsUniversal [] = { +static fieldDefinition fieldDefinitionsUniversal [] = { DEFINE_FIELD_SPEC_FULL ('r', "role", false, "Role", renderFieldRole, isRoleFieldAvailable), @@ -182,43 +182,43 @@ extern void initFieldDescs (void) Assert (fieldDescs == NULL); fieldDescAllocated - = ARRAY_SIZE (fieldSpecsFixed) - + ARRAY_SIZE (fieldSpecsExuberant) - + ARRAY_SIZE (fieldSpecsUniversal); + = ARRAY_SIZE (fieldDefinitionsFixed) + + ARRAY_SIZE (fieldDefinitionsExuberant) + + ARRAY_SIZE (fieldDefinitionsUniversal); fieldDescs = xMalloc (fieldDescAllocated, fieldDesc); fieldDescUsed = 0; - for (i = 0; i < ARRAY_SIZE (fieldSpecsFixed); i++) + for (i = 0; i < ARRAY_SIZE (fieldDefinitionsFixed); i++) { fdesc = fieldDescs + i + fieldDescUsed; - fdesc->spec = fieldSpecsFixed + i; + fdesc->spec = fieldDefinitionsFixed + i; fdesc->fixed = 1; fdesc->buffer = NULL; fdesc->nameWithPrefix = fdesc->spec->name; fdesc->language = LANG_IGNORE; fdesc->sibling = FIELD_UNKNOWN; } - fieldDescUsed += ARRAY_SIZE (fieldSpecsFixed); + fieldDescUsed += ARRAY_SIZE (fieldDefinitionsFixed); - for (i = 0; i < ARRAY_SIZE (fieldSpecsExuberant); i++) + for (i = 0; i < ARRAY_SIZE (fieldDefinitionsExuberant); i++) { fdesc = fieldDescs + i + fieldDescUsed; - fdesc->spec = fieldSpecsExuberant +i; + fdesc->spec = fieldDefinitionsExuberant +i; fdesc->fixed = 0; fdesc->buffer = NULL; fdesc->nameWithPrefix = fdesc->spec->name; fdesc->language = LANG_IGNORE; fdesc->sibling = FIELD_UNKNOWN; } - fieldDescUsed += ARRAY_SIZE (fieldSpecsExuberant); + fieldDescUsed += ARRAY_SIZE (fieldDefinitionsExuberant); - for (i = 0; i < ARRAY_SIZE (fieldSpecsUniversal); i++) + for (i = 0; i < ARRAY_SIZE (fieldDefinitionsUniversal); i++) { char *nameWithPrefix; fdesc = fieldDescs + i + fieldDescUsed; - fdesc->spec = fieldSpecsUniversal + i; + fdesc->spec = fieldDefinitionsUniversal + i; fdesc->fixed = 0; fdesc->buffer = NULL; @@ -235,7 +235,7 @@ extern void initFieldDescs (void) fdesc->language = LANG_IGNORE; fdesc->sibling = FIELD_UNKNOWN; } - fieldDescUsed += ARRAY_SIZE (fieldSpecsUniversal); + fieldDescUsed += ARRAY_SIZE (fieldDefinitionsUniversal); Assert ( fieldDescAllocated == fieldDescUsed ); } @@ -401,9 +401,10 @@ static const char *renderEscapedName (const char* s, int c = *s; if ((c > 0x00 && c <= 0x1F) || c == 0x7F) { + char letter = getLanguageKind(tag->langType, tag->kindIndex)->letter; verbose ("Unexpected character (0 < *c && *c < 0x20) included in a tagEntryInfo: %s\n", base); verbose ("File: %s, Line: %lu, Lang: %s, Kind: %c\n", - tag->inputFileName, tag->lineNumber, tag->language, tag->kind->letter); + tag->inputFileName, tag->lineNumber, getLanguageName(tag->langType), letter); verbose ("Escape the character\n"); break; } @@ -518,7 +519,8 @@ static const char* renderCompactInputLine (vString *b, const char *const line) static const char *renderFieldKindName (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) { - return renderAsIs (b, tag->kind->name); + kindDefinition *kdef = getLanguageKind(tag->langType, tag->kindIndex); + return renderAsIs (b, kdef->name); } static const char *renderFieldCompactInputLine (const tagEntryInfo *const tag, @@ -564,15 +566,12 @@ static const char *renderFieldRole (const tagEntryInfo *const tag, vString* b) { int rindex = tag->extensionFields.roleIndex; - const roleDesc * role; if (rindex == ROLE_INDEX_DEFINITION) vStringClear (b); else { - Assert (rindex < tag->kind->nRoles); - role = & (tag->kind->roles [rindex]); - return renderRole (role, b); + return "TODO"; } return vStringValue (b); @@ -582,10 +581,12 @@ static const char *renderFieldLanguage (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) { - const char *l = tag->language; + const char *l; - if (Option.lineDirectives && tag->sourceLanguage) - l = tag->sourceLanguage; + if (Option.lineDirectives && (tag->sourceLangType != LANG_IGNORE)) + l = getLanguageName(tag->sourceLangType); + else + l = getLanguageName(tag->langType); return renderAsIs (b, WITH_DEFUALT_VALUE(l)); } @@ -601,11 +602,7 @@ static const char *renderFieldKindLetter (const tagEntryInfo *const tag, const char *value CTAGS_ATTR_UNUSED, vString* b) { - static char c[2] = { [1] = '\0' }; - - c [0] = tag->kind->letter; - - return renderAsIs (b, c); + return "TODO"; } static const char *renderFieldImplementation (const tagEntryInfo *const tag, @@ -712,7 +709,7 @@ static const char *renderFieldEnd (const tagEntryInfo *const tag, static bool isLanguageFieldAvailable (const tagEntryInfo *const tag) { - return (tag->language != NULL)? true: false; + return (tag->langType == LANG_IGNORE)? false: true; } static bool isTyperefFieldAvailable (const tagEntryInfo *const tag) @@ -789,7 +786,7 @@ static bool isFieldFixed (fieldType type) extern bool enableField (fieldType type, bool state, bool warnIfFixedField) { - fieldSpec *spec = getFieldDesc(type)->spec; + fieldDefinition *spec = getFieldDesc(type)->spec; bool old = spec->enabled? true: false; if (isFieldFixed (type)) { @@ -877,7 +874,7 @@ static const char* defaultRenderer (const tagEntryInfo *const tag, return value; } -extern int defineField (fieldSpec *spec, langType language) +extern int defineField (fieldDefinition *spec, langType language) { fieldDesc *fdesc; char *nameWithPrefix; diff --git a/ctags/main/field.h b/ctags/main/field.h index e3902ac6..741f2588 100644 --- a/ctags/main/field.h +++ b/ctags/main/field.h @@ -58,7 +58,7 @@ typedef const char* (* renderEscaped) (const tagEntryInfo *const tag, typedef bool (* isValueAvailable) (const struct sTagEntryInfo *const tag); #define FIELD_LETTER_NO_USE '\0' -typedef struct sFieldSpec { +typedef struct sFieldDefinition { /* lettern, and ftype are initialized in the main part, not in a parser. */ #define NUL_FIELD_LETTER '\0' @@ -70,7 +70,7 @@ typedef struct sFieldSpec { isValueAvailable isValueAvailable; unsigned int ftype; /* Given from the main part */ -} fieldSpec; +} fieldDefinition; extern fieldType getFieldTypeForOption (char letter); @@ -105,7 +105,7 @@ extern int countFields (void); /* language should be typed to langType. Use int here to avoid circular dependency */ -extern int defineField (fieldSpec *spec, langType language); +extern int defineField (fieldDefinition *spec, langType language); extern fieldType nextSiblingField (fieldType type); #endif /* CTAGS_MAIN_FIELD_H */ diff --git a/ctags/main/htable.c b/ctags/main/htable.c index c223b65b..2556110f 100644 --- a/ctags/main/htable.c +++ b/ctags/main/htable.c @@ -85,7 +85,7 @@ static void entry_reclaim (hentry* entry, entry = entry_destroy (entry, keyfreefn, valfreefn); } -static void *entry_find (hentry* entry, void* key, hashTableEqualFunc equalfn) +static void *entry_find (hentry* entry, const void* const key, hashTableEqualFunc equalfn) { while (entry) { @@ -141,6 +141,17 @@ extern hashTable *hashTableNew (unsigned int size, } extern void hashTableDelete (hashTable *htable) +{ + if (!htable) + return; + + hashTableClear (htable); + + eFree (htable->table); + eFree (htable); +} + +extern void hashTableClear (hashTable *htable) { unsigned int i; if (!htable) @@ -154,8 +165,6 @@ extern void hashTableDelete (hashTable *htable) entry_reclaim (entry, htable->keyfreefn, htable->valfreefn); htable->table[i] = NULL; } - eFree (htable->table); - eFree (htable); } extern void hashTablePutItem (hashTable *htable, void *key, void *value) @@ -166,7 +175,7 @@ extern void hashTablePutItem (hashTable *htable, void *key, void *value htable->table[i] = entry_new(key, value, htable->table[i]); } -extern void* hashTableGetItem (hashTable *htable, void *key) +extern void* hashTableGetItem (hashTable *htable, const void * key) { unsigned int i; @@ -183,7 +192,7 @@ extern bool hashTableDeleteItem (hashTable *htable, void *key) htable->equalfn, htable->keyfreefn, htable->valfreefn); } -extern bool hashTableHasItem (hashTable *htable, void *key) +extern bool hashTableHasItem (hashTable *htable, const void *key) { return hashTableGetItem (htable, key)? true: false; } @@ -208,19 +217,20 @@ extern int hashTableCountItem (hashTable *htable) hashTableForeachItem (htable, count, &c); return c; } -unsigned int hashPtrhash (void * x) + +unsigned int hashPtrhash (const void * const x) { union { - void *ptr; + const void * ptr; unsigned int ui; } v; - v.ui = 0; v.ptr = x; + return v.ui; } -bool hashPtreq (void *a, void *b) +bool hashPtreq (const void *const a, const void *const b) { return (a == b)? true: false; } @@ -228,7 +238,7 @@ bool hashPtreq (void *a, void *b) /* http://www.cse.yorku.ca/~oz/hash.html */ static unsigned long -djb2(unsigned char *str) +djb2(const unsigned char *str) { unsigned long hash = 5381; int c; @@ -239,18 +249,35 @@ djb2(unsigned char *str) return hash; } -unsigned int hashCstrhash (void * x) +static unsigned long +casedjb2(const unsigned char *str) { - char *s = x; - return (unsigned int)djb2((unsigned char *)s); + unsigned long hash = 5381; + int c; + + while ((c = *str++)) + { + if (('a' <= c) && (c <= 'z')) + c += ('A' - 'a'); + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + } + + return hash; } -bool hashCstreq (void *a, void *b) + +unsigned int hashCstrhash (const void *const x) +{ + const char *const s = x; + return (unsigned int)djb2((const unsigned char *)s); +} + +bool hashCstreq (const void * const a, const void *const b) { return !!(strcmp (a, b) == 0); } -unsigned int hashInthash (void *x) +unsigned int hashInthash (const void *const x) { union tmp { unsigned int u; @@ -262,10 +289,22 @@ unsigned int hashInthash (void *x) return x0.u; } -bool hashInteq (void *a, void *b) +bool hashInteq (const void *const a, const void *const b) { int ai = *(int *)a; int bi = *(int *)b; return !!(ai == bi); } + + +unsigned int hashCstrcasehash (const void *const x) +{ + const char *const s = x; + return (unsigned int)casedjb2((const unsigned char *)s); +} + +bool hashCstrcaseeq (const void *const a, const void *const b) +{ + return !!(strcasecmp (a, b) == 0); +} diff --git a/ctags/main/htable.h b/ctags/main/htable.h index 9a0cecf7..03cff361 100644 --- a/ctags/main/htable.h +++ b/ctags/main/htable.h @@ -14,19 +14,22 @@ #include "general.h" typedef struct sHashTable hashTable; -typedef unsigned int (* hashTableHashFunc) (void * key); -typedef bool (* hashTableEqualFunc) (void* a, void* b); +typedef unsigned int (* hashTableHashFunc) (const void * const key); +typedef bool (* hashTableEqualFunc) (const void* a, const void* b); typedef void (* hashTableFreeFunc) (void * ptr); typedef void (* hashTableForeachFunc) (void *key, void *value, void* user_data); -unsigned int hashPtrhash (void * x); -bool hashPtreq (void *a, void *b); +unsigned int hashPtrhash (const void * x); +bool hashPtreq (const void * a, const void * constb); -unsigned int hashCstrhash (void * x); -bool hashCstreq (void *a, void *b); +unsigned int hashCstrhash (const void * x); +bool hashCstreq (const void * a, const void * b); -unsigned int hashInthash (void *x); -bool hashInteq (void *a, void *b); +unsigned int hashCstrcasehash (const void * x); +bool hashCstrcaseeq (const void * a, const void * b); + +unsigned int hashInthash (const void * x); +bool hashInteq (const void * a, const void * b); extern hashTable* hashTableNew (unsigned int size, hashTableHashFunc hashfn, @@ -34,9 +37,10 @@ extern hashTable* hashTableNew (unsigned int size, hashTableFreeFunc keyfreefn, hashTableFreeFunc valfreefn); extern void hashTableDelete (hashTable *htable); +extern void hashTableClear (hashTable *htable); extern void hashTablePutItem (hashTable *htable, void *key, void *value); -extern void* hashTableGetItem (hashTable *htable, void *key); -extern bool hashTableHasItem (hashTable *htable, void *key); +extern void* hashTableGetItem (hashTable *htable, const void * key); +extern bool hashTableHasItem (hashTable * htable, const void * key); extern bool hashTableDeleteItem (hashTable *htable, void *key); extern void hashTableForeachItem (hashTable *htable, hashTableForeachFunc proc, void *user_data); extern int hashTableCountItem (hashTable *htable); diff --git a/ctags/main/inline.h b/ctags/main/inline.h new file mode 100644 index 00000000..34ceb385 --- /dev/null +++ b/ctags/main/inline.h @@ -0,0 +1,26 @@ +/* + * + * Copyright (c) 2016, 2017 Matthew Brush + * + * + * This source code is released for free distribution under the terms of the + * GNU General Public License version 2 or (at your option) any later version. + * + */ + +#ifndef CTAGS_MAIN_INLINE_H +#define CTAGS_MAIN_INLINE_H + +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +# define CTAGS_INLINE static inline +#elif defined(_MSC_VER) +# define CTAGS_INLINE static __inline +#elif defined(__GNUC__) || defined(__clang__) +# define CTAGS_INLINE static __inline__ +// #elif ... other compilers/tests here ... +// # define CTAGS_INLINE ... +#else +# define CTAGS_INLINE static +#endif + +#endif /* CTAGS_MAIN_INLINE_H */ diff --git a/ctags/main/kind.c b/ctags/main/kind.c index cee4bb98..a751aa42 100644 --- a/ctags/main/kind.c +++ b/ctags/main/kind.c @@ -17,13 +17,13 @@ #include "kind.h" #include "parse.h" -extern void printRole (const roleDesc* const role) +extern void printRole (const roleDefinition* const role) { if (role) printf ("%s\t%s\t%s\n", role->name, role->description, role->enabled? "on": "off"); } -extern const char *renderRole (const roleDesc* const role, vString* b) +extern const char *renderRole (const roleDefinition* const role, vString* b) { vStringCatS (b, role->name); return vStringValue (b); @@ -78,7 +78,7 @@ extern void printKindListHeader (bool indent, bool tabSeparated) #undef KIND_HEADER_COMMON_FMT } -extern void printKind (const kindOption* const kind, bool allKindFields, bool indent, +extern void printKind (const kindDefinition* const kind, bool allKindFields, bool indent, bool tabSeparated) { #define KIND_FMT MAKE_KIND_FMT("", c, d) @@ -109,11 +109,10 @@ extern void printKind (const kindOption* const kind, bool allKindFields, bool in #undef KIND_FMT } -const char *scopeSeparatorFor (const kindOption *kind, char parentLetter) +const char *scopeSeparatorFor (langType lang, int kindIndex, int parentKindIndex) { - scopeSeparator *table; - Assert (kind); - table = kind->separators; + kindDefinition *kind = getLanguageKind (lang, kindIndex); + scopeSeparator *table = kind->separators; /* If no table is given, use the default generic separator ".". The exception is if a root separator is looked up. In this case, @@ -121,7 +120,7 @@ const char *scopeSeparatorFor (const kindOption *kind, char parentLetter) if (table == NULL) { - if (parentLetter == KIND_NULL) + if (parentKindIndex == KIND_GHOST_INDEX) return NULL; else return "."; @@ -131,21 +130,21 @@ const char *scopeSeparatorFor (const kindOption *kind, char parentLetter) { /* KIND_WILDCARD cannot be used as a key for finding a root separator.*/ - if ( (table->parentLetter == KIND_WILDCARD - && parentLetter != KIND_NULL) - || table->parentLetter == parentLetter) + if ( (table->parentKindIndex == KIND_WILDCARD_INDEX + && parentKindIndex != KIND_GHOST_INDEX) + || table->parentKindIndex == parentKindIndex) return table->separator; table++; } - if (parentLetter == KIND_NULL) + if (parentKindIndex == KIND_GHOST_INDEX) return NULL; else return "."; } -extern void enableKind (kindOption *kind, bool enable) +extern void enableKind (kindDefinition *kind, bool enable) { - kindOption *slave; + kindDefinition *slave; if (kind->master) enableKind (kind->master, enable); diff --git a/ctags/main/kind.h b/ctags/main/kind.h index e90f1ce3..0bd56599 100644 --- a/ctags/main/kind.h +++ b/ctags/main/kind.h @@ -13,14 +13,14 @@ #include "routines.h" /* for STRINGIFY */ #include "vstring.h" -typedef struct sRoleDesc { +typedef struct sRoleDefinition { bool enabled; const char* name; /* role name */ const char* description; /* displayed in --help output */ -} roleDesc; +} roleDefinition; -extern void printRole (const roleDesc* const role); /* for --help */ -extern const char *renderRole (const roleDesc* const role, vString* b); +extern void printRole (const roleDefinition* const role); /* for --help */ +extern const char *renderRole (const roleDefinition* const role, vString* b); /* * Predefined kinds @@ -32,9 +32,11 @@ extern const char *renderRole (const roleDesc* const role, vString* b); #define KIND_NULL '\0' +#define KIND_GHOST_INDEX -1 #define KIND_GHOST ' ' #define KIND_GHOST_LONG "ghost" +#define KIND_FILE_INDEX -2 #define KIND_FILE_DEFAULT 'F' #define KIND_FILE_DEFAULT_LONG "file" @@ -43,21 +45,22 @@ extern const char *renderRole (const roleDesc* const role, vString* b); #define KIND_GENERIC_REFERENCE '@' #define KIND_GENERIC_REFERENCE_DEFAULT_LONG "reference" +#define KIND_WILDCARD_INDEX -3 #define KIND_WILDCARD '*' typedef struct sScopeSeparator { - char parentLetter; + int parentKindIndex; const char *separator; } scopeSeparator; -struct sKindOption { +struct sKindDefinition { bool enabled; /* are tags for kind enabled? */ char letter; /* kind letter */ const char* name; /* kind name */ const char* description; /* displayed in --help output */ bool referenceOnly; int nRoles; /* The number of role elements. */ - roleDesc *roles; + roleDefinition *roles; scopeSeparator *separators; unsigned int separatorCount; @@ -71,20 +74,20 @@ struct sKindOption { If the value other than `LANG_AUTO' is specified, the main part does nothing. */ langType syncWith; - kindOption *slave; - kindOption *master; + kindDefinition *slave; + kindDefinition *master; }; #define ATTACH_ROLES(RS) .nRoles = ARRAY_SIZE(RS), .roles = RS #define ATTACH_SEPARATORS(S) .separators = S, .separatorCount = ARRAY_SIZE(S) /* The value of `tabSeparated' is meaningfull only when `allKindFields' is true. */ -extern void printKind (const kindOption* const kind, bool allKindFields, bool indent, +extern void printKind (const kindDefinition* const kind, bool allKindFields, bool indent, bool tabSeparated); extern void printKindListHeader (bool indent, bool tabSeparated); -extern const char *scopeSeparatorFor (const kindOption *kind, char parentLetter); +extern const char *scopeSeparatorFor (langType lang, int kindIndex, int parentKindIndex); -extern void enableKind (kindOption *kind, bool enable); +extern void enableKind (kindDefinition *kind, bool enable); #define PR_KIND_STR(X) PR_KIND_WIDTH_##X #define PR_KIND_FMT(X,T) "%-" STRINGIFY(PR_KIND_STR(X)) STRINGIFY(T) diff --git a/ctags/main/lcpp.c b/ctags/main/lcpp.c index dbdf6a3b..956003e9 100644 --- a/ctags/main/lcpp.c +++ b/ctags/main/lcpp.c @@ -66,7 +66,7 @@ typedef struct sCppState { bool resolveRequired; /* must resolve if/else/elif/endif branch */ bool hasAtLiteralStrings; /* supports @"c:\" strings */ bool hasCxxRawLiteralStrings; /* supports R"xxx(...)xxx" strings */ - const kindOption *defineMacroKind; + int defineMacroKindIndex; struct sDirective { enum eState state; /* current directive being processed */ bool accept; /* is a directive syntactically permitted? */ @@ -92,7 +92,7 @@ static cppState Cpp = { false, /* resolveRequired */ false, /* hasAtLiteralStrings */ false, /* hasCxxRawLiteralStrings */ - NULL, /* defineMacroKind */ + -1, /* defineMacroKindIndex */ { DRCTV_NONE, /* state */ false, /* accept */ @@ -118,7 +118,7 @@ extern unsigned int cppGetDirectiveNestLevel (void) extern void cppInit (const bool state, const bool hasAtLiteralStrings, const bool hasCxxRawLiteralStrings, - const kindOption *defineMacroKind) + int defineMacroKindIndex) { BraceFormat = state; @@ -127,7 +127,7 @@ extern void cppInit (const bool state, const bool hasAtLiteralStrings, Cpp.resolveRequired = false; Cpp.hasAtLiteralStrings = hasAtLiteralStrings; Cpp.hasCxxRawLiteralStrings = hasCxxRawLiteralStrings; - Cpp.defineMacroKind = defineMacroKind; + Cpp.defineMacroKindIndex = defineMacroKindIndex; Cpp.directive.state = DRCTV_NONE; Cpp.directive.accept = true; @@ -326,22 +326,22 @@ static int makeDefineTag (const char *const name, bool parameterized, bool undef { const bool isFileScope = (bool) (! isInputHeaderFile ()); - if (!Cpp.defineMacroKind) + if (Cpp.defineMacroKindIndex == -1) return CORK_NIL; if (isFileScope && !isXtagEnabled(XTAG_FILE_SCOPE)) return CORK_NIL; if ( /* condition for definition tag */ - ((!undef) && Cpp.defineMacroKind->enabled) + ((!undef) && isLanguageKindEnabled (getInputLanguage(), Cpp.defineMacroKindIndex)) || /* condition for reference tag */ (undef && isXtagEnabled(XTAG_REFERENCE_TAGS))) { tagEntryInfo e; - initTagEntry (&e, name, Cpp.defineMacroKind); + initTagEntry (&e, name, Cpp.defineMacroKindIndex); e.lineNumberEntry = (bool) (Option.locate == EX_LINENUM); e.isFileScope = isFileScope; - e.truncateLine = true; + e.truncateLineAfterTag = true; if (parameterized) e.extensionFields.signature = cppGetSignature (); makeTagEntry (&e); diff --git a/ctags/main/lcpp.h b/ctags/main/lcpp.h index 46bd1513..86760a91 100644 --- a/ctags/main/lcpp.h +++ b/ctags/main/lcpp.h @@ -64,7 +64,7 @@ extern unsigned int cppGetDirectiveNestLevel (void); extern void cppInit (const bool state, const bool hasAtLiteralStrings, const bool hasCxxRawLiteralStrings, - const kindOption *defineMacroKind); + int defineMacroKindIndex); extern void cppTerminate (void); extern void cppBeginStatement (void); extern void cppEndStatement (void); diff --git a/ctags/main/lregex.c b/ctags/main/lregex.c index d8875cf0..6686beec 100644 --- a/ctags/main/lregex.c +++ b/ctags/main/lregex.c @@ -75,7 +75,7 @@ typedef struct { union { struct { char *name_pattern; - kindOption *kind; + kindDefinition *kind; } tag; struct { regexCallback function; @@ -139,9 +139,14 @@ static void clearPatternSet (const langType language) */ static int makeRegexTag ( - const vString* const name, const kindOption* const kind, int scopeIndex, int placeholder) + const vString* const name, const kindDefinition* const kind, int scopeIndex, int placeholder) { Assert (kind != NULL); + /* TODO: Disable regex tag generation for now. We would need to pass kindIndex + * to initTagEntry() but currently we don't have kinds indexable because + * they are stored in hash table. Consider whether we want to support + * regex parsers at all in Geany. */ +#if 0 if (kind->enabled) { tagEntryInfo e; @@ -152,6 +157,7 @@ static int makeRegexTag ( return makeTagEntry (&e); } else +#endif return CORK_NIL; } @@ -299,9 +305,9 @@ static flagDefinition scopePtrnFlagDef[] = { NULL, "don't put this tag to tags file."}, }; -static kindOption *kindNew () +static kindDefinition *kindNew () { - kindOption *kind = xCalloc (1, kindOption); + kindDefinition *kind = xCalloc (1, kindDefinition); kind->letter = '\0'; kind->name = NULL; kind->description = NULL; @@ -314,7 +320,7 @@ static kindOption *kindNew () static void kindFree (void *data) { - kindOption *kind = data; + kindDefinition *kind = data; kind->letter = '\0'; if (kind->name) { @@ -335,7 +341,7 @@ static regexPattern* addCompiledTagCommon (const langType language, { patternSet* set; regexPattern *ptrn; - kindOption *kind = NULL; + kindDefinition *kind = NULL; if (language > SetUpper) { @@ -865,14 +871,14 @@ extern bool processRegexOption (const char *const option, } struct kindCbHelperData { - bool (*func) (kindOption *, void *); + bool (*func) (kindDefinition *, void *); void *func_data; bool result; }; static void kindCbHelper (void *key, void *value, void* user_data) { - kindOption *kind = value; + kindDefinition *kind = value; struct kindCbHelperData *helper_data = user_data; if (helper_data->result) @@ -882,7 +888,7 @@ static void kindCbHelper (void *key, void *value, void* user_data) } extern void foreachRegexKinds (const langType language, - bool (*func) (kindOption *, void *), + bool (*func) (kindDefinition *, void *), void *data) { initializeParser (language); @@ -900,7 +906,7 @@ extern void foreachRegexKinds (const langType language, } -static bool kind_reset_cb (kindOption *kind, void *data) +static bool kind_reset_cb (kindDefinition *kind, void *data) { kind->enabled = *(bool *)data; return false; /* continue */ @@ -919,7 +925,7 @@ struct kind_and_mode_and_result bool result; }; -static bool enable_kind_cb (kindOption *kind, void *data) +static bool enable_kind_cb (kindDefinition *kind, void *data) { struct kind_and_mode_and_result *kmr = data; if ((kmr->kind != KIND_NULL @@ -967,7 +973,7 @@ struct kind_and_result bool result; }; -static bool is_kind_enabled_cb (kindOption *kind, void *data) +static bool is_kind_enabled_cb (kindDefinition *kind, void *data) { bool r = false; struct kind_and_result *kr = data; @@ -981,7 +987,7 @@ static bool is_kind_enabled_cb (kindOption *kind, void *data) return r; } -static bool does_kind_exist_cb (kindOption *kind, void *data) +static bool does_kind_exist_cb (kindDefinition *kind, void *data) { bool r = false; struct kind_and_result *kr = data; @@ -1026,7 +1032,7 @@ struct printRegexKindCBData{ bool tabSeparated; }; -static bool printRegexKind (kindOption *kind, void *user_data) +static bool printRegexKind (kindDefinition *kind, void *user_data) { struct printRegexKindCBData *data = user_data; if (kind->letter != KIND_GHOST) diff --git a/ctags/main/lxcmd.c b/ctags/main/lxcmd.c index 1f220915..6811b3cf 100644 --- a/ctags/main/lxcmd.c +++ b/ctags/main/lxcmd.c @@ -116,7 +116,7 @@ typedef struct { } address; - const kindOption* kind; + const kindDefinition* kind; /* is tag of file-limited scope? */ short fileScope; @@ -134,7 +134,7 @@ typedef struct { typedef struct { vString *path; - kindOption *kinds; + kindDefinition *kinds; unsigned int n_kinds; bool available; unsigned int id; /* not used yet */ @@ -166,7 +166,7 @@ static void clearPathSet (const langType language) p->available = false; for (k = 0; k < p->n_kinds; k++) { - kindOption* kind = &(p->kinds[k]); + kindDefinition* kind = &(p->kinds[k]); eFree ((void *)kind->name); kind->name = NULL; @@ -191,7 +191,7 @@ static bool loadPathKind (xcmdPath *const path, char* line, char *args[]) const char* backup = line; char* off; vString *desc; - kindOption *kind; + kindDefinition *kind; if (line[0] == '\0') return false; @@ -201,7 +201,7 @@ static bool loadPathKind (xcmdPath *const path, char* line, char *args[]) return false; } - path->kinds = xRealloc (path->kinds, path->n_kinds + 1, kindOption); + path->kinds = xRealloc (path->kinds, path->n_kinds + 1, kindDefinition); kind = &path->kinds [path->n_kinds]; memset (kind, 0, sizeof (*kind)); kind->enabled = true; @@ -405,7 +405,7 @@ static bool loadPathKinds (xcmdPath *const path, const langType language) extern void foreachXcmdKinds (const langType language, - bool (*func) (kindOption *, void *), + bool (*func) (kindDefinition *, void *), void *data) { #ifdef HAVE_COPROC @@ -428,7 +428,7 @@ extern void foreachXcmdKinds (const langType language, #endif } -static bool kind_reset_cb (kindOption *kind, void *data) +static bool kind_reset_cb (kindDefinition *kind, void *data) { kind->enabled = *(bool *)data; return false; /* continue */ @@ -447,7 +447,7 @@ struct kind_and_mode_and_result bool result; }; -static bool enable_kind_cb (kindOption *kind, void *data) +static bool enable_kind_cb (kindDefinition *kind, void *data) { struct kind_and_mode_and_result *kmr = data; if ((kmr->kind != KIND_NULL @@ -500,7 +500,7 @@ struct kind_and_result bool result; }; -static bool is_kind_enabled_cb (kindOption *kind, void *data) +static bool is_kind_enabled_cb (kindDefinition *kind, void *data) { bool r = false; struct kind_and_result *kr = data; @@ -514,7 +514,7 @@ static bool is_kind_enabled_cb (kindOption *kind, void *data) return r; } -static bool does_kind_exist_cb (kindOption *kind, void *data) +static bool does_kind_exist_cb (kindDefinition *kind, void *data) { bool r = false; struct kind_and_result *kr = data; @@ -560,7 +560,7 @@ struct printXcmdKindCBData { }; #ifdef HAVE_COPROC -static bool printXcmdKind (kindOption *kind, void *user_data) +static bool printXcmdKind (kindDefinition *kind, void *user_data) { struct printXcmdKindCBData *data = user_data; @@ -732,10 +732,10 @@ extern bool processXcmdOption (const char *const option, const char *const param } #ifdef HAVE_COPROC -static const kindOption* lookupKindFromLetter (const xcmdPath* const path, char kind_letter) +static const kindDefinition* lookupKindFromLetter (const xcmdPath* const path, char kind_letter) { unsigned int k; - kindOption *kind; + kindDefinition *kind; for (k = 0; k < path->n_kinds; k++) { @@ -747,10 +747,10 @@ static const kindOption* lookupKindFromLetter (const xcmdPath* const path, char } -static const kindOption* lookupKindFromName (const xcmdPath* const path, const char* const kind_name) +static const kindDefinition* lookupKindFromName (const xcmdPath* const path, const char* const kind_name) { unsigned int k; - kindOption *kind; + kindDefinition *kind; for (k = 0; k < path->n_kinds; k++) { @@ -799,7 +799,7 @@ static const char* entryGetAnyUnpulledField (tagEntry *const entry, const char * static bool isKindEnabled (xcmdPath* path, const char* value) { unsigned int k; - kindOption *kind; + kindDefinition *kind; Assert (path->kinds); Assert (value); @@ -857,7 +857,7 @@ static bool parseExtensionFields (tagEntry *const entry, char *const string, xcm entry->kind = lookupKindFromLetter (path, field[0]); if (entry->kind == NULL) { - kindOption *fileKind = getInputLanguageFileKind (); + kindDefinition *fileKind = getInputLanguageFileKind (); if (fileKind && fileKind->letter == field[0]) /* ctags will make a tag for file. */ goto reject; @@ -889,7 +889,7 @@ static bool parseExtensionFields (tagEntry *const entry, char *const string, xcm entry->kind = lookupKindFromName (path, value); if (entry->kind == NULL) { - kindOption *fileKind = getInputLanguageFileKind (); + kindDefinition *fileKind = getInputLanguageFileKind (); if (fileKind && (strcmp(fileKind->name, value) == 0)) /* ctags will make a tag for file. */ goto reject; diff --git a/ctags/main/lxpath.c b/ctags/main/lxpath.c index 4e5be076..c66a310a 100644 --- a/ctags/main/lxpath.c +++ b/ctags/main/lxpath.c @@ -23,25 +23,22 @@ static void simpleXpathMakeTag (xmlNode *node, const tagXpathMakeTagSpec *spec, - const kindOption* const kinds, + const kindDefinition* const kinds, void *userData) { tagEntryInfo tag; xmlChar* str; - const kindOption *kind; char *path; str = xmlNodeGetContent(node); if (str == NULL) return; - kind = kinds + spec->kind; - if (spec->role == ROLE_INDEX_DEFINITION) - initTagEntry (&tag, (char *)str, kind); + initTagEntry (&tag, (char *)str, spec->kind); else if (isXtagEnabled(XTAG_REFERENCE_TAGS)) initRefTagEntry (&tag, (char *)str, - kind, + spec->kind, spec->role); else goto out; @@ -77,7 +74,7 @@ extern void addTagXpath (const langType language CTAGS_ATTR_UNUSED, tagXpathTabl static void findXMLTagsCore (xmlXPathContext *ctx, xmlNode *root, const tagXpathTableTable *xpathTableTable, - const kindOption* const kinds,void *userData) + const kindDefinition* const kinds,void *userData) { unsigned int i; int j; @@ -156,7 +153,7 @@ static xmlDocPtr makeXMLDoc (void) extern void findXMLTags (xmlXPathContext *ctx, xmlNode *root, const tagXpathTableTable *xpathTableTable, - const kindOption* const kinds,void *userData) + const kindDefinition* const kinds,void *userData) { bool usedAsEntryPoint = false; xmlDocPtr doc = NULL; @@ -210,7 +207,7 @@ extern void addTagXpath (const langType language, tagXpathTable *xpathTable) extern void findXMLTags (xmlXPathContext *ctx, xmlNode *root, const tagXpathTableTable *xpathTableTable, - const kindOption* const kinds, void *userData) + const kindDefinition* const kinds, void *userData) { } diff --git a/ctags/main/mbcs.h b/ctags/main/mbcs.h new file mode 100644 index 00000000..cb8848cc --- /dev/null +++ b/ctags/main/mbcs.h @@ -0,0 +1 @@ +/* Dummy header - only included by some parsers */ diff --git a/ctags/main/mio.c b/ctags/main/mio.c index 53581dfe..1e924cdb 100644 --- a/ctags/main/mio.c +++ b/ctags/main/mio.c @@ -48,7 +48,7 @@ #define xCalloc(n,Type) (Type *)eCalloc((size_t)(n), sizeof (Type)) #define xRealloc(p,n,Type) (Type *)eRealloc((p), (n) * sizeof (Type)) -extern void *eMalloc (const size_t size) +static void *eMalloc (const size_t size) { void *buffer = malloc (size); @@ -61,7 +61,7 @@ extern void *eMalloc (const size_t size) return buffer; } -extern void *eCalloc (const size_t count, const size_t size) +static void *eCalloc (const size_t count, const size_t size) { void *buffer = calloc (count, size); @@ -74,7 +74,7 @@ extern void *eCalloc (const size_t count, const size_t size) return buffer; } -extern void *eRealloc (void *const ptr, const size_t size) +static void *eRealloc (void *const ptr, const size_t size) { void *buffer; if (ptr == NULL) @@ -91,13 +91,13 @@ extern void *eRealloc (void *const ptr, const size_t size) return buffer; } -extern void eFree (void *const ptr) +static void eFree (void *const ptr) { free (ptr); } -# define Assert(c) -# define AssertNotReached() +# define Assert(c) do {} while(0) +# define AssertNotReached() do {} while(0) #endif /* minimal reallocation chunk size */ @@ -361,12 +361,12 @@ MIO *mio_new_memory (unsigned char *data, * @size: the length of the data copied from @base to new mio * * Creates a new #MIO object by copying data from existing #MIO (@base). - * The range for copying are given with @start and @size. + * The range for copying is given with @start and @size. * Copying data at the range from @start to the end of @base is - * done if 0 is given as @size. + * done if -1 is given as @size. * - * If @size(!= 0) is larger than the length from @start to the end of - * @base, %NULL is return. + * If @size is larger than the length from @start to the end of + * @base, %NULL is returned. * * The function doesn't move the file position of @base. * @@ -374,7 +374,7 @@ MIO *mio_new_memory (unsigned char *data, * */ -MIO *mio_new_mio (MIO *base, long start, size_t size) +MIO *mio_new_mio (MIO *base, long start, long size) { unsigned char *data; long original_pos; @@ -383,15 +383,15 @@ MIO *mio_new_mio (MIO *base, long start, size_t size) original_pos = mio_tell (base); - if (size == 0) + if (size == -1) { long end; if (mio_seek (base, 0, SEEK_END) != 0) return NULL; end = mio_tell (base); + Assert (end >= start); size = end - start; - Assert (size >= 0); } if (mio_seek (base, start, SEEK_SET) != 0) @@ -732,7 +732,7 @@ size_t mio_write (MIO *mio, * Writes a character to a #MIO stream. This function behaves the same as * fputc(). * - * Returns: The written wharacter, or %EOF on error. + * Returns: The written character, or %EOF on error. */ int mio_putc (MIO *mio, int c) { @@ -796,7 +796,7 @@ int mio_puts (MIO *mio, const char *s) /** * mio_vprintf: * @mio: A #MIO object - * @format: A printf fomrat string + * @format: A printf format string * @ap: The variadic argument list for the format * * Writes a formatted string into a #MIO stream. This function behaves the same @@ -1100,7 +1100,7 @@ int mio_error (MIO *mio) * SEEK_CUR from the current position and SEEK_SET from the end of the * stream. * - * Sets the curosr position on a #MIO stream. This functions behaves the same as + * Sets the cursor position on a #MIO stream. This functions behaves the same as * fseek(). See also mio_tell() and mio_setpos(). * * Returns: 0 on success, -1 otherwise, in which case errno should be set to diff --git a/ctags/main/mio.h b/ctags/main/mio.h index e78ad896..b9186f78 100644 --- a/ctags/main/mio.h +++ b/ctags/main/mio.h @@ -123,7 +123,7 @@ MIO *mio_new_memory (unsigned char *data, MIOReallocFunc realloc_func, MIODestroyNotify free_func); -MIO *mio_new_mio (MIO *base, long start, size_t size); +MIO *mio_new_mio (MIO *base, long start, long size); MIO *mio_ref (MIO *mio); int mio_free (MIO *mio); diff --git a/ctags/main/numarray.c b/ctags/main/numarray.c new file mode 100644 index 00000000..b6c8619c --- /dev/null +++ b/ctags/main/numarray.c @@ -0,0 +1,175 @@ +/* +* Copyright (c) 1999-2002, Darren Hiebert +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* This module contains functions managing resizable pointer arrays. +*/ + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ + +#include "debug.h" +#include "numarray.h" +#include "routines.h" + +#include +#include + +#define impNumArray(prefix,Prefix,type) \ + \ + struct s##Prefix##Array { \ + unsigned int max; \ + unsigned int count; \ + type *array; \ + }; \ + \ + extern prefix##Array *prefix##ArrayNew (void) \ + { \ + prefix##Array* const result = xMalloc (1, prefix##Array); \ + result->max = 8; \ + result->count = 0; \ + result->array = xMalloc (result->max, type); \ + return result; \ + } \ + \ + extern void prefix##ArrayAdd (prefix##Array *const current, type num) \ + { \ + Assert (current != NULL); \ + if (current->count == current->max) \ + { \ + current->max *= 2; \ + current->array = xRealloc (current->array, current->max, type); \ + } \ + current->array [current->count++] = num; \ + } \ + \ + extern void prefix##ArrayRemoveLast (prefix##Array *const current) \ + { \ + Assert (current != NULL); \ + Assert (current->count > 0); \ + --current->count; \ + } \ + \ + extern void prefix##ArrayCombine (prefix##Array *const current, prefix##Array *const from) \ + { \ + unsigned int i; \ + Assert (current != NULL); \ + Assert (from != NULL); \ + for (i = 0 ; i < from->count ; ++i) \ + prefix##ArrayAdd (current, from->array [i]); \ + from->count = 0; \ + prefix##ArrayDelete (from); \ + } \ + \ + extern unsigned int prefix##ArrayCount (const prefix##Array *const current) \ + { \ + Assert (current != NULL); \ + return current->count; \ + } \ + \ + extern type prefix##ArrayItem (const prefix##Array *const current, const unsigned int indx) \ + { \ + Assert (current != NULL); \ + return current->array [indx]; \ + } \ + \ + extern type prefix##ArrayLast (const prefix##Array *const current) \ + { \ + Assert (current != NULL); \ + Assert (current->count > 0); \ + return current->array [current->count - 1]; \ + } \ + \ + extern void prefix##ArrayClear (prefix##Array *const current) \ + { \ + Assert (current != NULL); \ + current->count = 0; \ + } \ + \ + extern void prefix##ArrayDelete (prefix##Array *const current) \ + { \ + if (current != NULL) \ + { \ + prefix##ArrayClear (current); \ + eFree (current->array); \ + eFree (current); \ + } \ + } \ + \ + extern bool prefix##ArrayHasTest (const prefix##Array *const current, \ + bool (*test)(const type num, void *userData), \ + void *userData) \ + { \ + bool result = false; \ + unsigned int i; \ + Assert (current != NULL); \ + for (i = 0 ; ! result && i < current->count ; ++i) \ + result = (*test)(current->array [i], userData); \ + return result; \ + } \ + \ + static bool prefix##Eq (const type num, void *userData) \ + { \ + type *num0 = userData; \ + return (num == *num0); \ + } \ + \ + extern bool prefix##ArrayHas (const prefix##Array *const current, type num) \ + { \ + return prefix##ArrayHasTest (current, prefix##Eq, &num); \ + } \ + \ + extern void prefix##ArrayReverse (const prefix##Array *const current) \ + { \ + unsigned int i, j; \ + type tmp; \ + \ + Assert (current != NULL); \ + for (i = 0, j = current->count - 1 ; i < (current->count / 2); ++i, --j) \ + { \ + tmp = current->array[i]; \ + current->array[i] = current->array[j]; \ + current->array[j] = tmp; \ + } \ + } \ + \ + extern void prefix##ArrayDeleteItem (prefix##Array* const current, unsigned int indx) \ + { \ + memmove (current->array + indx, current->array + indx + 1, \ + (current->count - indx) * sizeof (*current->array)); \ + --current->count; \ + } \ + static int prefix##GreaterThan(const void *a, const void *b) \ + { \ + type an = *(type *)a; \ + type bn = *(type *)b; \ + if (an > bn) \ + return 1; \ + else if (an == bn) \ + return 0; \ + else \ + return -1; \ + } \ + static int prefix##LessThan(const void *a, const void *b) \ + { \ + return prefix##GreaterThan (b, a); \ + } \ + extern void prefix##ArraySort (prefix##Array *const current, bool descendingOrder) \ + { \ + if (descendingOrder) \ + qsort (current->array, current->count, sizeof (type), prefix##GreaterThan); \ + else \ + qsort (current->array, current->count, sizeof (type), prefix##LessThan); \ + } + +/* We expect the linker we use is enough clever to delete dead code. */ +impNumArray(char, Char, char); +impNumArray(uchar, Uchar, unsigned char); +impNumArray(int, Int, int); +impNumArray(uint, Uint, unsigned int); +impNumArray(long, Long, long); +impNumArray(ulong, Ulong, unsigned long); diff --git a/ctags/main/numarray.h b/ctags/main/numarray.h new file mode 100644 index 00000000..318931ae --- /dev/null +++ b/ctags/main/numarray.h @@ -0,0 +1,48 @@ +/* +* Copyright (c) 1999-2002, Darren Hiebert +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* Defines external interface to resizable pointer arrays. +*/ +#ifndef CTAGS_MAIN_NUMARRAY_H +#define CTAGS_MAIN_NUMARRAY_H + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ + + +#define declNumArray(prefix,Prefix,type) \ + \ + struct s##Prefix##Array; \ + typedef struct s##Prefix##Array prefix##Array; \ + \ + extern prefix##Array *prefix##ArrayNew (void); \ + extern void prefix##ArrayAdd (prefix##Array *const current, type num); \ + extern void prefix##ArrayRemoveLast (prefix##Array *const current); \ + extern void prefix##ArrayCombine (prefix##Array *const current, prefix##Array *const from); \ + extern void prefix##ArrayClear (prefix##Array *const current); \ + extern unsigned int prefix##ArrayCount (const prefix##Array *const current); \ + extern type prefix##ArrayItem (const prefix##Array *const current, const unsigned int indx); \ + extern type prefix##ArrayLast (const prefix##Array *const current); \ + extern void prefix##ArrayDelete (prefix##Array *const current); \ + extern bool prefix##ArrayHasTest (const prefix##Array *const current, \ + bool (*test)(const type num, void *userData), \ + void *userData); \ + extern bool prefix##ArrayHas (const prefix##Array *const current, type num); \ + extern void prefix##ArrayReverse (const prefix##Array *const current); \ + extern void prefix##ArrayDeleteItem (prefix##Array* const current, unsigned int indx); \ + \ + extern void prefix##ArraySort (prefix##Array *const current, bool descendingOrder); + +declNumArray(char, Char, char); +declNumArray(uchar, Uchar, unsigned char); +declNumArray(int, Int, int); +declNumArray(uint, Uint, unsigned int); +declNumArray(long, Long, long); +declNumArray(ulong, Ulong, unsigned long); + +#endif /* CTAGS_MAIN_NUMARRAY_H */ diff --git a/ctags/main/objpool.c b/ctags/main/objpool.c index ec7f3f46..dbe2b750 100644 --- a/ctags/main/objpool.c +++ b/ctags/main/objpool.c @@ -26,13 +26,15 @@ struct sObjPool { objPoolCreateFunc createFunc; objPoolDeleteFunc deleteFunc; objPoolClearFunc clearFunc; + void *createArg; }; /* * FUNCTION DEFINITIONS */ extern objPool *objPoolNew (unsigned int size, - objPoolCreateFunc createFunc, objPoolDeleteFunc deleteFunc, objPoolClearFunc clearFunc) + objPoolCreateFunc createFunc, objPoolDeleteFunc deleteFunc, objPoolClearFunc clearFunc, + void *createArg) { objPool* const result = xMalloc (1, objPool); result->array = ptrArrayNew (deleteFunc); @@ -40,6 +42,7 @@ extern objPool *objPoolNew (unsigned int size, result->createFunc = createFunc; result->deleteFunc = deleteFunc; result->clearFunc = clearFunc; + result->createArg = createArg; return result; } @@ -59,7 +62,7 @@ extern void *objPoolGet (objPool *pool) ptrArrayRemoveLast (pool->array); } else - obj = pool->createFunc (); + obj = pool->createFunc (pool->createArg); if (pool->clearFunc) pool->clearFunc (obj); diff --git a/ctags/main/objpool.h b/ctags/main/objpool.h index 1e6e6033..eb6133df 100644 --- a/ctags/main/objpool.h +++ b/ctags/main/objpool.h @@ -20,7 +20,7 @@ /* * DATA DECLARATIONS */ -typedef void * (*objPoolCreateFunc) (void); +typedef void * (*objPoolCreateFunc) (void *createArg); typedef void (*objPoolDeleteFunc) (void *data); typedef void (*objPoolClearFunc) (void *data); @@ -31,7 +31,8 @@ typedef struct sObjPool objPool; * FUNCTION PROTOTYPES */ extern objPool *objPoolNew (unsigned int size, - objPoolCreateFunc createFunc, objPoolDeleteFunc deleteFunc, objPoolClearFunc clearFunc); + objPoolCreateFunc createFunc, objPoolDeleteFunc deleteFunc, objPoolClearFunc clearFunc, + void *createArg); extern void objPoolDelete (objPool *pool); extern void *objPoolGet (objPool *pool); extern void objPoolPut (objPool *pool, void *obj); diff --git a/ctags/main/options.c b/ctags/main/options.c index 108690e0..73cb3b2f 100644 --- a/ctags/main/options.c +++ b/ctags/main/options.c @@ -1759,7 +1759,7 @@ static void processListExtraOption ( exit (0); } -static void processListFileKindOption ( +static void processListFileKindDefinition ( const char *const option, const char *const parameter) { if (parameter [0] == '\0' || strcasecmp (parameter, "all") == 0) @@ -2556,7 +2556,7 @@ static parametricOption ParametricOptions [] = { { "list-extra", processListExtraOption, true, STAGE_ANY }, { "list-features", processListFeaturesOption, true, STAGE_ANY }, { "list-fields", processListFieldsOption, true, STAGE_ANY }, - { "list-file-kind", processListFileKindOption, true, STAGE_ANY }, + { "list-file-kind", processListFileKindDefinition, true, STAGE_ANY }, { "list-kinds", processListKindsOption, true, STAGE_ANY }, { "list-kinds-full", processListKindsOption, true, STAGE_ANY }, { "list-languages", processListLanguagesOption, true, STAGE_ANY }, @@ -2816,7 +2816,7 @@ static void processLongOption ( ; else if (processParametricOption (option, parameter)) ; - else if (processKindOption (option, parameter)) + else if (processKindDefinition (option, parameter)) ; else if (processAliasOption (option, parameter)) ; @@ -3484,3 +3484,8 @@ extern void verbose (const char *const format, ...) { } /* GEANY DIFF END */ + +extern bool canUseLineNumberAsLocator (void) +{ + return (Option.locate != EX_PATTERN); +} diff --git a/ctags/main/options.h b/ctags/main/options.h index 2d694972..6fb7e396 100644 --- a/ctags/main/options.h +++ b/ctags/main/options.h @@ -180,7 +180,7 @@ extern langType getLanguageComponentInOption (const char *const option, extern void processLanguageDefineOption (const char *const option, const char *const parameter); extern bool processMapOption (const char *const option, const char *const parameter); -extern bool processKindOption (const char *const option, const char *const parameter); +extern bool processKindDefinition (const char *const option, const char *const parameter); extern bool processCorpusOption (const char *const option, const char *const parameter); extern bool processAliasOption (const char *const option, const char *const parameter); #ifdef HAVE_ICONV @@ -192,4 +192,7 @@ extern bool processXcmdOption (const char *const option, const char *const param typedef void (* mainLoopFunc) (cookedArgs *args, void *data); extern void setMainLoop (mainLoopFunc func, void *data); +/* This is for emitting a tag for a commnn block of Fortran parser*/ +extern bool canUseLineNumberAsLocator (void); + #endif /* CTAGS_MAIN_OPTIONS_H */ diff --git a/ctags/main/parse.c b/ctags/main/parse.c index 688941a8..449b8bea 100644 --- a/ctags/main/parse.c +++ b/ctags/main/parse.c @@ -27,6 +27,7 @@ #include "ptag.h" #include "read.h" #include "routines.h" +#include "trashbox.h" #include "vstring.h" #ifdef HAVE_ICONV # include "mbcs.h" @@ -53,6 +54,8 @@ typedef struct { enum specType specType; } parserCandidate; +static ptrArray *parsersUsedInCurrentInput; + /* * FUNCTION PROTOTYPES */ @@ -63,6 +66,8 @@ static void addParserPseudoTags (langType language); static void installKeywordTable (const langType language); static void installTagRegexTable (const langType language); static void installTagXpathTable (const langType language); +static void anonResetMaybe (parserDefinition *lang); +static void clearParsersUsedInCurrentInput (void); /* * DATA DEFINITIONS @@ -88,7 +93,7 @@ static parserDefinitionFunc* BuiltInParsers[] = { }; static parserDefinition** LanguageTable = NULL; static unsigned int LanguageCount = 0; -static kindOption defaultFileKind = { +static kindDefinition defaultFileKind = { .enabled = false, .letter = KIND_FILE_DEFAULT, .name = KIND_FILE_DEFAULT_LONG, @@ -105,21 +110,21 @@ extern unsigned int countParsers (void) } extern int makeSimpleTag ( - const vString* const name, kindOption* const kinds, const int kind) + const vString* const name, const int kindIndex) { int r = CORK_NIL; - if (kinds [kind].enabled && name != NULL && vStringLength (name) > 0) + if (isInputLanguageKindEnabled(kindIndex) && name != NULL && vStringLength (name) > 0) { tagEntryInfo e; - initTagEntry (&e, vStringValue (name), & kinds [kind]); + initTagEntry (&e, vStringValue (name), kindIndex); r = makeTagEntry (&e); } return r; } -extern int makeSimpleRefTag (const vString* const name, kindOption* const kinds, const int kind, +extern int makeSimpleRefTag (const vString* const name, const int kindIndex, int roleIndex) { int r = CORK_NIL; @@ -127,12 +132,13 @@ extern int makeSimpleRefTag (const vString* const name, kindOption* const kinds, if (! isXtagEnabled (XTAG_REFERENCE_TAGS)) return r; - Assert (roleIndex < kinds[kind].nRoles); + Assert (roleIndex < countInputLanguageRoles(kindIndex)); - if (kinds[kind].roles[roleIndex].enabled) + /* do not check for kind being disabled - that happens later in makeTagEntry() */ + if (name != NULL && vStringLength (name) > 0) { tagEntryInfo e; - initRefTagEntry (&e, vStringValue (name), & kinds [kind], roleIndex); + initRefTagEntry (&e, vStringValue (name), kindIndex, roleIndex); r = makeTagEntry (&e); } @@ -151,7 +157,7 @@ extern bool isLanguageEnabled (const langType language) if ((lang->method & METHOD_XCMD) && (!(lang->method & METHOD_XCMD_AVAILABLE)) && - (lang->kinds == NULL) && + (lang->kindTable == NULL) && (!(lang->method & METHOD_REGEX)) && (!(lang->method & METHOD_XPATH))) return false; @@ -168,11 +174,11 @@ extern parserDefinition* parserNew (const char* name) return parserNewFull (name, 0); } -static kindOption* fileKindNew (char letter) +static kindDefinition* fileKindNew (char letter) { - kindOption *fileKind; + kindDefinition *fileKind; - fileKind = xMalloc (1, kindOption); + fileKind = xMalloc (1, kindDefinition); *(fileKind) = defaultFileKind; fileKind->letter = letter; return fileKind; @@ -216,9 +222,9 @@ extern const char *getLanguageName (const langType language) return result; } -extern kindOption* getLanguageFileKind (const langType language) +extern kindDefinition* getLanguageFileKind (const langType language) { - kindOption* kind; + kindDefinition* kind; Assert (0 <= language && language < (int) LanguageCount); @@ -229,6 +235,13 @@ extern kindOption* getLanguageFileKind (const langType language) return kind; } +extern kindDefinition* getLanguageKind (const langType language, int kindIndex) +{ + Assert (0 <= language && language < (int) LanguageCount); + + return (LanguageTable [language]->kindTable) + kindIndex; +} + extern langType getNamedLanguage (const char *const name, size_t len) { langType result = LANG_IGNORE; @@ -724,7 +737,7 @@ struct getLangCtx { (mio_memory_get_data((_glc_)->input, NULL) == NULL)) \ { \ MIO *tmp_ = (_glc_)->input; \ - (_glc_)->input = mio_new_mio (tmp_, 0, 0); \ + (_glc_)->input = mio_new_mio (tmp_, 0, -1); \ mio_free (tmp_); \ if (!(_glc_)->input) { \ (_glc_)->err = true; \ @@ -1148,6 +1161,28 @@ extern langType getFileLanguage (const char *const fileName) return getFileLanguageAndKeepMIO(fileName, NULL); } +extern langType getLanguageForCommand (const char *const command, langType startFrom) +{ + const char *const tmp_command = baseFilename (command); + char *tmp_spec; + enum specType tmp_specType; + + return getNameOrAliasesLanguageAndSpec (tmp_command, startFrom, + (const char **const)&tmp_spec, + &tmp_specType); +} + +extern langType getLanguageForFilename (const char *const filename, langType startFrom) +{ + const char *const tmp_filename = baseFilename (filename); + char *tmp_spec; + enum specType tmp_specType; + + return getPatternLanguageAndSpec (tmp_filename, startFrom, + (const char **const)&tmp_spec, + &tmp_specType); +} + typedef void (*languageCallback) (langType language, void* user_data); static void foreachLanguage(languageCallback callback, void *user_data) { @@ -1384,21 +1419,21 @@ static bool doesParserUseKind (const parserDefinition *const parser, char letter } #endif -static void installFieldSpec (const langType language) +static void installFieldDefinition (const langType language) { unsigned int i; parserDefinition * parser; Assert (0 <= language && language < (int) LanguageCount); parser = LanguageTable [language]; - if (parser->fieldSpecCount > PRE_ALLOCATED_PARSER_FIELDS) + if (parser->fieldCount > PRE_ALLOCATED_PARSER_FIELDS) error (FATAL, "INTERNAL ERROR: in a parser, fields are defined more than PRE_ALLOCATED_PARSER_FIELDS\n"); - if (parser->fieldSpecs != NULL) + if (parser->fieldTable != NULL) { - for (i = 0; i < parser->fieldSpecCount; i++) - defineField (& parser->fieldSpecs [i], language); + for (i = 0; i < parser->fieldCount; i++) + defineField (& parser->fieldTable [i], language); } } @@ -1415,7 +1450,7 @@ static void initializeParserOne (langType lang) installKeywordTable (lang); installTagRegexTable (lang); installTagXpathTable (lang); - installFieldSpec (lang); + installFieldDefinition (lang); if (hasScopeActionInRegex (lang) || parser->requestAutomaticFQTag) @@ -1613,45 +1648,36 @@ extern void processLanguageDefineOption ( } } -static kindOption *langKindOption (const langType language, const int flag) +static kindDefinition *langKindDefinition (const langType language, const int flag) { unsigned int i; - kindOption* result = NULL; + kindDefinition* result = NULL; const parserDefinition* lang; Assert (0 <= language && language < (int) LanguageCount); lang = LanguageTable [language]; for (i=0 ; i < lang->kindCount && result == NULL ; ++i) - if (lang->kinds [i].letter == flag) - result = &lang->kinds [i]; + if (lang->kindTable [i].letter == flag) + result = &lang->kindTable [i]; return result; } -static kindOption *langKindLongOption (const langType language, const char *kindLong) +static kindDefinition *langKindLongOption (const langType language, const char *kindLong) { unsigned int i; - kindOption* result = NULL; + kindDefinition* result = NULL; const parserDefinition* lang; Assert (0 <= language && language < (int) LanguageCount); lang = LanguageTable [language]; for (i=0 ; i < lang->kindCount && result == NULL ; ++i) - if (strcmp (lang->kinds [i].name, kindLong) == 0) - result = &lang->kinds [i]; + if (strcmp (lang->kindTable [i].name, kindLong) == 0) + result = &lang->kindTable [i]; return result; } -extern bool isLanguageKindEnabled (const langType language, char kind) +extern bool isLanguageKindEnabled (const langType language, int kindIndex) { - const kindOption *kindOpt; - - if (hasRegexKind (language, kind)) - return isRegexKindEnabled (language, kind); - else if (hasXcmdKind (language, kind)) - return isXcmdKindEnabled (language, kind); - - kindOpt = langKindOption (language, kind); - Assert (kindOpt); - - return kindOpt->enabled; + kindDefinition * kdef = getLanguageKind (language, kindIndex); + return kdef->enabled; } @@ -1666,7 +1692,7 @@ static void resetLanguageKinds (const langType language, const bool mode) { unsigned int i; for (i = 0 ; i < lang->kindCount ; ++i) - enableKind (lang->kinds + i, mode); + enableKind (lang->kindTable + i, mode); } } @@ -1674,7 +1700,7 @@ static bool enableLanguageKind ( const langType language, const int kind, const bool mode) { bool result = false; - kindOption* const opt = langKindOption (language, kind); + kindDefinition* const opt = langKindDefinition (language, kind); if (opt != NULL) { enableKind (opt, mode); @@ -1689,7 +1715,7 @@ static bool enableLanguageKindLong ( const langType language, const char * const kindLong, const bool mode) { bool result = false; - kindOption* const opt = langKindLongOption (language, kindLong); + kindDefinition* const opt = langKindLongOption (language, kindLong); if (opt != NULL) { enableKind (opt, mode); @@ -1700,7 +1726,7 @@ static bool enableLanguageKindLong ( return result; } -static void processLangKindOption ( +static void processLangKindDefinition ( const langType language, const char *const option, const char *const parameter) { @@ -1777,25 +1803,25 @@ static void processLangKindOption ( } } -struct langKindOptionStruct { +struct langKindDefinitionStruct { const char *const option; const char *const parameter; }; -static void processLangKindOptionEach( +static void processLangKindDefinitionEach( langType lang, void* user_data) { - struct langKindOptionStruct *arg = user_data; - processLangKindOption (lang, arg->option, arg->parameter); + struct langKindDefinitionStruct *arg = user_data; + processLangKindDefinition (lang, arg->option, arg->parameter); } -extern bool processKindOption ( +extern bool processKindDefinition ( const char *const option, const char *const parameter) { #define PREFIX "kinds-" #define PREFIX_LEN strlen(PREFIX) bool handled = false; - struct langKindOptionStruct arg = { + struct langKindDefinitionStruct arg = { .option = option, .parameter = parameter, }; @@ -1808,7 +1834,7 @@ extern bool processKindOption ( size_t len = dash - option; if ((len == 1) && (*option == '*')) - foreachLanguage(processLangKindOptionEach, &arg); + foreachLanguage(processLangKindDefinitionEach, &arg); else { vString* langName = vStringNew (); @@ -1817,7 +1843,7 @@ extern bool processKindOption ( if (language == LANG_IGNORE) error (WARNING, "Unknown language \"%s\" in \"%s\" option", vStringValue (langName), option); else - processLangKindOption (language, option, parameter); + processLangKindDefinition (language, option, parameter); vStringDelete (langName); } handled = true; @@ -1833,7 +1859,7 @@ extern bool processKindOption ( error (WARNING, "No language given in \"%s\" option", option); else if (len == 1 && lang[0] == '*') { - foreachLanguage(processLangKindOptionEach, &arg); + foreachLanguage(processLangKindDefinitionEach, &arg); handled = true; } else @@ -1843,7 +1869,7 @@ extern bool processKindOption ( error (WARNING, "Unknown language \"%s\" in \"%s\" option", lang, option); else { - processLangKindOption (language, option, parameter); + processLangKindDefinition (language, option, parameter); handled = true; } } @@ -1865,15 +1891,15 @@ static void printRoles (const langType language, const char* letters, bool allow for (c = letters; *c != '\0'; c++) { unsigned int i; - const kindOption *k; + const kindDefinition *k; for (i = 0; i < lang->kindCount; ++i) { - k = lang->kinds + i; + k = lang->kindTable + i; if (*c == KIND_WILDCARD || k->letter == *c) { int j; - const roleDesc *r; + const roleDefinition *r; for (j = 0; j < k->nRoles; j++) { @@ -1925,14 +1951,14 @@ static void printKinds (langType language, bool allKindFields, bool indent) initializeParser (language); lang = LanguageTable [language]; - if (lang->kinds != NULL) + if (lang->kindTable != NULL) { unsigned int i; for (i = 0 ; i < lang->kindCount ; ++i) { if (allKindFields && indent) printf (Option.machinable? "%s": PR_KIND_FMT (LANG,s), lang->name); - printKind (lang->kinds + i, allKindFields, indent, Option.machinable); + printKind (lang->kindTable + i, allKindFields, indent, Option.machinable); } } printRegexKinds (language, allKindFields, indent, Option.machinable); @@ -2095,7 +2121,7 @@ static void printLanguage (const langType language, parserDefinition** ltable) if (lang->method & METHOD_XCMD) initializeParser (lang->id); - if (lang->kinds != NULL || (lang->method & METHOD_REGEX) || (lang->method & METHOD_XCMD)) + if (lang->kindTable != NULL || (lang->method & METHOD_REGEX) || (lang->method & METHOD_XCMD)) printf ("%s%s\n", lang->name, isLanguageEnabled (lang->id) ? "" : " [disabled]"); } @@ -2153,6 +2179,8 @@ static bool createTagsWithFallback1 (const langType language) addParserPseudoTags (language); tagFilePosition (&tagfpos); + anonResetMaybe (LanguageTable [language]); + while ( ( whyRescan = createTagsForFile (language, ++passCount) ) != RESCAN_NONE) @@ -2199,6 +2227,8 @@ static bool createTagsWithFallback1 (const langType language, if (LanguageTable [language]->useCork) corkTagFile(); + anonResetMaybe (LanguageTable [language]); + passCallback(userData); while ( ( whyRescan = createTagsForFile (language, ++passCount) ) @@ -2284,10 +2314,13 @@ extern void createTagsWithFallback(unsigned char *buffer, size_t bufferSize, if ((!buffer && openInputFile (fileName, language, NULL)) || (buffer && bufferOpen (fileName, language, buffer, bufferSize))) { + initParserTrashBox (); + clearParsersUsedInCurrentInput (); setTagEntryFunction(tagCallback, userData); createTagsWithFallback1 (language, passCallback, userData); forcePromises (); closeInputFile (); + finiParserTrashBox (); } else error (WARNING, "Unable to open %s", fileName); @@ -2459,6 +2492,9 @@ extern bool parseFile (const char *const fileName) #endif setupWriter (); + + clearParsersUsedInCurrentInput (); + #ifndef CTAGS_LIB tagFileResized = createTagsWithFallback (fileName, language, mio); #endif @@ -2581,7 +2617,7 @@ extern bool makeKindSeparatorsPseudoTags (const langType language, const ptagDesc *pdesc) { parserDefinition* lang; - kindOption *kinds; + kindDefinition *kinds; unsigned int kindCount; unsigned int i, j; @@ -2589,7 +2625,7 @@ extern bool makeKindSeparatorsPseudoTags (const langType language, Assert (0 <= language && language < (int) LanguageCount); lang = LanguageTable [language]; - kinds = lang->kinds; + kinds = lang->kindTable; kindCount = lang->kindCount; if (kinds == NULL) @@ -2605,17 +2641,17 @@ extern bool makeKindSeparatorsPseudoTags (const langType language, for (j = 0; j < kinds[i].separatorCount; ++j) { char name[5] = {[0] = '/', [3] = '/', [4] = '\0'}; - const kindOption *upperKind; + const kindDefinition *upperKind; const scopeSeparator *sep; sep = kinds[i].separators + j; - if (sep->parentLetter == KIND_WILDCARD) + if (sep->parentKindIndex == KIND_WILDCARD_INDEX) { name[1] = KIND_WILDCARD; name[2] = kinds[i].letter; } - else if (sep->parentLetter == KIND_NULL) + else if (sep->parentKindIndex == KIND_GHOST_INDEX) { /* This is root separator: no upper item is here. */ name[1] = kinds[i].letter; @@ -2624,8 +2660,8 @@ extern bool makeKindSeparatorsPseudoTags (const langType language, } else { - upperKind = langKindOption (language, - sep->parentLetter); + upperKind = langKindDefinition (language, + sep->parentKindIndex); if (!upperKind) continue; @@ -2651,7 +2687,7 @@ struct makeKindDescriptionPseudoTagData { bool written; }; -static bool makeKindDescriptionPseudoTag (kindOption *kind, +static bool makeKindDescriptionPseudoTag (kindDefinition *kind, void *user_data) { struct makeKindDescriptionPseudoTagData *data = user_data; @@ -2685,13 +2721,13 @@ extern bool makeKindDescriptionsPseudoTags (const langType language, { parserDefinition* lang; - kindOption *kinds; + kindDefinition *kinds; unsigned int kindCount, i; struct makeKindDescriptionPseudoTagData data; Assert (0 <= language && language < (int) LanguageCount); lang = LanguageTable [language]; - kinds = lang->kinds; + kinds = lang->kindTable; kindCount = lang->kindCount; data.langName = lang->name; @@ -2716,12 +2752,25 @@ extern bool makeKindDescriptionsPseudoTags (const langType language, * Anonymous name generator */ -extern void anonReset (void) +static void clearParsersUsedInCurrentInput (void) { - parserDefinition* lang = LanguageTable [getInputLanguage ()]; - lang -> anonumousIdentiferId = 0; + if (parsersUsedInCurrentInput) + ptrArrayClear (parsersUsedInCurrentInput); + else + parsersUsedInCurrentInput = ptrArrayNew (NULL); } +static void anonResetMaybe (parserDefinition *lang) +{ + if (ptrArrayHas (parsersUsedInCurrentInput, lang)) + return; + + lang -> anonumousIdentiferId = 0; + ptrArrayAdd (parsersUsedInCurrentInput, lang); +} + +/* GEANY DIFF */ +#if 0 static unsigned int anonHash(const unsigned char *str) { unsigned int hash = 5381; @@ -2732,6 +2781,8 @@ static unsigned int anonHash(const unsigned char *str) return hash ; } +#endif +/* GEANY DIFF END */ extern void anonGenerate (vString *buffer, const char *prefix, int kind) { @@ -2742,8 +2793,12 @@ extern void anonGenerate (vString *buffer, const char *prefix, int kind) vStringCopyS(buffer, prefix); - unsigned int uHash = anonHash((const unsigned char *)getInputFileName()); - sprintf(szNum,"%08x%02x%02x",uHash,lang -> anonumousIdentiferId, kind); +/* GEANY DIFF */ +/* unsigned int uHash = anonHash((const unsigned char *)getInputFileName()); + sprintf(szNum,"%08x%02x%02x",uHash,lang -> anonumousIdentiferId, kind); */ + sprintf(szNum,"%u", lang -> anonumousIdentiferId); +/* GEANY DIFF END */ + vStringCatS(buffer,szNum); } @@ -2760,11 +2815,11 @@ typedef enum { R_BROKEN_REF, } CTST_BrokenRole; -static roleDesc CTST_BrokenRoles [] = { +static roleDefinition CTST_BrokenRoles [] = { {true, "broken", "broken" }, }; -static kindOption CTST_Kinds[KIND_COUNT] = { +static kindDefinition CTST_Kinds[KIND_COUNT] = { {true, 'b', "broken tag", "name with unwanted characters", .referenceOnly = false, ATTACH_ROLES (CTST_BrokenRoles) }, }; @@ -2785,7 +2840,7 @@ static void createCTSTTags (void) switch (i) { case K_BROKEN: - initTagEntry (&e, "one\nof\rbroken\tname", &CTST_Kinds[i]); + initTagEntry (&e, "one\nof\rbroken\tname", i); e.extensionFields.scopeKind = & (CTST_Kinds [K_BROKEN]); e.extensionFields.scopeName = "\\Broken\tContext"; makeTagEntry (&e); @@ -2801,7 +2856,7 @@ static parserDefinition *CTagsSelfTestParser (void) static const char *const extensions[] = { NULL }; parserDefinition *const def = parserNew ("CTagsSelfTest"); def->extensions = extensions; - def->kinds = CTST_Kinds; + def->kindTable = CTST_Kinds; def->kindCount = KIND_COUNT; def->parser = createCTSTTags; def->invisible = true; diff --git a/ctags/main/parse.h b/ctags/main/parse.h index fb995a3b..efd38c35 100644 --- a/ctags/main/parse.h +++ b/ctags/main/parse.h @@ -127,9 +127,9 @@ typedef struct { struct sParserDefinition { /* defined by parser */ char* name; /* name of language */ - kindOption* kinds; /* tag kinds handled by parser */ + kindDefinition* kindTable; /* tag kinds handled by parser */ unsigned int kindCount; /* size of `kinds' list */ - kindOption* fileKind; /* kind for overriding the default fileKind */ + kindDefinition* fileKind; /* kind for overriding the default fileKind */ const char *const *extensions; /* list of default extensions */ const char *const *patterns; /* list of default file name patterns */ const char *const *aliases; /* list of default aliases (alternative names) */ @@ -149,8 +149,8 @@ struct sParserDefinition { tagXpathTableTable *tagXpathTableTable; unsigned int tagXpathTableCount; bool invisible; - fieldSpec *fieldSpecs; - unsigned int fieldSpecCount; + fieldDefinition *fieldTable; + unsigned int fieldCount; parserDependency * dependencies; unsigned int dependencyCount; @@ -204,19 +204,22 @@ extern parserDefinitionFunc YAML_PARSER_LIST; /* Language processing and parsing */ -extern int makeSimpleTag (const vString* const name, kindOption* const kinds, const int kind); -extern int makeSimpleRefTag (const vString* const name, kindOption* const kinds, const int kind, +extern int makeSimpleTag (const vString* const name, const int kind); +extern int makeSimpleRefTag (const vString* const name, const int kindIndexS, int roleIndex); extern parserDefinition* parserNew (const char* name); extern parserDefinition* parserNewFull (const char* name, char fileKind); +extern kindDefinition* getLanguageKind(const langType language, int kindIndex); extern bool doesLanguageAllowNullTag (const langType language); extern bool doesLanguageRequestAutomaticFQTag (const langType language); extern const char *getLanguageName (const langType language); -extern kindOption* getLanguageFileKind (const langType language); +extern kindDefinition* getLanguageFileKind (const langType language); extern langType getNamedLanguage (const char *const name, size_t len); extern langType getFileLanguage (const char *const fileName); +extern langType getLanguageForCommand (const char *const command, langType startFrom); +extern langType getLanguageForFilename (const char *const filename, langType startFrom); extern bool isLanguageEnabled (const langType language); -extern bool isLanguageKindEnabled (const langType language, char kind); +extern bool isLanguageKindEnabled (const langType language, int kindIndex); extern void installLanguageMapDefault (const langType language); extern void installLanguageMapDefaults (void); @@ -282,7 +285,7 @@ extern bool isRegexKindEnabled (const langType language, const int kind); extern bool hasRegexKind (const langType language, const int kind); extern void printRegexKinds (const langType language, bool allKindFields, bool indent, bool tabSeparated); -extern void foreachRegexKinds (const langType language, bool (* func) (kindOption*, void*), void *data); +extern void foreachRegexKinds (const langType language, bool (* func) (kindDefinition*, void*), void *data); extern void freeRegexResources (void); extern bool checkRegex (void); extern void useRegexMethod (const langType language); @@ -301,7 +304,7 @@ extern bool isXcmdKindEnabled (const langType language, const int kind); extern bool hasXcmdKind (const langType language, const int kind); extern void printXcmdKinds (const langType language, bool allKindFields, bool indent, bool tabSeparated); -extern void foreachXcmdKinds (const langType language, bool (* func) (kindOption*, void*), void *data); +extern void foreachXcmdKinds (const langType language, bool (* func) (kindDefinition*, void*), void *data); extern void freeXcmdResources (void); extern void useXcmdMethod (const langType language); extern void notifyAvailabilityXcmdMethod (const langType language); @@ -309,7 +312,7 @@ extern void notifyAvailabilityXcmdMethod (const langType language); /* Xpath interface */ extern void findXMLTags (xmlXPathContext *ctx, xmlNode *root, const tagXpathTableTable *xpathTableTable, - const kindOption* const kinds, void *userData); + const kindDefinition* const kinds, void *userData); extern void addTagXpath (const langType language, tagXpathTable *xpathTable); @@ -318,7 +321,6 @@ extern bool makeKindSeparatorsPseudoTags (const langType language, extern bool makeKindDescriptionsPseudoTags (const langType language, const ptagDesc *pdesc); -extern void anonReset (void); extern void anonGenerate (vString *buffer, const char *prefix, int kind); #endif /* CTAGS_MAIN_PARSE_H */ diff --git a/ctags/main/parsers.h b/ctags/main/parsers.h index be322335..254278fe 100644 --- a/ctags/main/parsers.h +++ b/ctags/main/parsers.h @@ -23,7 +23,7 @@ PerlParser, \ PhpParser, \ PythonParser, \ - LaTeXParser, \ + TexParser, \ AsmParser, \ ConfParser, \ SqlParser, \ @@ -42,13 +42,13 @@ JavaScriptParser, \ HaskellParser, \ CsharpParser, \ - FreeBasicParser,\ + BasicParser,\ HaxeParser,\ - RestParser, \ + RstParser, \ HtmlParser, \ F77Parser, \ GLSLParser, \ - MatlabParser, \ + MatLabParser, \ ValaParser, \ ActionScriptParser, \ NsisParser, \ diff --git a/ctags/main/promise.c b/ctags/main/promise.c index 323c0b45..82d6be66 100644 --- a/ctags/main/promise.c +++ b/ctags/main/promise.c @@ -38,8 +38,12 @@ int makePromise (const char *parser, int r; langType lang; +/* GEANY DIFF */ +/* if (!isXtagEnabled (XTAG_TAGS_GENERATED_BY_SUB_PARSERS)) return -1; +*/ +/* GEANY DIFF END */ lang = getNamedLanguage (parser, 0); if (lang == LANG_IGNORE) diff --git a/ctags/main/ptrarray.c b/ctags/main/ptrarray.c index 82f2e7e8..d45a6621 100644 --- a/ctags/main/ptrarray.c +++ b/ctags/main/ptrarray.c @@ -13,9 +13,11 @@ #include "general.h" /* must always come first */ #include +#include #include "debug.h" #include "ptrarray.h" +#include "routines.h" /* * DATA DECLARATIONS @@ -125,6 +127,16 @@ extern bool ptrArrayHasTest (const ptrArray *const current, return result; } +static bool ptrEq (const void *ptr, void *userData) +{ + return (ptr == userData); +} + +extern bool ptrArrayHas (const ptrArray *const current, void *ptr) +{ + return ptrArrayHasTest (current, ptrEq, ptr); +} + extern void ptrArrayReverse (const ptrArray *const current) { unsigned int i, j; @@ -150,3 +162,19 @@ extern void ptrArrayDeleteItem (ptrArray* const current, unsigned int indx) (current->count - indx) * sizeof (*current->array)); --current->count; } + +static int (*ptrArraySortCompareVar)(const void *, const void *); + +static int ptrArraySortCompare(const void *a0, const void *b0) +{ + void *const *a = (void *const *)a0; + void *const *b = (void *const *)b0; + + return ptrArraySortCompareVar (*a, *b); +} + +extern void ptrArraySort (ptrArray *const current, int (*compare)(const void *, const void *)) +{ + ptrArraySortCompareVar = compare; + qsort (current->array, current->count, sizeof (void *), ptrArraySortCompare); +} diff --git a/ctags/main/ptrarray.h b/ctags/main/ptrarray.h index 144bb0a7..2c0fd779 100644 --- a/ctags/main/ptrarray.h +++ b/ctags/main/ptrarray.h @@ -13,6 +13,7 @@ * INCLUDE FILES */ #include "general.h" /* must always come first */ +#include "types.h" /* * DATA DECLARATIONS @@ -21,7 +22,6 @@ typedef void (*ptrArrayDeleteFunc) (void *data); struct sPtrArray; -typedef struct sPtrArray ptrArray; /* * FUNCTION PROTOTYPES @@ -36,11 +36,13 @@ extern unsigned int ptrArrayCount (const ptrArray *const current); extern void* ptrArrayItem (const ptrArray *const current, const unsigned int indx); extern void* ptrArrayLast (const ptrArray *const current); extern void ptrArrayDelete (ptrArray *const current); -extern bool ptrArrayHasInsensitive (const ptrArray *const current, const void *const ptr); extern bool ptrArrayHasTest (const ptrArray *const current, bool (*test)(const void *ptr, void *userData), void *userData); +extern bool ptrArrayHas (const ptrArray *const current, void *ptr); extern void ptrArrayReverse (const ptrArray *const current); extern void ptrArrayDeleteItem (ptrArray* const current, unsigned int indx); +extern void ptrArraySort (ptrArray *const current, int (*compare)(const void *, const void *)); + #endif /* CTAGS_MAIN_PTRARRAY_H */ diff --git a/ctags/main/read.c b/ctags/main/read.c index 6fc1ac6d..6a1750b5 100644 --- a/ctags/main/read.c +++ b/ctags/main/read.c @@ -108,6 +108,7 @@ typedef struct sInputFile { inputLineFposMap lineFposMap; } inputFile; +static langType sourceLang; /* * FUNCTION DECLARATIONS @@ -183,9 +184,9 @@ extern bool isInputHeaderFile (void) return File.input.isHeader; } -extern bool isInputLanguageKindEnabled (char c) +extern bool isInputLanguageKindEnabled (int kindIndex) { - return isLanguageKindEnabled (getInputLanguage (), c); + return isLanguageKindEnabled (getInputLanguage (), kindIndex); } extern bool doesInputLanguageAllowNullTag (void) @@ -193,7 +194,7 @@ extern bool doesInputLanguageAllowNullTag (void) return doesLanguageAllowNullTag (getInputLanguage ()); } -extern kindOption *getInputLanguageFileKind (void) +extern kindDefinition *getInputLanguageFileKind (void) { return getLanguageFileKind (getInputLanguage ()); } @@ -208,9 +209,9 @@ extern const char *getSourceFileTagPath (void) return vStringValue (File.source.tagPath); } -extern const char *getSourceLanguageName (void) +extern langType getSourceLanguage (void) { - return getLanguageName (File.source.langInfo.type); + return sourceLang; } extern unsigned long getSourceLineNumber (void) @@ -370,6 +371,7 @@ static void setSourceFileParameters (vString *const fileName, const langType lan setInputFileParametersCommon (&File.source, fileName, language, setLangToType, File.sourceTagPathHolder); + sourceLang = language; } static bool setSourceFileName (vString *const fileName) diff --git a/ctags/main/read.h b/ctags/main/read.h index ca14f084..b0ac8e75 100644 --- a/ctags/main/read.h +++ b/ctags/main/read.h @@ -64,9 +64,9 @@ extern const char *getInputLanguageName (void); extern const char *getInputFileTagPath (void); extern bool isInputLanguage (langType lang); extern bool isInputHeaderFile (void); -extern bool isInputLanguageKindEnabled (char c); +extern bool isInputLanguageKindEnabled (int kindIndex); extern bool doesInputLanguageAllowNullTag (void); -extern kindOption *getInputLanguageFileKind (void); +extern kindDefinition *getInputLanguageFileKind (void); extern bool doesInputLanguageRequestAutomaticFQTag (void); extern void freeInputFileResources (void); @@ -100,7 +100,7 @@ enum nestedInputBoundaryFlag { extern unsigned int getNestedInputBoundaryInfo (unsigned long lineNumber); extern const char *getSourceFileTagPath (void); -extern const char *getSourceLanguageName (void); +extern langType getSourceLanguage (void); extern unsigned long getSourceLineNumber (void); /* Raw: reading from given a parameter, fp */ diff --git a/ctags/main/trace.h b/ctags/main/trace.h new file mode 100644 index 00000000..cb8848cc --- /dev/null +++ b/ctags/main/trace.h @@ -0,0 +1 @@ +/* Dummy header - only included by some parsers */ diff --git a/ctags/main/trashbox.c b/ctags/main/trashbox.c new file mode 100644 index 00000000..2ace4f67 --- /dev/null +++ b/ctags/main/trashbox.c @@ -0,0 +1,246 @@ +/* +* +* Copyright (c) 2014, Red Hat, Inc. +* Copyright (c) 2014, Masatake YAMATO +* +* This source code is released for free distribution under the terms of the +* GNU General Public License. +* +*/ + +#include "general.h" + +#include "debug.h" +#include "routines.h" +#include "trashbox.h" + +typedef TrashBoxDestroyItemProc TrashDestroyItemProc; +typedef struct sTrash { + void* item; + struct sTrash* next; + TrashDestroyItemProc destrctor; +} Trash; + +struct sTrashBox { + Trash *trash; +}; + +static TrashBox* defaultTrashBox; +static TrashBox* parserTrashBox; + +static Trash* trashPut (Trash* trash, void* item, + TrashDestroyItemProc destrctor); +static Trash* trashTakeBack (Trash* trash, void* item, TrashDestroyItemProc* destrctor); +static Trash* trashMakeEmpty (Trash* trash); + +extern TrashBox* trashBoxNew (void) +{ + TrashBox *t = xMalloc (1, TrashBox); + t->trash = NULL; + return t; +} + +extern TrashBox* trashBoxStack (TrashBox* trash_box) +{ + TrashBox *t = trashBoxNew(); + + if (!trash_box) + trash_box = defaultTrashBox; + + trashBoxPut (trash_box, t, (TrashBoxDestroyItemProc) trashBoxDelete); + + return t; +} + +extern void trashBoxDelete (TrashBox* trash_box) +{ + if (!trash_box) + trash_box = defaultTrashBox; + + trashBoxMakeEmpty(trash_box); + + eFree (trash_box); +} + +extern void* trashBoxPut (TrashBox* trash_box, void* item, TrashBoxDestroyItemProc destroy) +{ + if (!trash_box) + trash_box = defaultTrashBox; + + trash_box->trash = trashPut(trash_box->trash, item, destroy); + return item; +} + +extern TrashBoxDestroyItemProc trashBoxTakeBack (TrashBox* trash_box, void* item) +{ + TrashBoxDestroyItemProc d; + + if (!trash_box) + trash_box = defaultTrashBox; + + trash_box->trash = trashTakeBack(trash_box->trash, item, &d); + return d; +} + +extern void trashBoxMakeEmpty (TrashBox* trash_box) +{ + if (!trash_box) + trash_box = defaultTrashBox; + + trash_box->trash = trashMakeEmpty (trash_box->trash); +} + + +extern void trashBoxFree (TrashBox* trash_box, void* item) +{ + TrashBoxDestroyItemProc d; + + if (!trash_box) + trash_box = defaultTrashBox; + + d = trashBoxTakeBack (trash_box, item); + d (item); +} + +static Trash* trashPut (Trash* trash, void* item, + TrashDestroyItemProc destrctor) +{ + Trash* t = xMalloc (1, Trash); + t->next = trash; + t->item = item; + t->destrctor = destrctor? destrctor: eFree; + return t; +} + +static TrashBoxDestroyItemProc trashTakeBack0 (Trash** trash, void* item) +{ + TrashBoxDestroyItemProc removed; + Trash* tmp; + + removed = NULL; + while (*trash) + { + if ( (*trash)->item == item ) + { + tmp = *trash; + *trash = (*trash)->next; + tmp->next = NULL; + tmp->item = NULL; + removed = tmp->destrctor; + + eFree (tmp); + tmp = NULL; + break; + } + else + trash = &(*trash)->next; + } + + Assert (removed); + return removed; +} + +static Trash* trashTakeBack (Trash* trash, void* item, TrashDestroyItemProc *destrctor) +{ + TrashDestroyItemProc d; + d = trashTakeBack0 (&trash, item); + if (destrctor) + *destrctor = d; + + return trash; +} + +static Trash* trashMakeEmpty (Trash* trash) +{ + Trash* tmp; + + while (trash) + { + tmp = trash; + trash = trash->next; + tmp->destrctor (tmp->item); + tmp->item = NULL; + tmp->destrctor = NULL; + eFree (tmp); + } + return NULL; +} + +extern void initDefaultTrashBox (void) +{ + defaultTrashBox = trashBoxNew (); +} + +extern void finiDefaultTrashBox (void) +{ + trashBoxDelete (defaultTrashBox); + defaultTrashBox = NULL; +} + +extern void initParserTrashBox (void) +{ + parserTrashBox = trashBoxNew (); +} + +extern void finiParserTrashBox (void) +{ + trashBoxDelete (parserTrashBox); + parserTrashBox = NULL; +} + +extern void* parserTrashBoxPut (void* item, TrashBoxDestroyItemProc destroy) +{ + return trashBoxPut(parserTrashBox, item, destroy); +} + +extern TrashBoxDestroyItemProc parserTrashBoxTakeBack (void* item) +{ + return trashBoxTakeBack(parserTrashBox, item); +} + +#ifdef TRASH_TEST +#include + +int main (void) +{ + Trash* trash = NULL; + Trash* tmp; + char* d = eStrdup ("d"); + char* b = eStrdup ("b"); + + trash = trashPut (trash, eStrdup ("a")); + trash = trashPut (trash, b); + trash = trashPut (trash, eStrdup ("c")); + trash = trashPut (trash, d); + + trash = trashTakeBack (trash, b, NULL); + eFree (b); + + fputs("expects: dca\nactual: ", stderr); + for (tmp = trash; tmp; tmp = tmp->next) + fputs(tmp->item, stderr); + fputc('\n', stderr); + + + trash = trashTakeBack (trash, d, NULL); + eFree (d); + + fputs("expects: ca\nactual: ", stderr); + for (tmp = trash; tmp; tmp = tmp->next) + fputs(tmp->item, stderr); + fputc('\n', stderr); + + trash = trashMakeEmpty (trash); + + fputs("expects: \nactual: ", stderr); + for (tmp = trash; tmp; tmp = tmp->next) + fputs(tmp->item, stderr); + fputc('\n', stderr); + return 0; +} + +#include +void eFree (void *ptr) { free(ptr); } +void *eMalloc (const size_t size) { return malloc(size); } +char *eStrdup (const char* str) { return strdup(str); } +#endif diff --git a/ctags/main/trashbox.h b/ctags/main/trashbox.h new file mode 100644 index 00000000..fef93e5b --- /dev/null +++ b/ctags/main/trashbox.h @@ -0,0 +1,41 @@ +/* +* +* Copyright (c) 2017, Red Hat, Inc. +* Copyright (c) 2017, Masatake YAMATO +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* Facility for delayed memory releasing, inspired from AutoreleasePool +* of OpenStep. +*/ + +#ifndef CTAGS_MAIN_TRASH_H +#define CTAGS_MAIN_TRASH_H + +typedef void (* TrashBoxDestroyItemProc) (void *); +typedef struct sTrashBox TrashBox; + +extern TrashBox* trashBoxNew (void); +extern TrashBox* trashBoxStack (TrashBox* trash_box); +extern void trashBoxDelete (TrashBox* trash_box); +extern void* trashBoxPut (TrashBox* trash_box, void* item, TrashBoxDestroyItemProc destroy); +extern TrashBoxDestroyItemProc trashBoxTakeBack (TrashBox* trash_box, void* item); +extern void trashBoxFree (TrashBox* trash_box, void* item); +extern void trashBoxMakeEmpty (TrashBox* trash_box); + +#define DEFAULT_TRASH_BOX(PTR,PROC) trashBoxPut(NULL,PTR,(TrashBoxDestroyItemProc)PROC) +#define DEFAULT_TRASH_BOX_TAKE_BACK(PTR) trashBoxTakeBack(NULL,PTR) + +#define PARSER_TRASH_BOX(PTR,PROC) parserTrashBoxPut(PTR,(TrashBoxDestroyItemProc)PROC) +#define PARSER_TRASH_BOX_TAKE_BACK(PTR) parserTrashBoxTakeBack(PTR) + +extern void initDefaultTrashBox (void); +extern void finiDefaultTrashBox (void); + +extern void initParserTrashBox (void); +extern void finiParserTrashBox (void); +extern void* parserTrashBoxPut (void* item, TrashBoxDestroyItemProc destroy); +extern TrashBoxDestroyItemProc parserTrashBoxTakeBack (void* item); + +#endif /* CTAGS_MAIN_TRASH_H */ diff --git a/ctags/main/types.h b/ctags/main/types.h index 6b2993c6..7a4af2a1 100644 --- a/ctags/main/types.h +++ b/ctags/main/types.h @@ -21,9 +21,13 @@ typedef struct sFieldDesc fieldDesc; struct sPtagDesc; typedef struct sPtagDesc ptagDesc; -struct sKindOption; -typedef struct sKindOption kindOption; +struct sKindDefinition; +typedef struct sKindDefinition kindDefinition; struct sParserDefinition; typedef struct sParserDefinition parserDefinition; + +struct sPtrArray; +typedef struct sPtrArray ptrArray; + #endif /* CTAGS_MAIN_TYPES_H */ diff --git a/ctags/main/vstring.c b/ctags/main/vstring.c index bb329a9b..2e33505b 100644 --- a/ctags/main/vstring.c +++ b/ctags/main/vstring.c @@ -19,6 +19,7 @@ #include "debug.h" #include "routines.h" #include "vstring.h" +#include "trashbox.h" /* * DATA DEFINITIONS @@ -79,18 +80,6 @@ extern vString *vStringNew (void) return string; } -#ifndef VSTRING_PUTC_MACRO -extern void vStringPut (vString *const string, const int c) -{ - if (string->length + 1 == string->size) /* check for buffer overflow */ - vStringResize (string, string->size * 2); - - string->buffer [string->length] = c; - if (c != '\0') - string->buffer [++string->length] = '\0'; -} -#endif - extern vString *vStringNewCopy (const vString *const string) { vString *vs = vStringNew (); @@ -105,13 +94,20 @@ extern vString *vStringNewInit (const char *const s) return vs; } +extern vString *vStringNewNInit (const char *const s, const size_t length) +{ + vString *vs = vStringNew (); + vStringNCatS (vs, s, length); + return vs; +} + static void stringCat ( vString *const string, const char *const s, const size_t length) { if (string->length + length + 1 > string->size) vStringResize (string, string->length + length + 1); - strncpy (string->buffer + string->length, s, length); + memcpy (string->buffer + string->length, s, length); string->length += length; vStringPut (string, '\0'); } @@ -168,13 +164,14 @@ extern void vStringStripNewline (vString *const string) */ extern void vStringStripLeading (vString *const string) { - while (isspace ((int) string->buffer [0]) && string->length > 0) + size_t n = 0; + + while (n < string->length && isspace ((int) string->buffer [n])) + n++; + if (n > 0) { - size_t i; - for (i = 1 ; i < string->length ; ++i) - string->buffer [i - 1] = string->buffer [i]; - --string->length; - string->buffer [string->length] = '\0'; + memmove (string->buffer, string->buffer + n, string->length - n); + vStringTruncate (string, string->length - n); } } @@ -364,3 +361,27 @@ extern vString *vStringNewOrClear (vString *const string) else return vStringNew (); } + +extern vString *vStringNewOrClearWithAutoRelease (vString *const string) +{ + vString *r; + + bool autoRelease = false; + if (!string) + autoRelease = true; + + r = vStringNewOrClear(string); + if (autoRelease) + DEFAULT_TRASH_BOX(r, vStringDelete); + + return r; +} + +extern void vStringTranslate(vString *const string, char fromC, char toC) +{ + for (unsigned int i = 0; i < vStringLength(string); i++) + { + if (string->buffer[i] == fromC) + string->buffer[i] = toC; + } +} diff --git a/ctags/main/vstring.h b/ctags/main/vstring.h index 203a5c2f..5ac421d4 100644 --- a/ctags/main/vstring.h +++ b/ctags/main/vstring.h @@ -20,25 +20,18 @@ #include +#include "inline.h" #include "mio.h" /* * MACROS */ -#ifndef DEBUG -# define VSTRING_PUTC_MACRO 1 -#endif -#ifdef VSTRING_PUTC_MACRO -#define vStringPut(s,c) \ - (void)(((s)->length + 1 == (s)->size ? vStringResize ((s), (s)->size * 2) : 0), \ - ((s)->buffer [(s)->length] = (c)), \ - ((c) == '\0' ? 0 : ((s)->buffer [++(s)->length] = '\0'))) -#endif #define vStringValue(vs) ((vs)->buffer) #define vStringItem(vs,i) ((vs)->buffer[i]) #define vStringLast(vs) ((vs)->buffer[(vs)->length - 1]) #define vStringLength(vs) ((vs)->length) +#define vStringIsEmpty(vs) ((vs)->length == 0) #define vStringSize(vs) ((vs)->size) #define vStringChar(vs,i) ((vs)->buffer[i]) #define vStringLower(vs) toLowerString((vs)->buffer) @@ -66,9 +59,6 @@ typedef struct sVString { extern void vStringResize (vString *const string, const size_t newSize); extern vString *vStringNew (void); extern void vStringDelete (vString *const string); -#ifndef VSTRING_PUTC_MACRO -extern void vStringPut (vString *const string, const int c); -#endif extern void vStringStripNewline (vString *const string); extern void vStringStripLeading (vString *const string); extern void vStringChop (vString *const string); @@ -79,6 +69,7 @@ extern void vStringNCat (vString *const string, const vString *const s, const si extern void vStringNCatS (vString *const string, const char *const s, const size_t length); extern vString *vStringNewCopy (const vString *const string); extern vString *vStringNewInit (const char *const s); +extern vString *vStringNewNInit (const char *const s, const size_t length); extern void vStringCopy (vString *const string, const vString *const s); extern void vStringCopyS (vString *const string, const char *const s); extern void vStringNCopy (vString *const string, const vString *const s, const size_t length); @@ -86,8 +77,10 @@ extern void vStringNCopyS (vString *const string, const char *const s, const siz extern void vStringCopyToLower (vString *const dest, const vString *const src); extern void vStringSetLength (vString *const string); extern void vStringTruncate (vString *const string, const size_t length); +extern void vStringTranslate(vString *const string, char fromC, char toC); extern vString *vStringNewOrClear (vString *const string); +extern vString *vStringNewOrClearWithAutoRelease (vString *const string); extern vString *vStringNewOwn (char *s); extern char *vStringDeleteUnwrap (vString *const string); @@ -95,4 +88,18 @@ extern char *vStringDeleteUnwrap (vString *const string); extern void vStringCatSWithEscaping (vString* b, const char *s); extern void vStringCatSWithEscapingAsPattern (vString *output, const char* input); +/* +* INLINE FUNCTIONS +*/ + +CTAGS_INLINE void vStringPut (vString *const string, const int c) +{ + if (string->length + 1 == string->size) /* check for buffer overflow */ + vStringResize (string, string->size * 2); + + string->buffer [string->length] = c; + if (c != '\0') + string->buffer [++string->length] = '\0'; +} + #endif /* CTAGS_MAIN_VSTRING_H */ diff --git a/ctags/main/xtag.c b/ctags/main/xtag.c index 105a3657..f6d5a460 100644 --- a/ctags/main/xtag.c +++ b/ctags/main/xtag.c @@ -39,6 +39,8 @@ static xtagDesc xtagDescs [] = { "Include reference tags", NULL}, { false, 's', "subparser", "Include tags generated by sub parsers", NULL}, + { true, '\0', "anonymous", + "Include tags for non-named objects like lambda"}, }; extern xtagDesc* getXtagDesc (xtagType type) diff --git a/ctags/main/xtag.h b/ctags/main/xtag.h index 1d486e25..17b3c9eb 100644 --- a/ctags/main/xtag.h +++ b/ctags/main/xtag.h @@ -23,6 +23,7 @@ typedef enum eXtagType { /* extra tag content control */ XTAG_QUALIFIED_TAGS, XTAG_REFERENCE_TAGS, XTAG_TAGS_GENERATED_BY_SUB_PARSERS, + XTAG_ANONYMOUS, XTAG_COUNT } xtagType; diff --git a/ctags/parsers/abaqus.c b/ctags/parsers/abaqus.c index 660f2d9b..7df7abb8 100644 --- a/ctags/parsers/abaqus.c +++ b/ctags/parsers/abaqus.c @@ -28,7 +28,7 @@ typedef enum { K_STEP } AbaqusKind; -static kindOption AbaqusKinds[] = { +static kindDefinition AbaqusKinds[] = { { true, 'c', "class", "Parts" }, { true, 'm', "member", "Assembly" }, { true, 'n', "namespace", "Steps" } @@ -71,7 +71,7 @@ static void createTag(AbaqusKind kind, const char *buf) vStringPut(name, (int) *buf); ++buf; } while ((*buf != '\0') && (*buf != ',')); - makeSimpleTag(name, AbaqusKinds, kind); + makeSimpleTag(name, kind); vStringDelete(name); } @@ -118,7 +118,7 @@ extern parserDefinition* AbaqusParser (void) { static const char *const extensions [] = { "inp", NULL }; parserDefinition * def = parserNew ("Abaqus"); - def->kinds = AbaqusKinds; + def->kindTable = AbaqusKinds; def->kindCount = ARRAY_SIZE (AbaqusKinds); def->extensions = extensions; def->parser = findAbaqusTags; diff --git a/ctags/parsers/abc.c b/ctags/parsers/abc.c index 1a6f360d..8c0c83dc 100644 --- a/ctags/parsers/abc.c +++ b/ctags/parsers/abc.c @@ -27,7 +27,7 @@ * DATA DEFINITIONS */ -static kindOption AbcKinds[] = { +static kindDefinition AbcKinds[] = { { true, 'm', "member", "sections" }, { true, 's', "struct", "header1"} }; @@ -53,7 +53,7 @@ static kindOption AbcKinds[] = { static void makeAbcTag (const vString* const name, bool name_before) { tagEntryInfo e; - initTagEntry (&e, vStringValue(name), &(AbcKinds[0])); + initTagEntry (&e, vStringValue(name), 0); if (name_before) e.lineNumber--; /* we want the line before the underline chars */ @@ -114,7 +114,7 @@ extern parserDefinition* AbcParser (void) static const char *const extensions [] = { "abc", NULL }; parserDefinition* const def = parserNew ("Abc"); - def->kinds = AbcKinds; + def->kindTable = AbcKinds; def->kindCount = ARRAY_SIZE (AbcKinds); def->patterns = patterns; def->extensions = extensions; diff --git a/ctags/parsers/asciidoc.c b/ctags/parsers/asciidoc.c index e93f510b..86d375dc 100644 --- a/ctags/parsers/asciidoc.c +++ b/ctags/parsers/asciidoc.c @@ -1,47 +1,62 @@ /* -* -* Copyright (c) 2012, Lex Trotman -* Based on Rest code by Nick Treleaven, see rest.c -* -* This source code is released for free distribution under the terms of the -* GNU General Public License. -* -* This module contains functions for generating tags for asciidoc files. -*/ + * + * Copyright (c) 2007-2011, Nick Treleaven + * Copyright (c) 2012, Lex Trotman + * + * This source code is released for free distribution under the terms of the + * GNU General Public License version 2 or (at your option) any later version. + * + * This module contains functions for generating tags for asciidoc files. + * + * Based on Rest code by Nick Treleaven, see rest.c + * + * This code was ported from geany git commit 40396a3 at: + * https://github.com/geany/geany/blob/master/ctags/parsers/asciidoc.c + * with the changes in geany's PR #1263, with some changes to work in uctags. + */ /* -* INCLUDE FILES -*/ + * INCLUDE FILES + */ #include "general.h" /* must always come first */ #include #include +#include "debug.h" +#include "entry.h" #include "parse.h" #include "read.h" #include "vstring.h" #include "nestlevel.h" #include "routines.h" -#include "entry.h" /* -* DATA DEFINITIONS -*/ + * DATA DEFINITIONS + */ typedef enum { K_CHAPTER = 0, K_SECTION, K_SUBSECTION, K_SUBSUBSECTION, - K_LEVEL5SECTION, - SECTION_COUNT + K_LEVEL4SECTION, + /* level-5 section not in here because it only works for one-line */ + SECTION_COUNT, /* this is the same as level-5 kind number */ + K_ANCHOR } asciidocKind; -static kindOption AsciidocKinds[] = { - { true, 'n', "namespace", "chapters"}, - { true, 'm', "member", "sections" }, - { true, 'd', "macro", "level2sections" }, - { true, 'v', "variable", "level3sections" }, - { true, 's', "struct", "level4sections" } +/* + * The following kind letters are based on the markdown parser kinds, + * and thus different than geany's. + */ +static kindDefinition AsciidocKinds[] = { + { true, 'c', "chapter", "chapters"}, + { true, 's', "section", "sections" }, + { true, 'S', "subsection", "level 2 sections" }, + { true, 't', "subsubsection", "level 3 sections" }, + { true, 'T', "l4subsection", "level 4 sections" }, + { true, 'u', "l5subsection", "level 5 sections" }, + { true, 'a', "anchor", "anchors" } }; static char kindchars[SECTION_COUNT]={ '=', '-', '~', '^', '+' }; @@ -52,7 +67,7 @@ static NestingLevels *nestingLevels = NULL; * FUNCTION DEFINITIONS */ -static void popNestingLevelToKind(const int kind) +static NestingLevel *getNestingLevel(const int kind) { NestingLevel *nl; tagEntryInfo *e; @@ -61,30 +76,60 @@ static void popNestingLevelToKind(const int kind) { nl = nestingLevelsGetCurrent(nestingLevels); e = getEntryOfNestingLevel (nl); - if ((nl && (e == NULL)) || (e && (e->kind - AsciidocKinds) >= kind)) + if ((nl && (e == NULL)) || (e && (e->kindIndex >= kind))) nestingLevelsPop(nestingLevels); else break; } + return nl; } -static void makeAsciidocTag (const vString* const name, const int kind) +static int makeAsciidocTag (const vString* const name, const int kind, const bool two_line) { + const NestingLevel *const nl = getNestingLevel(kind); int r = CORK_NIL; - popNestingLevelToKind(kind); - if (vStringLength (name) > 0) { + tagEntryInfo *parent = getEntryOfNestingLevel (nl); tagEntryInfo e; - initTagEntry (&e, vStringValue (name), &(AsciidocKinds [kind])); + initTagEntry (&e, vStringValue (name), kind); - e.lineNumber--; /* we want the line before the '---' underline chars */ + if (two_line) + { + /* we want the line before the '---' underline chars */ + const unsigned long line = getInputLineNumber(); + Assert (line > 0); + if (line > 0) + { + e.lineNumber--; + e.filePosition = getInputFilePositionForLine(line - 1); + } + } + + if (parent && (parent->kindIndex < kind)) + { + /* + * This doesn't use Cork, but in this case I think this is better, + * because Cork would record the scopes of all parents in the chain + * which is weird for text section identifiers, and also this is + * what the rst.c reStructuredText parser does. + */ + e.extensionFields.scopeKindIndex = parent->kindIndex; + e.extensionFields.scopeName = parent->name; + } r = makeTagEntry (&e); } + return r; +} + +static int makeSectionAsciidocTag (const vString* const name, const int kind, const bool two_line) +{ + int r = makeAsciidocTag(name, kind, two_line); nestingLevelsPush(nestingLevels, r); + return r; } @@ -101,6 +146,137 @@ static int get_kind(char c) } +static bool is_anchor(const unsigned char *line) +{ + /* must be at least "[#a]" */ + return line[0] == '[' && (line[1] == '#' || line[1] == '['); +} + +static int capture_anchor(const unsigned char *const orig, int* captured_len) +{ + vString *name = vStringNew (); + int r = CORK_NIL; + const bool shorthand = orig[1] == '#' ? true : false; + bool is_valid = false; + bool seen_comma = false; + const unsigned char *line = orig; + + Assert (line[0] == '['); + Assert (line[1] == '#' || line[1] == '['); + + if (captured_len) *captured_len = 0; + + line += 2; + + while (*line != '\0') + { + if (*line == ']') + { + if (shorthand || line[1] == ']') + { + is_valid = true; + if (shorthand) line++; + else line += 2; + break; + } + /* otherwise it's not the end, keep going */ + } + + if (*line == ',') + seen_comma = true; + + if (!seen_comma) + vStringPut (name, *line); + + line++; + } + + if (is_valid && vStringLength (name) != 0) + { + r = makeAsciidocTag (name, K_ANCHOR, false); + + if (captured_len) + { + *captured_len = line - orig; + } + } + + vStringDelete (name); + return r; +} + + +/* skips any leading anchor(s) in a one-line title, generating tags for them */ +static int process_leading_anchors(const unsigned char *const begin) +{ + int captured_len = 0; + const unsigned char *current = begin; + + while (is_anchor(current) && capture_anchor(current, &captured_len) != CORK_NIL) + { + /* minimum is "[#a]" */ + Assert (captured_len >= 4); + current += captured_len; + while (isspace(*current)) ++current; + } + + return current - begin; +} + +static int process_trailing_anchor(const unsigned char *const begin, + const unsigned char *const end) +{ + int captured_len = 0; + const unsigned char *found = NULL; + + /* minimum is "[#a]" */ + if (*end == ']' && (end - begin) >= 4) + { + found = (const unsigned char*) strrchr((const char*) begin , '['); + if (found && ((end - found) >= 4)) + { + /* see if it's not shorthand [#a] but instead [[a]] */ + if (end[-1] == ']' && found > begin && found[-1] == '[') + --found; + + if (is_anchor (found)) + capture_anchor(found, &captured_len); + } + } + + return captured_len; +} + +static void process_name(vString *const name, const int kind, + const unsigned char *line, const int line_len) +{ + int start = kind + 1; + int end = line_len - 1; + + Assert (kind >= 0 && kind < K_ANCHOR); + Assert (line_len > start); + + vStringClear(name); + + while (line[end] == line[0]) --end; + while (isspace(line[start])) ++start; + while (isspace(line[end])) --end; + + if (start < end) + { + /* pop nesting levels, so that anchors get the parent's scope */ + getNestingLevel(kind); + end -= process_trailing_anchor(line + start, line + end); + start += process_leading_anchors(line + start); + } + + while (isspace(line[end])) --end; + + if (start < end) + vStringNCatS(name, (const char*)(&(line[start])), end - start + 1); +} + + /* computes the length of an UTF-8 string * if the string doesn't look like UTF-8, return -1 * FIXME consider East_Asian_Width Unicode property */ @@ -141,13 +317,22 @@ static void findAsciidocTags(void) while ((line = readLineFromInputFile()) != NULL) { + if (is_anchor (line)) + { + if (capture_anchor (line, NULL) != CORK_NIL) + { + vStringClear (name); + continue; + } + } + int line_len = strlen((const char*) line); int name_len_bytes = vStringLength(name); int name_len = utf8_strlen(vStringValue(name), name_len_bytes); /* if the name doesn't look like UTF-8, assume one-byte charset */ if (name_len < 0) name_len = name_len_bytes; - + /* if its a title underline, or a delimited block marking character */ if (line[0] == '=' || line[0] == '-' || line[0] == '~' || line[0] == '^' || line[0] == '+' || line[0] == '.' || @@ -155,7 +340,7 @@ static void findAsciidocTags(void) { int n_same; for (n_same = 1; line[n_same] == line[0]; ++n_same); - + /* is it a two line title or a delimited block */ if (n_same == line_len) { @@ -164,7 +349,7 @@ static void findAsciidocTags(void) { if (line[0] == in_block) in_block = '\0'; } - + /* if its a =_~^+ and the same length +-2 as the line before then its a title */ /* (except in the special case its a -- open block start line) */ else if ((line[0] == '=' || line[0] == '-' || line[0] == '~' || @@ -175,11 +360,11 @@ static void findAsciidocTags(void) int kind = get_kind((char)(line[0])); if (kind >= 0) { - makeAsciidocTag(name, kind); + makeSectionAsciidocTag(name, kind, true); continue; } } - + /* else if its 4 or more /+-.*_= (plus the -- special case) its a block start */ else if (((line[0] == '/' || line[0] == '+' || line[0] == '-' || line[0] == '.' || line[0] == '*' || line[0] == '_' || @@ -189,20 +374,14 @@ static void findAsciidocTags(void) in_block = line[0]; } } - + /* otherwise is it a one line title */ - else if (line[0] == '=' && n_same <= 5 && isspace(line[n_same]) && + else if (line[0] == '=' && n_same <= 6 && isspace(line[n_same]) && !in_block) { int kind = n_same - 1; - int start = n_same; - int end = line_len - 1; - while (line[end] == line[0])--end; - while (isspace(line[start]))++start; - while (isspace(line[end]))--end; - vStringClear(name); - vStringNCatS(name, (const char*)(&(line[start])), end - start + 1); - makeAsciidocTag(name, kind); + process_name(name, kind, line, line_len); + makeSectionAsciidocTag(name, kind, false); continue; } } @@ -216,16 +395,17 @@ static void findAsciidocTags(void) extern parserDefinition* AsciidocParser (void) { - static const char *const patterns [] = { "*.asciidoc", NULL }; - static const char *const extensions [] = { "asciidoc", NULL }; + static const char *const patterns [] = { "*.asc", "*.adoc", "*.asciidoc", NULL }; + static const char *const extensions [] = { "asc", "adoc", "asciidoc", NULL }; + parserDefinition* const def = parserNew ("Asciidoc"); - def->kinds = AsciidocKinds; + def->kindTable = AsciidocKinds; def->kindCount = ARRAY_SIZE (AsciidocKinds); def->patterns = patterns; def->extensions = extensions; def->parser = findAsciidocTags; - + /* do we even need to use Cork? */ def->useCork = true; return def; diff --git a/ctags/parsers/asm.c b/ctags/parsers/asm.c index 8003a238..35de6456 100644 --- a/ctags/parsers/asm.c +++ b/ctags/parsers/asm.c @@ -60,7 +60,7 @@ typedef struct { */ static langType Lang_asm; -static kindOption AsmKinds [] = { +static kindDefinition AsmKinds [] = { { true, 'd', "define", "defines" }, { true, 'l', "label", "labels" }, { true, 'm', "macro", "macros" }, @@ -152,7 +152,7 @@ static bool readPreProc (const unsigned char *const line) vStringPut (name, *cp); ++cp; } - makeSimpleTag (name, AsmKinds, K_DEFINE); + makeSimpleTag (name, K_DEFINE); } vStringDelete (name); return result; @@ -201,18 +201,18 @@ static void makeAsmTag ( if (found) { if (kind != K_NONE) - makeSimpleTag (name, AsmKinds, kind); + makeSimpleTag (name, kind); } else if (isDefineOperator (operator)) { if (! nameFollows) - makeSimpleTag (name, AsmKinds, K_DEFINE); + makeSimpleTag (name, K_DEFINE); } else if (labelCandidate) { operatorKind (name, &found); if (! found) - makeSimpleTag (name, AsmKinds, K_LABEL); + makeSimpleTag (name, K_LABEL); } } } @@ -352,7 +352,7 @@ extern parserDefinition* AsmParser (void) NULL }; parserDefinition* def = parserNew ("Asm"); - def->kinds = AsmKinds; + def->kindTable = AsmKinds; def->kindCount = ARRAY_SIZE (AsmKinds); def->extensions = extensions; def->patterns = patterns; diff --git a/ctags/parsers/basic.c b/ctags/parsers/basic.c index e0232ccf..b2036a29 100644 --- a/ctags/parsers/basic.c +++ b/ctags/parsers/basic.c @@ -39,7 +39,7 @@ typedef struct { BasicKind kind; } KeyWord; -static kindOption BasicKinds[] = { +static kindDefinition BasicKinds[] = { {true, 'c', "constant", "constants"}, {true, 'f', "function", "functions"}, {true, 'l', "label", "labels"}, @@ -119,7 +119,7 @@ static int extract_dim (char const *pos, vString * name, BasicKind kind) for (; *pos && !isspace (*pos) && *pos != '(' && *pos != ',' && *pos != '='; pos++) vStringPut (name, *pos); - makeSimpleTag (name, BasicKinds, kind); + makeSimpleTag (name, kind); /* if the line contains a ',', we have multiple declarations */ while (*pos && strchr (pos, ',')) @@ -140,7 +140,7 @@ static int extract_dim (char const *pos, vString * name, BasicKind kind) vStringClear (name); for (; *pos && !isspace (*pos) && *pos != '(' && *pos != ',' && *pos != '='; pos++) vStringPut (name, *pos); - makeSimpleTag (name, BasicKinds, kind); + makeSimpleTag (name, kind); } vStringDelete (name); @@ -192,7 +192,7 @@ static int match_keyword (const char *p, KeyWord const *kw) { p = extract_name (p, name); } - makeSimpleTag (name, BasicKinds, kw->kind); + makeSimpleTag (name, kw->kind); vStringDelete (name); return 1; } @@ -207,7 +207,7 @@ static void match_colon_label (char const *p) { vString *name = vStringNew (); vStringNCatS (name, p, end - p); - makeSimpleTag (name, BasicKinds, K_LABEL); + makeSimpleTag (name, K_LABEL); vStringDelete (name); } } @@ -240,11 +240,11 @@ static void findBasicTags (void) } } -parserDefinition *FreeBasicParser (void) +parserDefinition *BasicParser (void) { static char const *extensions[] = { "bas", "bi", "bb", "pb", NULL }; parserDefinition *def = parserNew ("FreeBasic"); - def->kinds = BasicKinds; + def->kindTable = BasicKinds; def->kindCount = ARRAY_SIZE (BasicKinds); def->extensions = extensions; def->parser = findBasicTags; diff --git a/ctags/parsers/c.c b/ctags/parsers/c.c index 56cf8bc0..f8982767 100644 --- a/ctags/parsers/c.c +++ b/ctags/parsers/c.c @@ -281,7 +281,7 @@ typedef enum CK_EXTERN_VARIABLE } cKind; -static kindOption CKinds [] = { +static kindDefinition CKinds [] = { { true, 'c', "class", "classes"}, { true, 'd', "macro", "macro definitions"}, { true, 'e', "enumerator", "enumerators (values inside an enumeration)"}, @@ -307,7 +307,7 @@ typedef enum DK_EXTERN_VARIABLE } dKind; -static kindOption DKinds [] = { +static kindDefinition DKinds [] = { { true, 'c', "class", "classes"}, { true, 'e', "enumerator", "enumerators (values inside an enumeration)"}, { true, 'f', "function", "function definitions"}, @@ -331,7 +331,7 @@ typedef enum JK_PACKAGE, JK_ENUMERATOR, JK_ENUMERATION } javaKind; -static kindOption JavaKinds [] = { +static kindDefinition JavaKinds [] = { { true, 'c', "class", "classes"}, { true, 'f', "field", "fields"}, { true, 'i', "interface", "interfaces"}, @@ -349,7 +349,7 @@ typedef enum CSK_NAMESPACE, CSK_PROPERTY, CSK_STRUCT, CSK_TYPEDEF } csharpKind; -static kindOption CsharpKinds [] = { +static kindDefinition CsharpKinds [] = { { true, 'c', "class", "classes"}, { true, 'd', "macro", "macro definitions"}, { true, 'e', "enumerator", "enumerators (values inside an enumeration)"}, @@ -372,7 +372,7 @@ typedef enum { VK_NAMESPACE, VK_PROPERTY, VK_SIGNAL, VK_STRUCT } valaKind; -static kindOption ValaKinds [] = { +static kindDefinition ValaKinds [] = { { true, 'c', "class", "classes"}, { true, 'd', "macro", "macro definitions"}, { true, 'e', "enumerator", "enumerators (values inside an enumeration)"}, @@ -1093,19 +1093,19 @@ static javaKind javaTagKind (const tagType type) return result; } -static const kindOption *tagKind (const tagType type) +static int kindIndexForType (const tagType type) { - const kindOption* result; + int result; if (isInputLanguage (Lang_java)) - result = &JavaKinds [javaTagKind (type)]; + result = javaTagKind (type); else if (isInputLanguage (Lang_csharp)) - result = &CsharpKinds [csharpTagKind (type)]; + result = csharpTagKind (type); else if (isInputLanguage (Lang_d)) - result = &DKinds [dTagKind (type)]; + result = dTagKind (type); else if (isInputLanguage (Lang_vala)) - result = &ValaKinds [valaTagKind (type)]; + result = valaTagKind (type); else - result = &CKinds [cTagKind (type)]; + result = cTagKind (type); return result; } @@ -1184,10 +1184,10 @@ static void addOtherFields (tagEntryInfo* const tag, const tagType type, (isMember (st) || st->parent->declaration == DECL_NAMESPACE)) { if (isType (st->context, TOKEN_NAME)) - tag->extensionFields.scopeKind = tagKind (TAG_CLASS); + tag->extensionFields.scopeKindIndex = kindIndexForType (TAG_CLASS); else - tag->extensionFields.scopeKind = - tagKind (declToTagType (parentDecl (st))); + tag->extensionFields.scopeKindIndex = + kindIndexForType (declToTagType (parentDecl (st))); tag->extensionFields.scopeName = vStringValue (scope); } if ((type == TAG_CLASS || type == TAG_INTERFACE || @@ -1435,7 +1435,7 @@ static void makeTag (const tokenInfo *const token, return; } - initTagEntry (&e, vStringValue (token->name), tagKind (type)); + initTagEntry (&e, vStringValue (token->name), kindIndexForType (type)); e.lineNumber = token->lineNumber; e.filePosition = token->filePosition; @@ -3131,7 +3131,7 @@ static rescanReason findCTags (const unsigned int passCount) Assert (passCount < 3); cppInit ((bool) (passCount > 1), isInputLanguage (Lang_csharp), isInputLanguage(Lang_cpp), - CKinds+CK_DEFINE); + CK_DEFINE); exception = (exception_t) setjmp (Exception); rescan = RESCAN_NONE; @@ -3238,7 +3238,7 @@ extern parserDefinition* CParser (void) { static const char *const extensions [] = { "c", "pc", "sc", NULL }; parserDefinition* def = parserNew ("C"); - def->kinds = CKinds; + def->kindTable = CKinds; def->kindCount = ARRAY_SIZE (CKinds); def->extensions = extensions; def->parser2 = findCTags; @@ -3257,7 +3257,7 @@ extern parserDefinition* CppParser (void) NULL }; parserDefinition* def = parserNew ("C++"); - def->kinds = CKinds; + def->kindTable = CKinds; def->kindCount = ARRAY_SIZE (CKinds); def->extensions = extensions; def->parser2 = findCTags; @@ -3269,7 +3269,7 @@ extern parserDefinition* JavaParser (void) { static const char *const extensions [] = { "java", NULL }; parserDefinition* def = parserNew ("Java"); - def->kinds = JavaKinds; + def->kindTable = JavaKinds; def->kindCount = ARRAY_SIZE (JavaKinds); def->extensions = extensions; def->parser2 = findCTags; @@ -3281,7 +3281,7 @@ extern parserDefinition* DParser (void) { static const char *const extensions [] = { "d", "di", NULL }; parserDefinition* def = parserNew ("D"); - def->kinds = DKinds; + def->kindTable = DKinds; def->kindCount = ARRAY_SIZE (DKinds); def->extensions = extensions; def->parser2 = findCTags; @@ -3293,7 +3293,7 @@ extern parserDefinition* GLSLParser (void) { static const char *const extensions [] = { "glsl", "frag", "vert", NULL }; parserDefinition* def = parserNew ("GLSL"); - def->kinds = CKinds; + def->kindTable = CKinds; def->kindCount = ARRAY_SIZE (CKinds); def->extensions = extensions; def->parser2 = findCTags; @@ -3305,7 +3305,7 @@ extern parserDefinition* FeriteParser (void) { static const char *const extensions [] = { "fe", NULL }; parserDefinition* def = parserNew ("Ferite"); - def->kinds = CKinds; + def->kindTable = CKinds; def->kindCount = ARRAY_SIZE (CKinds); def->extensions = extensions; def->parser2 = findCTags; @@ -3317,7 +3317,7 @@ extern parserDefinition* CsharpParser (void) { static const char *const extensions [] = { "cs", NULL }; parserDefinition* def = parserNew ("C#"); - def->kinds = CsharpKinds; + def->kindTable = CsharpKinds; def->kindCount = ARRAY_SIZE (CsharpKinds); def->extensions = extensions; def->parser2 = findCTags; @@ -3329,7 +3329,7 @@ extern parserDefinition* ValaParser (void) { static const char *const extensions [] = { "vala", NULL }; parserDefinition* def = parserNew ("Vala"); - def->kinds = ValaKinds; + def->kindTable = ValaKinds; def->kindCount = ARRAY_SIZE (ValaKinds); def->extensions = extensions; def->parser2 = findCTags; diff --git a/ctags/parsers/css.c b/ctags/parsers/css.c index e3cbb2ed..a540040c 100644 --- a/ctags/parsers/css.c +++ b/ctags/parsers/css.c @@ -6,19 +6,34 @@ **************************************************************************/ #include "general.h" -#include -#include +#include +#include #include "entry.h" -#include "parse.h" -#include "read.h" +#include "parse.h" +#include "read.h" #include "routines.h" +#define isSelectorChar(c) \ + /* attribute selectors are handled separately */ \ + (isalnum (c) || \ + (c) == '_' || /* allowed char */ \ + (c) == '-' || /* allowed char */ \ + (c) == '+' || /* allow all sibling in a single tag */ \ + (c) == '>' || /* allow all child in a single tag */ \ + (c) == '|' || /* allow namespace separator */ \ + (c) == '(' || /* allow pseudo-class arguments */ \ + (c) == ')' || \ + (c) == '.' || /* allow classes and selectors */ \ + (c) == ':' || /* allow pseudo classes */ \ + (c) == '*' || /* allow globs as P + * */ \ + (c) == '#') /* allow ids */ + typedef enum eCssKinds { K_CLASS, K_SELECTOR, K_ID } cssKind; -static kindOption CssKinds [] = { +static kindDefinition CssKinds [] = { { true, 'c', "class", "classes" }, { true, 's', "selector", "selectors" }, { true, 'i', "id", "identities" } @@ -37,23 +52,6 @@ typedef struct { } tokenInfo; -static bool isSelectorChar (const int c) -{ - /* attribute selectors are handled separately */ - return (isalnum (c) || - c == '_' || // allowed char - c == '-' || // allowed char - c == '+' || // allow all sibling in a single tag - c == '>' || // allow all child in a single tag - c == '|' || // allow namespace separator - c == '(' || // allow pseudo-class arguments - c == ')' || - c == '.' || // allow classes and selectors - c == ':' || // allow pseudo classes - c == '*' || // allow globs as P + * - c == '#'); // allow ids -} - static void parseSelector (vString *const string, const int firstChar) { int c = firstChar; @@ -226,7 +224,7 @@ static void findCssTags (void) if (CssKinds[kind].enabled) { tagEntryInfo e; - initTagEntry (&e, vStringValue (selector), &(CssKinds[kind])); + initTagEntry (&e, vStringValue (selector), kind); e.lineNumber = lineNumber; e.filePosition = filePosition; @@ -258,10 +256,9 @@ extern parserDefinition* CssParser (void) { static const char *const extensions [] = { "css", NULL }; parserDefinition* def = parserNew ("CSS"); - def->kinds = CssKinds; + def->kindTable = CssKinds; def->kindCount = ARRAY_SIZE (CssKinds); def->extensions = extensions; def->parser = findCssTags; return def; } - diff --git a/ctags/parsers/diff.c b/ctags/parsers/diff.c index 892bbdf9..372fb101 100644 --- a/ctags/parsers/diff.c +++ b/ctags/parsers/diff.c @@ -28,7 +28,7 @@ typedef enum { K_FUNCTION } diffKind; -static kindOption DiffKinds [] = { +static kindDefinition DiffKinds [] = { { true, 'f', "function", "functions"} }; @@ -109,7 +109,7 @@ static void findDiffTags (void) tmp++; } - makeSimpleTag (filename, DiffKinds, K_FUNCTION); + makeSimpleTag (filename, K_FUNCTION); vStringClear (filename); } @@ -125,7 +125,7 @@ extern parserDefinition* DiffParser (void) static const char *const patterns [] = { "*.diff", "*.patch", NULL }; static const char *const extensions [] = { "diff", NULL }; parserDefinition* const def = parserNew ("Diff"); - def->kinds = DiffKinds; + def->kindTable = DiffKinds; def->kindCount = ARRAY_SIZE (DiffKinds); def->patterns = patterns; def->extensions = extensions; diff --git a/ctags/parsers/docbook.c b/ctags/parsers/docbook.c index e9e79027..2c030583 100644 --- a/ctags/parsers/docbook.c +++ b/ctags/parsers/docbook.c @@ -34,7 +34,7 @@ typedef enum { K_APPENDIX } docbookKind; -static kindOption DocBookKinds [] = { +static kindDefinition DocBookKinds [] = { { true, 'f', "function", "chapters"}, { true, 'c', "class", "sections"}, { true, 'm', "member", "sect1"}, @@ -77,7 +77,7 @@ static void createTag(docbookKind kind, const char *buf) vStringPut(name, (int) *buf); ++buf; } while ((*buf != '\0') && (*buf != '"')); - makeSimpleTag(name, DocBookKinds, kind); + makeSimpleTag(name, kind); } @@ -143,7 +143,7 @@ extern parserDefinition* DocBookParser (void) static const char *const extensions [] = { "sgml", "docbook", NULL }; parserDefinition* def = parserNew ("Docbook"); def->extensions = extensions; - def->kinds = DocBookKinds; + def->kindTable = DocBookKinds; def->kindCount = ARRAY_SIZE (DocBookKinds); def->parser = findDocBookTags; return def; diff --git a/ctags/parsers/erlang.c b/ctags/parsers/erlang.c index a39edab5..d86b5cad 100644 --- a/ctags/parsers/erlang.c +++ b/ctags/parsers/erlang.c @@ -28,7 +28,7 @@ typedef enum { K_MACRO, K_FUNCTION, K_MODULE, K_RECORD, K_TYPE } erlangKind; -static kindOption ErlangKinds[] = { +static kindDefinition ErlangKinds[] = { {true, 'd', "macro", "macro definitions"}, {true, 'f', "function", "functions"}, {true, 'm', "module", "modules"}, @@ -78,11 +78,11 @@ static void makeMemberTag ( if (ErlangKinds [kind].enabled && vStringLength (identifier) > 0) { tagEntryInfo tag; - initTagEntry (&tag, vStringValue (identifier), &(ErlangKinds[kind])); + initTagEntry (&tag, vStringValue (identifier), kind); if (module != NULL && vStringLength (module) > 0) { - tag.extensionFields.scopeKind = &(ErlangKinds [K_MODULE]); + tag.extensionFields.scopeKindIndex = K_MODULE; tag.extensionFields.scopeName = vStringValue (module); } makeTagEntry (&tag); @@ -93,7 +93,7 @@ static void parseModuleTag (const unsigned char *cp, vString *const module) { vString *const identifier = vStringNew (); parseIdentifier (cp, identifier); - makeSimpleTag (identifier, ErlangKinds, K_MODULE); + makeSimpleTag (identifier, K_MODULE); /* All further entries go in the new module */ vStringCopy (module, identifier); @@ -104,7 +104,7 @@ static void parseSimpleTag (const unsigned char *cp, erlangKind kind) { vString *const identifier = vStringNew (); parseIdentifier (cp, identifier); - makeSimpleTag (identifier, ErlangKinds, kind); + makeSimpleTag (identifier, kind); vStringDelete (identifier); } @@ -181,7 +181,7 @@ extern parserDefinition *ErlangParser (void) { static const char *const extensions[] = { "erl", "ERL", "hrl", "HRL", NULL }; parserDefinition *def = parserNew ("Erlang"); - def->kinds = ErlangKinds; + def->kindTable = ErlangKinds; def->kindCount = ARRAY_SIZE (ErlangKinds); def->extensions = extensions; def->parser = findErlangTags; diff --git a/ctags/parsers/fortran.c b/ctags/parsers/fortran.c index 9a4f75c0..79a6180f 100644 --- a/ctags/parsers/fortran.c +++ b/ctags/parsers/fortran.c @@ -208,7 +208,7 @@ static bool NewLine = true; static unsigned int contextual_fake_count = 0; /* indexed by tagType */ -static kindOption FortranKinds [TAG_COUNT] = { +static kindDefinition FortranKinds [TAG_COUNT] = { { true, 'b', "blockData", "block data"}, { true, 'c', "common", "common blocks"}, { true, 'e', "entry", "entry points"}, @@ -487,22 +487,22 @@ static void makeFortranTag (tokenInfo *const token, tagType tag) const char *const name = vStringValue (token->string); tagEntryInfo e; - initTagEntry (&e, name, &(FortranKinds [token->tag])); + initTagEntry (&e, name, token->tag); if (token->tag == TAG_COMMON_BLOCK) - e.lineNumberEntry = (bool) (Option.locate != EX_PATTERN); + e.lineNumberEntry = canUseLineNumberAsLocator(); e.lineNumber = token->lineNumber; e.filePosition = token->filePosition; e.isFileScope = isFileScope (token->tag); - e.truncateLine = (bool) (token->tag != TAG_LABEL); + e.truncateLineAfterTag = (bool) (token->tag != TAG_LABEL); if (ancestorCount () > 0) { const tokenInfo* const scope = ancestorScope (); if (scope != NULL) { - e.extensionFields.scopeKind = &(FortranKinds [scope->tag]); + e.extensionFields.scopeKindIndex = scope->tag; e.extensionFields.scopeName = vStringValue (scope->string); } } @@ -2322,7 +2322,7 @@ extern parserDefinition* FortranParser (void) NULL }; parserDefinition* def = parserNew ("Fortran"); - def->kinds = FortranKinds; + def->kindTable = FortranKinds; def->kindCount = ARRAY_SIZE (FortranKinds); def->extensions = extensions; def->parser2 = findFortranTags; @@ -2342,7 +2342,7 @@ extern parserDefinition* F77Parser (void) NULL }; parserDefinition* def = parserNew ("F77"); - def->kinds = FortranKinds; + def->kindTable = FortranKinds; def->kindCount = ARRAY_SIZE (FortranKinds); def->extensions = extensions; def->parser2 = findFortranTags; diff --git a/ctags/parsers/go.c b/ctags/parsers/go.c index 2b56e54c..0571ed67 100644 --- a/ctags/parsers/go.c +++ b/ctags/parsers/go.c @@ -90,7 +90,7 @@ typedef enum { GOTAG_MEMBER } goKind; -static kindOption GoKinds[] = { +static kindDefinition GoKinds[] = { {true, 'p', "package", "packages"}, {true, 'f', "func", "functions"}, {true, 'c', "const", "constants"}, @@ -521,7 +521,7 @@ static void makeTag (tokenInfo *const token, const goKind kind, const char *const name = vStringValue (token->string); tagEntryInfo e; - initTagEntry (&e, name, &(GoKinds [kind])); + initTagEntry (&e, name, kind); if (!GoKinds [kind].enabled) return; @@ -535,7 +535,7 @@ static void makeTag (tokenInfo *const token, const goKind kind, if (parent_kind != GOTAG_UNDEFINED && parent_token != NULL) { - e.extensionFields.scopeKind = &(GoKinds[parent_kind]); + e.extensionFields.scopeKindIndex = parent_kind; e.extensionFields.scopeName = vStringValue (parent_token->string); } makeTagEntry (&e); @@ -820,7 +820,7 @@ extern parserDefinition *GoParser (void) { static const char *const extensions[] = { "go", NULL }; parserDefinition *def = parserNew ("Go"); - def->kinds = GoKinds; + def->kindTable = GoKinds; def->kindCount = ARRAY_SIZE (GoKinds); def->extensions = extensions; def->parser = findGoTags; diff --git a/ctags/parsers/haskell.c b/ctags/parsers/haskell.c index e73a4ef4..32602ec1 100644 --- a/ctags/parsers/haskell.c +++ b/ctags/parsers/haskell.c @@ -37,7 +37,7 @@ typedef enum { K_TYPE, K_CONSTRUCTOR, K_FUNCTION, K_MODULE } haskellKind; -static kindOption HaskellKinds [] = { +static kindDefinition HaskellKinds [] = { { true, 't', "typedef", "types" }, { true, 'c', "macro", "type constructors" }, { true, 'f', "function", "functions" }, @@ -104,7 +104,7 @@ static void add_tag(const char *token, haskellKind kind, vString *name) for (i = 0; token[i] != '\0'; ++i) vStringPut(name, token[i]); - makeSimpleTag(name, HaskellKinds, kind); + makeSimpleTag(name, kind); vStringClear(name); } @@ -336,7 +336,7 @@ extern parserDefinition* HaskellParser (void) static const char *const extensions [] = { "hs", NULL }; parserDefinition* def = parserNew ("Haskell"); - def->kinds = HaskellKinds; + def->kindTable = HaskellKinds; def->kindCount = ARRAY_SIZE(HaskellKinds); def->extensions = extensions; def->parser = findNormalHaskellTags; @@ -347,7 +347,7 @@ extern parserDefinition* LiterateHaskellParser (void) { static const char *const extensions [] = { "lhs", NULL }; parserDefinition* def = parserNew ("Literate Haskell"); - def->kinds = HaskellKinds; + def->kindTable = HaskellKinds; def->kindCount = ARRAY_SIZE(HaskellKinds); def->extensions = extensions; def->parser = findLiterateHaskellTags; diff --git a/ctags/parsers/haxe.c b/ctags/parsers/haxe.c index 762c80d1..954d5710 100644 --- a/ctags/parsers/haxe.c +++ b/ctags/parsers/haxe.c @@ -47,7 +47,7 @@ typedef enum { HXTAG_COUNT } hxKind; -static kindOption HxKinds [] = { +static kindDefinition HxKinds [] = { { true, 'm', "method", "methods" }, { true, 'c', "class", "classes" }, { true, 'e', "enum", "enumerations" }, @@ -90,7 +90,7 @@ another: vStringPut (name, (int) *cp); ++cp; } - makeSimpleTag (name, HxKinds, HXTAG_VARIABLE); + makeSimpleTag (name, HXTAG_VARIABLE); vStringClear (name); } @@ -108,7 +108,7 @@ another: vStringPut (name, (int) *cp); ++cp; } - makeSimpleTag (name, HxKinds, HXTAG_METHODS); + makeSimpleTag (name, HXTAG_METHODS); vStringClear (name); } @@ -125,7 +125,7 @@ another: vStringPut (name, (int) *cp); ++cp; } - makeSimpleTag (name, HxKinds, HXTAG_CLASS); + makeSimpleTag (name, HXTAG_CLASS); vStringCopy(clsName,name); vStringClear (name); } @@ -142,7 +142,7 @@ another: vStringPut (name, (int) *cp); ++cp; } - makeSimpleTag (name, HxKinds, HXTAG_ENUM); + makeSimpleTag (name, HXTAG_ENUM); vStringClear (name); } else if (strncmp ((const char*) cp, "public", (size_t) 6) == 0 && isspace((int) cp [6])) @@ -171,7 +171,7 @@ another: vStringPut (name, (int) *cp); ++cp; } - makeSimpleTag (name, HxKinds, HXTAG_INTERFACE); + makeSimpleTag (name, HXTAG_INTERFACE); vStringClear (name); } else if (strncmp ((const char *) cp,"typedef",(size_t) 7) == 0 && isspace(((int) cp[7]))) { cp += 7; @@ -183,7 +183,7 @@ another: vStringPut (name, (int) *cp); ++cp; } - makeSimpleTag (name, HxKinds, HXTAG_TYPEDEF); + makeSimpleTag (name, HXTAG_TYPEDEF); vStringClear (name); } @@ -207,7 +207,7 @@ extern parserDefinition* HaxeParser (void) /* * New definitions for parsing instead of regex */ - def->kinds = HxKinds; + def->kindTable = HxKinds; def->kindCount = ARRAY_SIZE (HxKinds); def->parser = findHxTags; /*def->initialize = initialize;*/ diff --git a/ctags/parsers/html.c b/ctags/parsers/html.c index 0158ee96..8d2a4abf 100644 --- a/ctags/parsers/html.c +++ b/ctags/parsers/html.c @@ -1,5 +1,5 @@ /* -* Copyright (c) 2003, Darren Hiebert +* Copyright (c) 2016, Jiri Techet * * This source code is released for free distribution under the terms of the * GNU General Public License version 2 or (at your option) any later version. @@ -8,53 +8,513 @@ * files. */ -/* -* INCLUDE FILES -*/ -#include "general.h" /* must always come first */ +#include "general.h" + +#include +#include + +#include "entry.h" #include "parse.h" +#include "read.h" #include "routines.h" +#include "keyword.h" +#include "promise.h" -static tagRegexTable htmlTagRegexTable [] = { -#define POSSIBLE_ATTRIBUTES "([ \t]+[a-z]+=\"?[^>\"]*\"?)*" - {"\"]+)\"?" POSSIBLE_ATTRIBUTES - "[ \t]*>", "\\2", - "a,anchor,named anchors", "i", NULL}, - {"^[ \t]*function[ \t]*([A-Za-z0-9_]+)[ \t]*\\(", "\\1", - "f,function,JavaScript functions", NULL, NULL}, +/* The max. number of nested elements - prevents further recursion if the limit + * is exceeded and avoids stack overflow for invalid input containing too many + * open tags */ +#define MAX_DEPTH 1000 -/* the following matches headings with tags inside like - *

Some Text

- * and - *

Some Text

*/ -#define SPACES "[ \t]*" -#define ATTRS "[^>]*" -#define INNER_HEADING \ - ATTRS ">" SPACES "(<" ATTRS ">" SPACES ")*([^<]+).*" - {"", "\\2", - "n,namespace,H1 heading", "i", NULL}, +typedef enum { + K_ANCHOR, + K_HEADING1, + K_HEADING2, + K_HEADING3 +} htmlKind; - {"", "\\2", - "c,class,H2 heading", "i", NULL}, - {"", "\\2", - "v,variable,H3 heading", "i", NULL}, +static kindDefinition HtmlKinds [] = { + { true, 'a', "anchor", "named anchors" }, + { true, 'h', "heading1", "H1 headings" }, + { true, 'i', "heading2", "H2 headings" }, + { true, 'j', "heading3", "H3 headings" } }; -/* -* FUNCTION DEFINITIONS -*/ +typedef enum { + KEYWORD_h1, + KEYWORD_h2, + KEYWORD_h3, + KEYWORD_a, + KEYWORD_script, + KEYWORD_style, + KEYWORD_name, -/* Create parser definition structure */ + /* void elements */ + KEYWORD_area, + KEYWORD_base, + KEYWORD_br, + KEYWORD_col, + KEYWORD_command, + KEYWORD_embed, + KEYWORD_hr, + KEYWORD_img, + KEYWORD_input, + KEYWORD_keygen, + KEYWORD_link, + KEYWORD_meta, + KEYWORD_param, + KEYWORD_source, + KEYWORD_track, + KEYWORD_wbr +} keywordId; + +static const keywordTable HtmlKeywordTable[] = { + {"h1", KEYWORD_h1}, + {"h2", KEYWORD_h2}, + {"h3", KEYWORD_h3}, + {"a", KEYWORD_a}, + {"script", KEYWORD_script}, + {"style", KEYWORD_style}, + {"name", KEYWORD_name}, + + /* void elements */ + {"area", KEYWORD_area}, + {"base", KEYWORD_base}, + {"br", KEYWORD_br}, + {"col", KEYWORD_col}, + {"command", KEYWORD_command}, + {"embed", KEYWORD_embed}, + {"hr", KEYWORD_hr}, + {"img", KEYWORD_img}, + {"input", KEYWORD_input}, + {"keygen", KEYWORD_keygen}, + {"link", KEYWORD_link}, + {"meta", KEYWORD_meta}, + {"param", KEYWORD_param}, + {"source", KEYWORD_source}, + {"track", KEYWORD_track}, + {"wbr", KEYWORD_wbr}, +}; + +typedef enum { + TOKEN_EOF, + TOKEN_NAME, /* tag and attribute names */ + TOKEN_STRING, /* single- or double-quoted attribute value */ + TOKEN_TEXT, + TOKEN_TAG_START, /* < */ + TOKEN_TAG_START2, /* */ + TOKEN_TAG_END2, /* /> */ + TOKEN_EQUAL, + TOKEN_COMMENT, + TOKEN_OTHER +} tokenType; + +#ifdef DEBUG +const char *tokenTypes[] = { +#define E(X) [TOKEN_##X] = #X + E(EOF), + E(NAME), + E(STRING), + E(TEXT), + E(TAG_START), + E(TAG_START2), + E(TAG_END), + E(TAG_END2), + E(EQUAL), + E(COMMENT), + E(OTHER), +#undef E +}; +#endif + +typedef struct { + tokenType type; + vString *string; +} tokenInfo; + + +static int Lang_html; + + +static void readTag (tokenInfo *token, vString *text, int depth); + +#ifdef DEBUG +static void dumpToken (tokenInfo *token, const char *context, const char* extra_context) +{ + fprintf (stderr, "[%7s] %-20s@%s.%s\n", + tokenTypes[token->type], vStringValue(token->string), + context, extra_context? extra_context: "_"); +} +#endif + +static void readTokenText (tokenInfo *const token, bool collectText) +{ + int c; + int lastC = 'X'; /* whatever non-space character */ + + vStringClear (token->string); + +getNextChar: + + c = getcFromInputFile (); + + switch (c) + { + case EOF: + token->type = TOKEN_EOF; + break; + + case '<': + ungetcToInputFile (c); + token->type = TOKEN_TEXT; + break; + + default: + if (collectText) + { + if (isspace (c)) + c = ' '; + if (c != ' ' || lastC != ' ') + { + vStringPut (token->string, c); + lastC = c; + } + } + + goto getNextChar; + } +} + +static void readToken (tokenInfo *const token, bool skipComments) +{ + int c; + + vStringClear (token->string); + +getNextChar: + + c = getcFromInputFile (); + while (isspace (c)) + c = getcFromInputFile (); + + switch (c) + { + case EOF: + token->type = TOKEN_EOF; + break; + + case '<': + { + int d = getcFromInputFile (); + + if (d == '!') + { + d = getcFromInputFile (); + if (d == '-') + { + d = getcFromInputFile (); + if (d == '-') + { + int e = ' '; + int f = ' '; + do + { + d = e; + e = f; + f = getcFromInputFile (); + } + while (f != EOF && ! (d == '-' && e == '-' && f == '>')); + + if (skipComments) + goto getNextChar; + else + { + token->type = TOKEN_COMMENT; + break; + } + } + } + ungetcToInputFile (d); + token->type = TOKEN_OTHER; + } + else if (d == '?') + token->type = TOKEN_OTHER; + else if (d == '/') + token->type = TOKEN_TAG_START2; + else + { + ungetcToInputFile (d); + token->type = TOKEN_TAG_START; + } + break; + } + case '/': + { + int d = getcFromInputFile (); + if (d == '>') + token->type = TOKEN_TAG_END2; + else + { + ungetcToInputFile (d); + token->type = TOKEN_OTHER; + } + break; + } + case '>': + token->type = TOKEN_TAG_END; + break; + + case '=': + token->type = TOKEN_EQUAL; + break; + + case '"': + case '\'': + { + const int delimiter = c; + c = getcFromInputFile (); + while (c != EOF && c != delimiter) + { + vStringPut (token->string, c); + c = getcFromInputFile (); + } + token->type = TOKEN_STRING; + break; + } + + default: + { + do + { + vStringPut (token->string, tolower (c)); + c = getcFromInputFile (); + } + while (!isspace (c) && c != '<' && c != '>' && c != '/' && + c != '=' && c != '\'' && c != '"' && c != EOF); + if (c != EOF) + ungetcToInputFile (c); + token->type = TOKEN_NAME; + break; + } + } +} + +static void appendText (vString *text, vString *appendedText) +{ + if (text != NULL && vStringLength (appendedText) > 0) + { + if (vStringLength (text) > 0 && vStringLast (text) == ' ' && + vStringLength (appendedText) > 0 && vStringChar (appendedText, 0) == ' ') + { + vStringStripTrailing (text); + } + vStringCat (text, appendedText); + } +} + +static bool readTagContent (tokenInfo *token, vString *text, long *line, long *lineOffset, int depth) +{ + tokenType type; + + readTokenText (token, text != NULL); + appendText (text, token->string); + + do + { + *line = getInputLineNumber (); + *lineOffset = getInputLineOffset (); + readToken (token, false); + type = token->type; + if (type == TOKEN_TAG_START) + readTag (token, text, depth + 1); + if (type == TOKEN_COMMENT || type == TOKEN_TAG_START) + { + readTokenText (token, text != NULL); + appendText (text, token->string); + } + } + while (type == TOKEN_COMMENT || type == TOKEN_TAG_START); + + return type == TOKEN_TAG_START2; +} + +static bool skipScriptContent (tokenInfo *token, long *line, long *lineOffset) +{ + bool found_start = false; + bool found_script = false; + + long line_tmp[2]; + long lineOffset_tmp[2]; + + tokenType type; + + do + { + line_tmp[0] = getInputLineNumber (); + lineOffset_tmp[0] = getInputLineOffset (); + + readToken (token, false); + type = token->type; + + if (type == TOKEN_TAG_START2) + { + found_start = true; + line_tmp[1] = line_tmp[0]; + lineOffset_tmp[1] = lineOffset_tmp[0]; + } + else if (found_start + && type == TOKEN_NAME + && lookupKeyword (vStringValue (token->string), Lang_html) == KEYWORD_script) + { + found_script = true; + *line = line_tmp[1]; + *lineOffset = lineOffset_tmp[1]; + } + else + found_start = false; + } + while ((type != TOKEN_EOF) && (!found_script)); + + return found_script; +} + +static void readTag (tokenInfo *token, vString *text, int depth) +{ + bool textCreated = false; + + readToken (token, true); + if (token->type == TOKEN_NAME) + { + keywordId startTag; + bool isHeading; + bool isVoid; + + startTag = lookupKeyword (vStringValue (token->string), Lang_html); + isHeading = (startTag == KEYWORD_h1 || startTag == KEYWORD_h2 || startTag == KEYWORD_h3); + isVoid = (startTag >= KEYWORD_area && startTag <= KEYWORD_wbr); + if (text == NULL && isHeading) + { + text = vStringNew (); + textCreated = true; + } + + do + { + readToken (token, true); + if (startTag == KEYWORD_a && token->type == TOKEN_NAME) + { + keywordId attribute = lookupKeyword (vStringValue (token->string), Lang_html); + + if (attribute == KEYWORD_name) + { + readToken (token, true); + if (token->type == TOKEN_EQUAL) + { + readToken (token, true); + if (token->type == TOKEN_STRING || token->type == TOKEN_NAME) + makeSimpleTag (token->string, K_ANCHOR); + } + } + } + } + while (token->type != TOKEN_TAG_END && token->type != TOKEN_TAG_END2 && + token->type != TOKEN_EOF); + + if (!isVoid && token->type == TOKEN_TAG_END && depth < MAX_DEPTH) + { + long startSourceLineNumber = getSourceLineNumber (); + long startLineNumber = getInputLineNumber (); + long startLineOffset = getInputLineOffset (); + long endLineNumber; + long endLineOffset; + bool tag_start2; + + if (startTag == KEYWORD_script) + { + bool script = skipScriptContent (token, &endLineNumber, &endLineOffset); + if (script) + makePromise ("JavaScript", startLineNumber, startLineOffset, + endLineNumber, endLineOffset, startSourceLineNumber); + readToken (token, true); + goto out; + } + + tag_start2 = readTagContent (token, text, &endLineNumber, &endLineOffset, depth); + if (tag_start2) + { + readToken (token, true); + if (isHeading && textCreated && vStringLength (text) > 0) + { + keywordId endTag = lookupKeyword (vStringValue (token->string), Lang_html); + if (startTag == endTag) + { + htmlKind headingKind; + + if (startTag == KEYWORD_h1) + headingKind = K_HEADING1; + else if (startTag == KEYWORD_h2) + headingKind = K_HEADING2; + else + headingKind = K_HEADING3; + + vStringStripLeading (text); + vStringStripTrailing (text); + makeSimpleTag (text, headingKind); + } + } + else if (startTag == KEYWORD_style) + { + keywordId endTag = lookupKeyword (vStringValue (token->string), Lang_html); + if (startTag == endTag) + makePromise ("CSS", startLineNumber, startLineOffset, + endLineNumber, endLineOffset, startSourceLineNumber); + } + + readToken (token, true); + } + } + } + + out: + if (textCreated) + vStringDelete (text); +} + +static void findHtmlTags (void) +{ + tokenInfo token; + + token.string = vStringNew (); + + do + { + readToken (&token, true); + if (token.type == TOKEN_TAG_START) + readTag (&token, NULL, 0); + } + while (token.type != TOKEN_EOF); + + vStringDelete (token.string); +} + +static void initialize (const langType language) +{ + Lang_html = language; +} + +/* parser definition */ extern parserDefinition* HtmlParser (void) { static const char *const extensions [] = { "htm", "html", NULL }; - parserDefinition *const def = parserNew ("HTML"); - def->extensions = extensions; - def->tagRegexTable = htmlTagRegexTable; - def->tagRegexCount = ARRAY_SIZE (htmlTagRegexTable); - def->method = METHOD_NOT_CRAFTED|METHOD_REGEX; + parserDefinition* def = parserNew ("HTML"); + def->kindTable = HtmlKinds; + def->kindCount = ARRAY_SIZE (HtmlKinds); + def->extensions = extensions; + def->parser = findHtmlTags; + def->initialize = initialize; + def->keywordTable = HtmlKeywordTable; + def->keywordCount = ARRAY_SIZE (HtmlKeywordTable); return def; } diff --git a/ctags/parsers/conf.c b/ctags/parsers/iniconf.c similarity index 91% rename from ctags/parsers/conf.c rename to ctags/parsers/iniconf.c index 3896e15d..0f266df7 100644 --- a/ctags/parsers/conf.c +++ b/ctags/parsers/iniconf.c @@ -29,7 +29,7 @@ typedef enum { K_KEY } confKind; -static kindOption ConfKinds [] = { +static kindDefinition ConfKinds [] = { { true, 'n', "namespace", "sections"}, { true, 'm', "macro", "keys"} }; @@ -67,7 +67,7 @@ static void findConfTags (void) vStringPut (name, (int) *cp); ++cp; } - makeSimpleTag (name, ConfKinds, K_SECTION); + makeSimpleTag (name, K_SECTION); /* remember section name */ vStringCopy (scope, name); vStringClear (name); @@ -90,11 +90,11 @@ static void findConfTags (void) if (*cp == '=') { tagEntryInfo e; - initTagEntry (&e, vStringValue (name), &(ConfKinds [K_KEY])); + initTagEntry (&e, vStringValue (name), K_KEY); if (vStringLength (scope) > 0) { - e.extensionFields.scopeKind = &(ConfKinds [K_SECTION]); + e.extensionFields.scopeKindIndex = K_SECTION; e.extensionFields.scopeName = vStringValue(scope); } makeTagEntry (&e); @@ -119,7 +119,7 @@ extern parserDefinition* ConfParser (void) static const char *const patterns [] = { "*.ini", "*.conf", NULL }; static const char *const extensions [] = { "conf", NULL }; parserDefinition* const def = parserNew ("Conf"); - def->kinds = ConfKinds; + def->kindTable = ConfKinds; def->kindCount = ARRAY_SIZE (ConfKinds); def->patterns = patterns; def->extensions = extensions; diff --git a/ctags/parsers/jscript.c b/ctags/parsers/jscript.c index 0305e5b6..458b5cb1 100644 --- a/ctags/parsers/jscript.c +++ b/ctags/parsers/jscript.c @@ -20,24 +20,91 @@ */ #include "general.h" /* must always come first */ #include /* to define isalpha () */ -#include #ifdef DEBUG #include #endif +#ifdef HAVE_ICONV +#include +#include +# ifdef WORDS_BIGENDIAN +# define INTERNAL_ENCODING "UTF-32BE" +# else +# define INTERNAL_ENCODING "UTF-32LE" +# endif /* WORDS_BIGENDIAN */ +#endif + +#include #include "debug.h" -#include "mio.h" +#include "entry.h" #include "keyword.h" #include "parse.h" #include "read.h" #include "routines.h" #include "vstring.h" +#include "objpool.h" +#include "options.h" +#include "mbcs.h" +#include "trace.h" +#include "strlist.h" /* * MACROS */ #define isType(token,t) (bool) ((token)->type == (t)) #define isKeyword(token,k) (bool) ((token)->keyword == (k)) +#define isIdentChar(c) \ + (isalpha (c) || isdigit (c) || (c) == '$' || \ + (c) == '@' || (c) == '_' || (c) == '#' || \ + (c) >= 0x80) +#define newToken() (objPoolGet (TokenPool)) +#define deleteToken(t) (objPoolPut (TokenPool, (t))) + +/* + * Debugging + * + * Uncomment this to enable extensive debugging to stderr in jscript code. + * Please note that TRACING_ENABLED should be #defined in main/trace.h + * for this to work. + * + */ +//#define JSCRIPT_DEBUGGING_ENABLED 1 + +#if defined(DO_TRACING) && defined(JSCRIPT_DEBUGGING_ENABLED) + #define JSCRIPT_DO_DEBUGGING +#endif + +#ifdef JSCRIPT_DO_DEBUGGING + +#define JSCRIPT_DEBUG_ENTER() TRACE_ENTER() +#define JSCRIPT_DEBUG_LEAVE() TRACE_LEAVE() + +#define JSCRIPT_DEBUG_ENTER_TEXT(_szFormat,...) \ + TRACE_ENTER_TEXT(_szFormat,## __VA_ARGS__) + +#define JSCRIPT_DEBUG_LEAVE_TEXT(_szFormat,...) \ + TRACE_LEAVE_TEXT(_szFormat,## __VA_ARGS__) + +#define JSCRIPT_DEBUG_PRINT(_szFormat,...) \ + TRACE_PRINT(_szFormat,## __VA_ARGS__) + +#define JSCRIPT_DEBUG_ASSERT(_condition,_szFormat,...) \ + TRACE_ASSERT(_condition,_szFormat,## __VA_ARGS__) + +#else //!JSCRIPT_DO_DEBUGGING + +#define JSCRIPT_DEBUG_ENTER() do { } while(0) +#define JSCRIPT_DEBUG_LEAVE() do { } while(0) + +#define JSCRIPT_DEBUG_ENTER_TEXT(_szFormat,...) do { } while(0) +#define JSCRIPT_DEBUG_LEAVE_TEXT(_szFormat,...) do { } while(0) + +#define JSCRIPT_DEBUG_PRINT(_szFormat,...) do { } while(0) + +#define JSCRIPT_DEBUG_ASSERT(_condition,_szFormat,...) do { } while(0) + +#endif //!JSCRIPT_DO_DEBUGGING + /* * DATA DECLARATIONS @@ -71,7 +138,13 @@ enum eKeywordId { KEYWORD_catch, KEYWORD_finally, KEYWORD_sap, - KEYWORD_return + KEYWORD_return, + KEYWORD_class, + KEYWORD_extends, + KEYWORD_static, + KEYWORD_default, + KEYWORD_export, + KEYWORD_async, }; typedef int keywordId; /* to allow KEYWORD_NONE */ @@ -85,18 +158,18 @@ typedef enum eTokenType { TOKEN_COMMA, TOKEN_KEYWORD, TOKEN_OPEN_PAREN, - TOKEN_OPERATOR, TOKEN_IDENTIFIER, TOKEN_STRING, + TOKEN_TEMPLATE_STRING, TOKEN_PERIOD, TOKEN_OPEN_CURLY, TOKEN_CLOSE_CURLY, TOKEN_EQUAL_SIGN, - TOKEN_FORWARD_SLASH, TOKEN_OPEN_SQUARE, TOKEN_CLOSE_SQUARE, TOKEN_REGEXP, TOKEN_POSTFIX_OPERATOR, + TOKEN_STAR, TOKEN_BINARY_OPERATOR } tokenType; @@ -116,9 +189,16 @@ typedef struct sTokenInfo { */ static tokenType LastTokenType; +static tokenInfo *NextToken; static langType Lang_js; +static objPool *TokenPool = NULL; + +#ifdef HAVE_ICONV +static iconv_t JSUnicodeConverter = (iconv_t) -2; +#endif + typedef enum { JSTAG_FUNCTION, JSTAG_CLASS, @@ -126,16 +206,18 @@ typedef enum { JSTAG_PROPERTY, JSTAG_CONSTANT, JSTAG_VARIABLE, + JSTAG_GENERATOR, JSTAG_COUNT } jsKind; -static kindOption JsKinds [] = { +static kindDefinition JsKinds [] = { { true, 'f', "function", "functions" }, { true, 'c', "class", "classes" }, { true, 'm', "method", "methods" }, { true, 'p', "property", "properties" }, { true, 'C', "constant", "constants" }, - { true, 'v', "variable", "global variables" } + { true, 'v', "variable", "global variables" }, + { true, 'g', "generator", "generators" } }; static const keywordTable JsKeywordTable [] = { @@ -159,7 +241,13 @@ static const keywordTable JsKeywordTable [] = { { "catch", KEYWORD_catch }, { "finally", KEYWORD_finally }, { "sap", KEYWORD_sap }, - { "return", KEYWORD_return } + { "return", KEYWORD_return }, + { "class", KEYWORD_class }, + { "extends", KEYWORD_extends }, + { "static", KEYWORD_static }, + { "default", KEYWORD_default }, + { "export", KEYWORD_export }, + { "async", KEYWORD_async }, }; /* @@ -167,46 +255,66 @@ static const keywordTable JsKeywordTable [] = { */ /* Recursive functions */ +static void readTokenFull (tokenInfo *const token, bool include_newlines, vString *const repr); static void parseFunction (tokenInfo *const token); -static bool parseBlock (tokenInfo *const token, tokenInfo *const orig_parent); -static bool parseLine (tokenInfo *const token, tokenInfo *const parent, bool is_inside_class); +static bool parseBlock (tokenInfo *const token, const vString *const parentScope); +static bool parseLine (tokenInfo *const token, bool is_inside_class); static void parseUI5 (tokenInfo *const token); -static bool isIdentChar (const int c) +static void *newPoolToken (void *createArg CTAGS_ATTR_UNUSED) { - return (bool) - (isalpha (c) || isdigit (c) || c == '$' || - c == '@' || c == '_' || c == '#'); -} + tokenInfo *token = xMalloc (1, tokenInfo); -static tokenInfo *newToken (void) -{ - tokenInfo *const token = xMalloc (1, tokenInfo); - - token->type = TOKEN_UNDEFINED; - token->keyword = KEYWORD_NONE; token->string = vStringNew (); token->scope = vStringNew (); - token->nestLevel = 0; - token->ignoreTag = false; - token->lineNumber = getInputLineNumber (); - token->filePosition = getInputFilePosition (); return token; } -static void deleteToken (tokenInfo *const token) +static void clearPoolToken (void *data) { + tokenInfo *token = data; + + token->type = TOKEN_UNDEFINED; + token->keyword = KEYWORD_NONE; + token->nestLevel = 0; + token->ignoreTag = false; + token->lineNumber = getInputLineNumber (); + token->filePosition = getInputFilePosition (); + vStringClear (token->string); + vStringClear (token->scope); +} + +static void deletePoolToken (void *data) +{ + tokenInfo *token = data; vStringDelete (token->string); vStringDelete (token->scope); eFree (token); } +static void copyToken (tokenInfo *const dest, const tokenInfo *const src, + bool const include_non_read_info) +{ + dest->lineNumber = src->lineNumber; + dest->filePosition = src->filePosition; + dest->type = src->type; + dest->keyword = src->keyword; + vStringCopy(dest->string, src->string); + if (include_non_read_info) + { + dest->nestLevel = src->nestLevel; + vStringCopy(dest->scope, src->scope); + } +} + /* * Tag generation functions */ -static void makeJsTag (tokenInfo *const token, const jsKind kind, vString *const signature) +static void makeJsTagCommon (const tokenInfo *const token, const jsKind kind, + vString *const signature, vString *const inheritance, + bool anonymous) { if (JsKinds [kind].enabled && ! token->ignoreTag ) { @@ -215,21 +323,24 @@ static void makeJsTag (tokenInfo *const token, const jsKind kind, vString *const const char *p; tagEntryInfo e; - if ((p = strrchr (name, '.')) != NULL) + if (kind != JSTAG_PROPERTY && (p = strrchr (name, '.')) != NULL ) { if (vStringLength (fullscope) > 0) vStringPut (fullscope, '.'); - vStringNCatS (fullscope, name, p - name); + vStringNCatS (fullscope, name, (size_t) (p - name)); name = p + 1; } - initTagEntry (&e, name, &(JsKinds [kind])); + initTagEntry (&e, name, kind); + + JSCRIPT_DEBUG_PRINT("Emitting tag for symbol '%s' of kind %02x with scope '%s'",name,kind,vStringValue(fullscope)); e.lineNumber = token->lineNumber; e.filePosition = token->filePosition; if ( vStringLength(fullscope) > 0 ) { + /* FIXME: proper parent type */ jsKind parent_kind = JSTAG_CLASS; /* @@ -239,7 +350,7 @@ static void makeJsTag (tokenInfo *const token, const jsKind kind, vString *const if (kind == JSTAG_FUNCTION) parent_kind = JSTAG_FUNCTION; - e.extensionFields.scopeKind = &(JsKinds [parent_kind]); + e.extensionFields.scopeKindIndex = parent_kind; e.extensionFields.scopeName = vStringValue (fullscope); } @@ -260,12 +371,25 @@ static void makeJsTag (tokenInfo *const token, const jsKind kind, vString *const e.extensionFields.signature = vStringValue(signature); } + if (inheritance) + e.extensionFields.inheritance = vStringValue(inheritance); + + if (anonymous) + markTagExtraBit (&e, XTAG_ANONYMOUS); + makeTagEntry (&e); vStringDelete (fullscope); } } -static void makeClassTag (tokenInfo *const token, vString *const signature) +static void makeJsTag (const tokenInfo *const token, const jsKind kind, + vString *const signature, vString *const inheritance) +{ + makeJsTagCommon (token, kind, signature, inheritance, false); +} + +static void makeClassTagCommon (tokenInfo *const token, vString *const signature, + vString *const inheritance, bool anonymous) { vString * fulltag; @@ -275,8 +399,8 @@ static void makeClassTag (tokenInfo *const token, vString *const signature) if (vStringLength (token->scope) > 0) { vStringCopy(fulltag, token->scope); - vStringCatS (fulltag, "."); - vStringCatS (fulltag, vStringValue(token->string)); + vStringPut (fulltag, '.'); + vStringCat (fulltag, token->string); } else { @@ -285,13 +409,21 @@ static void makeClassTag (tokenInfo *const token, vString *const signature) if ( ! stringListHas(ClassNames, vStringValue (fulltag)) ) { stringListAdd (ClassNames, vStringNewCopy (fulltag)); - makeJsTag (token, JSTAG_CLASS, signature); + makeJsTagCommon (token, JSTAG_CLASS, signature, inheritance, + anonymous); } vStringDelete (fulltag); } } -static void makeFunctionTag (tokenInfo *const token, vString *const signature) +static void makeClassTag (tokenInfo *const token, vString *const signature, + vString *const inheritance) +{ + makeClassTagCommon (token, signature, inheritance, false); +} + +static void makeFunctionTagCommon (tokenInfo *const token, vString *const signature, bool generator, + bool anonymous) { vString * fulltag; @@ -301,8 +433,8 @@ static void makeFunctionTag (tokenInfo *const token, vString *const signature) if (vStringLength (token->scope) > 0) { vStringCopy(fulltag, token->scope); - vStringCatS (fulltag, "."); - vStringCatS (fulltag, vStringValue(token->string)); + vStringPut (fulltag, '.'); + vStringCat (fulltag, token->string); } else { @@ -311,16 +443,245 @@ static void makeFunctionTag (tokenInfo *const token, vString *const signature) if ( ! stringListHas(FunctionNames, vStringValue (fulltag)) ) { stringListAdd (FunctionNames, vStringNewCopy (fulltag)); - makeJsTag (token, JSTAG_FUNCTION, signature); + makeJsTagCommon (token, generator ? JSTAG_GENERATOR : JSTAG_FUNCTION, signature, NULL, + anonymous); } vStringDelete (fulltag); } } +static void makeFunctionTag (tokenInfo *const token, vString *const signature, bool generator) +{ + makeFunctionTagCommon (token, signature, generator, false); +} + /* * Parsing functions */ +/* given @p point, returns the first byte of the encoded output sequence, and + * make sure the next ones will be returned by calls to getcFromInputFile() + * as if the code point was simply written in the input file. */ +static int handleUnicodeCodePoint (uint32_t point) +{ + int c = (int) point; + + Assert (point < 0x110000); + +#ifdef HAVE_ICONV + /* if we do have iconv and the encodings are specified, use this */ + if (isConverting () && JSUnicodeConverter == (iconv_t) -2) + { + /* if we didn't try creating the converter yet, try and do so */ + JSUnicodeConverter = iconv_open (getLanguageEncoding (Lang_js), INTERNAL_ENCODING); + } + if (isConverting () && JSUnicodeConverter != (iconv_t) -1) + { + char *input_ptr = (char *) &point; + size_t input_left = sizeof point; + /* 4 bytes should be enough for any encoding (it's how much UTF-32 + * would need). */ + /* FIXME: actually iconv has a tendency to output a BOM for Unicode + * encodings where it matters when the endianess is not specified in + * the target encoding name. E.g., if the target encoding is "UTF-32" + * or "UTF-16" it will output 2 code points, the BOM (U+FEFF) and the + * one we expect. This does not happen if the endianess is specified + * explicitly, e.g. with "UTF-32LE", or "UTF-16BE". + * However, it's not very relevant for the moment as nothing in CTags + * cope well (if at all) with non-ASCII-compatible encodings like + * UTF-32 or UTF-16 anyway. */ + char output[4] = { 0 }; + char *output_ptr = output; + size_t output_left = ARRAY_SIZE (output); + + if (iconv (JSUnicodeConverter, &input_ptr, &input_left, &output_ptr, &output_left) == (size_t) -1) + { + /* something went wrong, which probably means the output encoding + * cannot represent the character. Use a placeholder likely to be + * supported instead, that's also valid in an identifier */ + verbose ("JavaScript: Encoding: %s\n", strerror (errno)); + c = '_'; + } + else + { + const size_t output_len = ARRAY_SIZE (output) - output_left; + + /* put all but the first byte back so that getcFromInputFile() will + * return them in the right order */ + for (unsigned int i = 1; i < output_len; i++) + ungetcToInputFile ((unsigned char) output[output_len - i]); + c = (unsigned char) output[0]; + } + + iconv (JSUnicodeConverter, NULL, NULL, NULL, NULL); + } + else +#endif + { + /* when no encoding is specified (or no iconv), assume UTF-8 is good. + * Why UTF-8? Because it's an ASCII-compatible common Unicode encoding. */ + if (point < 0x80) + c = (unsigned char) point; + else if (point < 0x800) + { + c = (unsigned char) (0xc0 | ((point >> 6) & 0x1f)); + ungetcToInputFile ((unsigned char) (0x80 | (point & 0x3f))); + } + else if (point < 0x10000) + { + c = (unsigned char) (0xe0 | ((point >> 12) & 0x0f)); + ungetcToInputFile ((unsigned char) (0x80 | ((point >> 0) & 0x3f))); + ungetcToInputFile ((unsigned char) (0x80 | ((point >> 6) & 0x3f))); + } + else if (point < 0x110000) + { + c = (unsigned char) (0xf0 | ((point >> 18) & 0x07)); + ungetcToInputFile ((unsigned char) (0x80 | ((point >> 0) & 0x3f))); + ungetcToInputFile ((unsigned char) (0x80 | ((point >> 6) & 0x3f))); + ungetcToInputFile ((unsigned char) (0x80 | ((point >> 12) & 0x3f))); + } + } + + return c; +} + +/* reads a Unicode escape sequence after the "\" prefix. + * @param value Location to store the escape sequence value. + * @param isUTF16 Location to store whether @param value is an UTF-16 word. + * @returns Whether a valid sequence was read. */ +static bool readUnicodeEscapeSequenceValue (uint32_t *const value, + bool *const isUTF16) +{ + bool valid = false; + int d = getcFromInputFile (); + + if (d != 'u') + ungetcToInputFile (d); + else + { + int e = getcFromInputFile (); + char cp[6 + 1]; /* up to 6 hex + possible closing '}' or invalid char */ + unsigned int cp_len = 0; + + *isUTF16 = (e != '{'); + if (e == '{') + { /* Handles Unicode code point escapes: \u{ HexDigits } + * We skip the leading 0s because there can be any number of them + * and they don't change any meaning. */ + bool has_leading_zero = false; + + while ((cp[cp_len] = (char) getcFromInputFile ()) == '0') + has_leading_zero = true; + + while (isxdigit (cp[cp_len]) && ++cp_len < ARRAY_SIZE (cp)) + cp[cp_len] = (char) getcFromInputFile (); + valid = ((cp_len > 0 || has_leading_zero) && + cp_len < ARRAY_SIZE (cp) && cp[cp_len] == '}' && + /* also check if it's a valid Unicode code point */ + (cp_len < 6 || + (cp_len == 6 && strncmp (cp, "110000", 6) < 0))); + if (! valid) /* put back the last (likely invalid) character */ + ungetcToInputFile (cp[cp_len]); + } + else + { /* Handles Unicode escape sequences: \u Hex4Digits */ + do + cp[cp_len] = (char) ((cp_len == 0) ? e : getcFromInputFile ()); + while (isxdigit (cp[cp_len]) && ++cp_len < 4); + valid = (cp_len == 4); + } + + if (! valid) + { + /* we don't get every character back, but it would require to + * be able to put up to 9 characters back (in the worst case + * for handling invalid \u{10FFFFx}), and here we're recovering + * from invalid syntax anyway. */ + ungetcToInputFile (e); + ungetcToInputFile (d); + } + else + { + *value = 0; + for (unsigned int i = 0; i < cp_len; i++) + { + *value *= 16; + + /* we know it's a hex digit, no need to double check */ + if (cp[i] < 'A') + *value += (unsigned int) cp[i] - '0'; + else if (cp[i] < 'a') + *value += 10 + (unsigned int) cp[i] - 'A'; + else + *value += 10 + (unsigned int) cp[i] - 'a'; + } + } + } + + return valid; +} + +static int valueToXDigit (unsigned char v) +{ + Assert (v <= 0xF); + + if (v >= 0xA) + return 'A' + (v - 0xA); + else + return '0' + v; +} + +/* Reads and expands a Unicode escape sequence after the "\" prefix. If the + * escape sequence is a UTF16 high surrogate, also try and read the low + * surrogate to emit the proper code point. + * @param fallback The character to return if the sequence is invalid. Usually + * this would be the '\' character starting the sequence. + * @returns The first byte of the sequence, or @param fallback if the sequence + * is invalid. On success, next calls to getcFromInputFile() will + * return subsequent bytes (if any). */ +static int readUnicodeEscapeSequence (const int fallback) +{ + int c; + uint32_t value; + bool isUTF16; + + if (! readUnicodeEscapeSequenceValue (&value, &isUTF16)) + c = fallback; + else + { + if (isUTF16 && (value & 0xfc00) == 0xd800) + { /* this is a high surrogate, try and read its low surrogate and + * emit the resulting code point */ + uint32_t low; + int d = getcFromInputFile (); + + if (d != '\\' || ! readUnicodeEscapeSequenceValue (&low, &isUTF16)) + ungetcToInputFile (d); + else if (! isUTF16) + { /* not UTF-16 low surrogate but a plain code point */ + d = handleUnicodeCodePoint (low); + ungetcToInputFile (d); + } + else if ((low & 0xfc00) != 0xdc00) + { /* not a low surrogate, so put back the escaped representation + * in case it was another high surrogate we should read as part + * of another pair. */ + ungetcToInputFile (valueToXDigit ((unsigned char) ((low & 0x000f) >> 0))); + ungetcToInputFile (valueToXDigit ((unsigned char) ((low & 0x00f0) >> 4))); + ungetcToInputFile (valueToXDigit ((unsigned char) ((low & 0x0f00) >> 8))); + ungetcToInputFile (valueToXDigit ((unsigned char) ((low & 0xf000) >> 12))); + ungetcToInputFile ('u'); + ungetcToInputFile ('\\'); + } + else + value = 0x010000 + ((value & 0x03ff) << 10) + (low & 0x03ff); + } + c = handleUnicodeCodePoint (value); + } + + return c; +} + static void parseString (vString *const string, const int delimiter) { bool end = false; @@ -338,7 +699,13 @@ static void parseString (vString *const string, const int delimiter) * sequence. * See ECMA-262 7.8.4 */ c = getcFromInputFile (); - if (c != '\r' && c != '\n') + if (c == 'u') + { + ungetcToInputFile (c); + c = readUnicodeEscapeSequence ('\\'); + vStringPut (string, c); + } + else if (c != '\r' && c != '\n') vStringPut(string, c); else if (c == '\r') { @@ -379,6 +746,12 @@ static void parseRegExp (void) ungetcToInputFile (c); break; } + else if (c == '\n' || c == '\r') + { + /* invalid in a regex */ + ungetcToInputFile (c); + break; + } else if (c == '\\') c = getcFromInputFile (); /* skip next character */ else if (c == '[') @@ -399,24 +772,76 @@ static void parseIdentifier (vString *const string, const int firstChar) { vStringPut (string, c); c = getcFromInputFile (); + if (c == '\\') + c = readUnicodeEscapeSequence (c); } while (isIdentChar (c)); + /* if readUnicodeEscapeSequence() read an escape sequence this is incorrect, + * as we should actually put back the whole escape sequence and not the + * decoded character. However, it's not really worth the hassle as it can + * only happen if the input has an invalid escape sequence. */ ungetcToInputFile (c); /* unget non-identifier character */ } -static keywordId analyzeToken (vString *const name) +static void parseTemplateString (vString *const string) { - vString *keyword = vStringNew (); - keywordId result; - vStringCopyToLower (keyword, name); - result = (keywordId) lookupKeyword (vStringValue (keyword), Lang_js); - vStringDelete (keyword); - return result; + int c; + do + { + c = getcFromInputFile (); + if (c == '`' || c == EOF) + break; + + vStringPut (string, c); + + if (c == '\\') + { + c = getcFromInputFile(); + if (c != EOF) + vStringPut(string, c); + } + else if (c == '$') + { + c = getcFromInputFile (); + if (c != '{') + ungetcToInputFile (c); + else + { + int depth = 1; + /* we need to use the real token machinery to handle strings, + * comments, regexes and whatnot */ + tokenInfo *token = newToken (); + LastTokenType = TOKEN_UNDEFINED; + vStringPut(string, c); + do + { + readTokenFull (token, false, string); + if (isType (token, TOKEN_OPEN_CURLY)) + depth++; + else if (isType (token, TOKEN_CLOSE_CURLY)) + depth--; + } + while (! isType (token, TOKEN_EOF) && depth > 0); + deleteToken (token); + } + } + } + while (c != EOF); } static void readTokenFull (tokenInfo *const token, bool include_newlines, vString *const repr) { int c; int i; + bool newline_encountered = false; + + /* if we've got a token held back, emit it */ + if (NextToken) + { + copyToken (token, NextToken, false); + deleteToken (NextToken); + NextToken = NULL; + return; + } token->type = TOKEN_UNDEFINED; token->keyword = KEYWORD_NONE; @@ -427,15 +852,16 @@ getNextChar: do { c = getcFromInputFile (); + if (include_newlines && (c == '\r' || c == '\n')) + newline_encountered = true; i++; } - while (c == '\t' || c == ' ' || - ((c == '\r' || c == '\n') && ! include_newlines)); + while (c == '\t' || c == ' ' || c == '\r' || c == '\n'); token->lineNumber = getInputLineNumber (); token->filePosition = getInputFilePosition (); - if (repr) + if (repr && c != EOF) { if (i > 1) vStringPut (repr, ' '); @@ -472,6 +898,8 @@ getNextChar: } case '*': + token->type = TOKEN_STAR; + break; case '%': case '?': case '>': @@ -482,40 +910,6 @@ getNextChar: token->type = TOKEN_BINARY_OPERATOR; break; - case '\r': - case '\n': - /* This isn't strictly correct per the standard, but following the - * real rules means understanding all statements, and that's not - * what the parser currently does. What we do here is a guess, by - * avoiding inserting semicolons that would make the statement on - * the left invalid. Hopefully this should not have false negatives - * (e.g. should not miss insertion of a semicolon) but might have - * false positives (e.g. it will wrongfully emit a semicolon for the - * newline in "foo\n+bar"). - * This should however be mostly harmless as we only deal with - * newlines in specific situations where we know a false positive - * wouldn't hurt too bad. */ - switch (LastTokenType) - { - /* these cannot be the end of a statement, so hold the newline */ - case TOKEN_EQUAL_SIGN: - case TOKEN_COLON: - case TOKEN_PERIOD: - case TOKEN_FORWARD_SLASH: - case TOKEN_BINARY_OPERATOR: - /* and these already end one, no need to duplicate it */ - case TOKEN_SEMICOLON: - case TOKEN_COMMA: - case TOKEN_CLOSE_CURLY: - case TOKEN_OPEN_CURLY: - include_newlines = false; /* no need to recheck */ - goto getNextChar; - break; - default: - token->type = TOKEN_SEMICOLON; - } - break; - case '\'': case '"': token->type = TOKEN_STRING; @@ -529,13 +923,16 @@ getNextChar: } break; - case '\\': - c = getcFromInputFile (); - if (c != '\\' && c != '"' && !isspace (c)) - ungetcToInputFile (c); - token->type = TOKEN_CHARACTER; + case '`': + token->type = TOKEN_TEMPLATE_STRING; + parseTemplateString (token->string); token->lineNumber = getInputLineNumber (); token->filePosition = getInputFilePosition (); + if (repr) + { + vStringCat (repr, token->string); + vStringPut (repr, c); + } break; case '/': @@ -550,10 +947,11 @@ getNextChar: case TOKEN_CHARACTER: case TOKEN_IDENTIFIER: case TOKEN_STRING: + case TOKEN_TEMPLATE_STRING: case TOKEN_CLOSE_CURLY: case TOKEN_CLOSE_PAREN: case TOKEN_CLOSE_SQUARE: - token->type = TOKEN_FORWARD_SLASH; + token->type = TOKEN_BINARY_OPERATOR; break; default: @@ -609,6 +1007,9 @@ getNextChar: } break; + case '\\': + c = readUnicodeEscapeSequence (c); + /* fallthrough */ default: if (! isIdentChar (c)) token->type = TOKEN_UNDEFINED; @@ -617,7 +1018,7 @@ getNextChar: parseIdentifier (token->string, c); token->lineNumber = getInputLineNumber (); token->filePosition = getInputFilePosition (); - token->keyword = analyzeToken (token->string); + token->keyword = lookupKeyword (vStringValue (token->string), Lang_js); if (isKeyword (token, KEYWORD_NONE)) token->type = TOKEN_IDENTIFIER; else @@ -628,25 +1029,75 @@ getNextChar: break; } + if (include_newlines && newline_encountered) + { + /* This isn't strictly correct per the standard, but following the + * real rules means understanding all statements, and that's not + * what the parser currently does. What we do here is a guess, by + * avoiding inserting semicolons that would make the statement on + * the left or right obviously invalid. Hopefully this should not + * have false negatives (e.g. should not miss insertion of a semicolon) + * but might have false positives (e.g. it will wrongfully emit a + * semicolon sometimes, i.e. for the newline in "foo\n(bar)"). + * This should however be mostly harmless as we only deal with + * newlines in specific situations where we know a false positive + * wouldn't hurt too bad. */ + + /* these already end a statement, so no need to duplicate it */ + #define IS_STMT_SEPARATOR(t) ((t) == TOKEN_SEMICOLON || \ + (t) == TOKEN_EOF || \ + (t) == TOKEN_COMMA || \ + (t) == TOKEN_OPEN_CURLY) + /* these cannot be the start or end of a statement */ + #define IS_BINARY_OPERATOR(t) ((t) == TOKEN_EQUAL_SIGN || \ + (t) == TOKEN_COLON || \ + (t) == TOKEN_PERIOD || \ + (t) == TOKEN_STAR || \ + (t) == TOKEN_BINARY_OPERATOR) + + if (! IS_STMT_SEPARATOR(LastTokenType) && + ! IS_STMT_SEPARATOR(token->type) && + ! IS_BINARY_OPERATOR(LastTokenType) && + ! IS_BINARY_OPERATOR(token->type) && + /* these cannot be followed by a semicolon */ + ! (LastTokenType == TOKEN_OPEN_PAREN || + LastTokenType == TOKEN_OPEN_SQUARE)) + { + /* hold the token... */ + Assert (NextToken == NULL); + NextToken = newToken (); + copyToken (NextToken, token, false); + + /* ...and emit a semicolon instead */ + token->type = TOKEN_SEMICOLON; + token->keyword = KEYWORD_NONE; + vStringClear (token->string); + if (repr) + vStringPut (token->string, '\n'); + } + + #undef IS_STMT_SEPARATOR + #undef IS_BINARY_OPERATOR + } + LastTokenType = token->type; } +#ifdef JSCRIPT_DO_DEBUGGING +/* trace readTokenFull() */ +static void readTokenFullDebug (tokenInfo *const token, bool include_newlines, vString *const repr) +{ + readTokenFull (token, include_newlines, repr); + JSCRIPT_DEBUG_PRINT("token '%s' of type %02x with scope '%s'",vStringValue(token->string),token->type, vStringValue(token->scope)); +} +# define readTokenFull readTokenFullDebug +#endif + static void readToken (tokenInfo *const token) { readTokenFull (token, false, NULL); } -static void copyToken (tokenInfo *const dest, tokenInfo *const src) -{ - dest->nestLevel = src->nestLevel; - dest->lineNumber = src->lineNumber; - dest->filePosition = src->filePosition; - dest->type = src->type; - dest->keyword = src->keyword; - vStringCopy(dest->string, src->string); - vStringCopy(dest->scope, src->scope); -} - /* * Token parsing functions */ @@ -667,6 +1118,8 @@ static void skipArgumentList (tokenInfo *const token, bool include_newlines, vSt nest_level++; else if (isType (token, TOKEN_CLOSE_PAREN)) nest_level--; + else if (isKeyword (token, KEYWORD_function)) + parseFunction (token); } readTokenFull (token, include_newlines, NULL); } @@ -701,25 +1154,26 @@ static void addContext (tokenInfo* const parent, const tokenInfo* const child) { if (vStringLength (parent->string) > 0) { - vStringCatS (parent->string, "."); + vStringPut (parent->string, '.'); } - vStringCatS (parent->string, vStringValue(child->string)); + vStringCat (parent->string, child->string); } -static void addToScope (tokenInfo* const token, vString* const extra) +static void addToScope (tokenInfo* const token, const vString* const extra) { if (vStringLength (token->scope) > 0) { - vStringCatS (token->scope, "."); + vStringPut (token->scope, '.'); } - vStringCatS (token->scope, vStringValue(extra)); + vStringCat (token->scope, extra); } /* * Scanning functions */ -static bool findCmdTerm (tokenInfo *const token, bool include_newlines) +static bool findCmdTerm (tokenInfo *const token, bool include_newlines, + bool include_commas) { /* * Read until we find either a semicolon or closing brace. @@ -727,12 +1181,13 @@ static bool findCmdTerm (tokenInfo *const token, bool include_newlines) */ while (! isType (token, TOKEN_SEMICOLON) && ! isType (token, TOKEN_CLOSE_CURLY) && + ! (include_commas && isType (token, TOKEN_COMMA)) && ! isType (token, TOKEN_EOF)) { /* Handle nested blocks */ if ( isType (token, TOKEN_OPEN_CURLY)) { - parseBlock (token, token); + parseBlock (token, NULL); readTokenFull (token, include_newlines, NULL); } else if ( isType (token, TOKEN_OPEN_PAREN) ) @@ -770,20 +1225,16 @@ static void parseSwitch (tokenInfo *const token) if (isType (token, TOKEN_OPEN_PAREN)) { - /* - * Handle nameless functions, these will only - * be considered methods. - */ skipArgumentList(token, false, NULL); } if (isType (token, TOKEN_OPEN_CURLY)) { - parseBlock (token, token); + parseBlock (token, NULL); } } -static bool parseLoop (tokenInfo *const token, tokenInfo *const parent) +static bool parseLoop (tokenInfo *const token) { /* * Handles these statements @@ -814,26 +1265,16 @@ static bool parseLoop (tokenInfo *const token, tokenInfo *const parent) if (isType (token, TOKEN_OPEN_PAREN)) { - /* - * Handle nameless functions, these will only - * be considered methods. - */ skipArgumentList(token, false, NULL); } if (isType (token, TOKEN_OPEN_CURLY)) { - /* - * This will be either a function or a class. - * We can only determine this by checking the body - * of the function. If we find a "this." we know - * it is a class, otherwise it is a function. - */ - parseBlock (token, parent); + parseBlock (token, NULL); } else { - is_terminated = parseLine(token, parent, false); + is_terminated = parseLine(token, false); } } else if (isKeyword (token, KEYWORD_do)) @@ -842,17 +1283,11 @@ static bool parseLoop (tokenInfo *const token, tokenInfo *const parent) if (isType (token, TOKEN_OPEN_CURLY)) { - /* - * This will be either a function or a class. - * We can only determine this by checking the body - * of the function. If we find a "this." we know - * it is a class, otherwise it is a function. - */ - parseBlock (token, parent); + parseBlock (token, NULL); } else { - is_terminated = parseLine(token, parent, false); + is_terminated = parseLine(token, false); } if (is_terminated) @@ -864,21 +1299,23 @@ static bool parseLoop (tokenInfo *const token, tokenInfo *const parent) if (isType (token, TOKEN_OPEN_PAREN)) { - /* - * Handle nameless functions, these will only - * be considered methods. - */ skipArgumentList(token, true, NULL); } if (! isType (token, TOKEN_SEMICOLON)) - is_terminated = false; + { + /* oddly enough, `do {} while (0) var foo = 42` is perfectly + * valid JS, so explicitly handle the remaining of the line + * for the sake of the root scope handling (as parseJsFile() + * always advances a token not to ever get stuck) */ + is_terminated = parseLine(token, false); + } } } return is_terminated; } -static bool parseIf (tokenInfo *const token, tokenInfo *const parent) +static bool parseIf (tokenInfo *const token) { bool read_next_token = true; /* @@ -933,28 +1370,18 @@ static bool parseIf (tokenInfo *const token, tokenInfo *const parent) if (isType (token, TOKEN_OPEN_PAREN)) { - /* - * Handle nameless functions, these will only - * be considered methods. - */ skipArgumentList(token, false, NULL); } if (isType (token, TOKEN_OPEN_CURLY)) { - /* - * This will be either a function or a class. - * We can only determine this by checking the body - * of the function. If we find a "this." we know - * it is a class, otherwise it is a function. - */ - parseBlock (token, parent); + parseBlock (token, NULL); } else { /* The next token should only be read if this statement had its own * terminator */ - read_next_token = findCmdTerm (token, true); + read_next_token = findCmdTerm (token, true, false); } return read_next_token; } @@ -964,21 +1391,37 @@ static void parseFunction (tokenInfo *const token) tokenInfo *const name = newToken (); vString *const signature = vStringNew (); bool is_class = false; - + bool is_generator = false; + bool is_anonymous = false; /* * This deals with these formats * function validFunctionTwo(a,b) {} + * function * generator(a,b) {} */ + copyToken (name, token, true); readToken (name); - /* Add scope in case this is an INNER function */ - addToScope(name, token->scope); + if (isType (name, TOKEN_STAR)) + { + is_generator = true; + readToken (name); + } + if (isType (name, TOKEN_OPEN_PAREN)) + { + /* anonymous function */ + copyToken (token, name, false); + anonGenerate (name->string, "AnonymousFunction", JSTAG_FUNCTION); + is_anonymous = true; + } + else if (!isType (name, TOKEN_IDENTIFIER)) + goto cleanUp; + else + readToken (token); - readToken (token); while (isType (token, TOKEN_PERIOD)) { readToken (token); - if ( isKeyword(token, KEYWORD_NONE) ) + if (! isType(token, TOKEN_KEYWORD)) { addContext (name, token); readToken (token); @@ -990,36 +1433,42 @@ static void parseFunction (tokenInfo *const token) if ( isType (token, TOKEN_OPEN_CURLY) ) { - is_class = parseBlock (token, name); + is_class = parseBlock (token, name->string); if ( is_class ) - makeClassTag (name, signature); + makeClassTagCommon (name, signature, NULL, is_anonymous); else - makeFunctionTag (name, signature); + makeFunctionTagCommon (name, signature, is_generator, is_anonymous); } - findCmdTerm (token, false); + findCmdTerm (token, false, false); + cleanUp: vStringDelete (signature); deleteToken (name); } -static bool parseBlock (tokenInfo *const token, tokenInfo *const orig_parent) +/* Parses a block surrounded by curly braces. + * @p parentScope is the scope name for this block, or NULL for unnamed scopes */ +static bool parseBlock (tokenInfo *const token, const vString *const parentScope) { + JSCRIPT_DEBUG_ENTER(); + bool is_class = false; bool read_next_token = true; vString * saveScope = vStringNew (); - tokenInfo *const parent = newToken (); - /* backup the parent token to allow calls like parseBlock(token, token) */ - copyToken (parent, orig_parent); + vStringCopy(saveScope, token->scope); + if (parentScope) + { + addToScope (token, parentScope); + token->nestLevel++; + } - token->nestLevel++; /* * Make this routine a bit more forgiving. * If called on an open_curly advance it */ - if ( isType (token, TOKEN_OPEN_CURLY) && - isKeyword(token, KEYWORD_NONE) ) + if (isType (token, TOKEN_OPEN_CURLY)) readToken(token); if (! isType (token, TOKEN_CLOSE_CURLY)) @@ -1038,16 +1487,12 @@ static bool parseBlock (tokenInfo *const token, tokenInfo *const orig_parent) * a class, not a function */ is_class = true; - vStringCopy(saveScope, token->scope); - addToScope (token, parent->string); /* * Ignore the remainder of the line * findCmdTerm(token); */ - read_next_token = parseLine (token, parent, is_class); - - vStringCopy(token->scope, saveScope); + read_next_token = parseLine (token, is_class); } else if (isKeyword (token, KEYWORD_var) || isKeyword (token, KEYWORD_let) || @@ -1057,22 +1502,12 @@ static bool parseBlock (tokenInfo *const token, tokenInfo *const orig_parent) * Potentially we have found an inner function. * Set something to indicate the scope */ - vStringCopy(saveScope, token->scope); - addToScope (token, parent->string); - read_next_token = parseLine (token, parent, is_class); - vStringCopy(token->scope, saveScope); - } - else if (isKeyword (token, KEYWORD_function)) - { - vStringCopy(saveScope, token->scope); - addToScope (token, parent->string); - parseFunction (token); - vStringCopy(token->scope, saveScope); + read_next_token = parseLine (token, is_class); } else if (isType (token, TOKEN_OPEN_CURLY)) { /* Handle nested blocks */ - parseBlock (token, parent); + parseBlock (token, NULL); } else { @@ -1082,7 +1517,7 @@ static bool parseBlock (tokenInfo *const token, tokenInfo *const orig_parent) * parseLine will detect this case and indicate * whether we should read an additional token. */ - read_next_token = parseLine (token, parent, is_class); + read_next_token = parseLine (token, is_class); } /* @@ -1100,17 +1535,27 @@ static bool parseBlock (tokenInfo *const token, tokenInfo *const orig_parent) ! isType (token, TOKEN_CLOSE_CURLY) && read_next_token); } - deleteToken (parent); + vStringCopy(token->scope, saveScope); vStringDelete(saveScope); - token->nestLevel--; + if (parentScope) + token->nestLevel--; + + JSCRIPT_DEBUG_LEAVE(); return is_class; } -static bool parseMethods (tokenInfo *const token, tokenInfo *const class) +static bool parseMethods (tokenInfo *const token, const tokenInfo *const class, + const bool is_es6_class) { + JSCRIPT_DEBUG_ENTER(); + tokenInfo *const name = newToken (); bool has_methods = false; + vString *saveScope = vStringNew (); + + vStringCopy (saveScope, token->scope); + addToScope (token, class->string); /* * This deals with these formats @@ -1118,6 +1563,13 @@ static bool parseMethods (tokenInfo *const token, tokenInfo *const class) * validMethod : function(a,b) {} * 'validMethod2' : function(a,b) {} * container.dirtyTab = {'url': false, 'title':false, 'snapshot':false, '*': false} + * + * ES6 methods: + * property(...) {} + * *generator() {} + * FIXME: what to do with computed names? + * [property]() {} + * *[generator]() {} */ do @@ -1125,27 +1577,51 @@ static bool parseMethods (tokenInfo *const token, tokenInfo *const class) readToken (token); if (isType (token, TOKEN_CLOSE_CURLY)) { - /* - * This was most likely a variable declaration of a hash table. - * indicate there were no methods and return. - */ - has_methods = false; goto cleanUp; } - if (isType (token, TOKEN_STRING) || isKeyword(token, KEYWORD_NONE)) + if (isKeyword (token, KEYWORD_async)) + readToken (token); + + if (! isType (token, TOKEN_KEYWORD) && + ! isType (token, TOKEN_SEMICOLON)) { - copyToken(name, token); + bool is_generator = false; + bool is_shorthand = false; /* ES6 shorthand syntax */ + + if (isType (token, TOKEN_STAR)) /* shorthand generator */ + { + is_generator = true; + readToken (token); + } + + copyToken(name, token, true); readToken (token); - if ( isType (token, TOKEN_COLON) ) + is_shorthand = isType (token, TOKEN_OPEN_PAREN); + if ( isType (token, TOKEN_COLON) || is_shorthand ) { - readToken (token); - if ( isKeyword (token, KEYWORD_function) ) + if (! is_shorthand) { + readToken (token); + if (isKeyword (token, KEYWORD_async)) + readToken (token); + } + if ( is_shorthand || isKeyword (token, KEYWORD_function) ) + { + JSCRIPT_DEBUG_PRINT("Seems to be a function or shorthand"); vString *const signature = vStringNew (); - readToken (token); + if (! is_shorthand) + { + readToken (token); + if (isType (token, TOKEN_STAR)) + { + /* generator: 'function' '*' '(' ... ')' '{' ... '}' */ + is_generator = true; + readToken (token); + } + } if ( isType (token, TOKEN_OPEN_PAREN) ) { skipArgumentList(token, false, signature); @@ -1154,22 +1630,23 @@ static bool parseMethods (tokenInfo *const token, tokenInfo *const class) if (isType (token, TOKEN_OPEN_CURLY)) { has_methods = true; - addToScope (name, class->string); - makeJsTag (name, JSTAG_METHOD, signature); - parseBlock (token, name); + makeJsTag (name, is_generator ? JSTAG_GENERATOR : JSTAG_METHOD, signature, NULL); + parseBlock (token, name->string); /* - * Read to the closing curly, check next - * token, if a comma, we must loop again + * If we aren't parsing an ES6 class (for which there + * is no mandatory separators), read to the closing + * curly, check next token, if a comma, we must loop + * again. */ - readToken (token); + if (! is_es6_class) + readToken (token); } vStringDelete (signature); } - else + else if (! is_es6_class) { - vString * saveScope = vStringNew (); bool has_child_methods = false; /* skip whatever is the value */ @@ -1180,10 +1657,7 @@ static bool parseMethods (tokenInfo *const token, tokenInfo *const class) if (isType (token, TOKEN_OPEN_CURLY)) { /* Recurse to find child properties/methods */ - vStringCopy (saveScope, token->scope); - addToScope (token, class->string); - has_child_methods = parseMethods (token, name); - vStringCopy (token->scope, saveScope); + has_child_methods = parseMethods (token, name, false); readToken (token); } else if (isType (token, TOKEN_OPEN_PAREN)) @@ -1199,29 +1673,110 @@ static bool parseMethods (tokenInfo *const token, tokenInfo *const class) readToken (token); } } - vStringDelete (saveScope); has_methods = true; - addToScope (name, class->string); if (has_child_methods) - makeJsTag (name, JSTAG_CLASS, NULL); + makeJsTag (name, JSTAG_CLASS, NULL, NULL); else - makeJsTag (name, JSTAG_PROPERTY, NULL); + makeJsTag (name, JSTAG_PROPERTY, NULL, NULL); } } } - } while ( isType(token, TOKEN_COMMA) ); + } while ( isType(token, TOKEN_COMMA) || + ( is_es6_class && ! isType(token, TOKEN_EOF) ) ); - findCmdTerm (token, false); + JSCRIPT_DEBUG_PRINT("Finished parsing methods"); + + findCmdTerm (token, false, false); cleanUp: + vStringCopy (token->scope, saveScope); + vStringDelete (saveScope); deleteToken (name); + JSCRIPT_DEBUG_LEAVE(); + return has_methods; } -static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, bool is_inside_class) +static bool parseES6Class (tokenInfo *const token, const tokenInfo *targetName) { + JSCRIPT_DEBUG_ENTER(); + + tokenInfo * className = newToken (); + vString *inheritance = NULL; + bool is_anonymous = true; + + copyToken (className, token, true); + readToken (className); + + /* optional name */ + if (isType (className, TOKEN_IDENTIFIER)) + { + readToken (token); + is_anonymous = false; + } + else + { + copyToken (token, className, true); + /* We create a fake name so we have a scope for the members */ + if (! targetName) + anonGenerate (className->string, "AnonymousClass", JSTAG_CLASS); + } + + if (! targetName) + targetName = className; + + if (isKeyword (token, KEYWORD_extends)) + inheritance = vStringNew (); + + /* skip inheritance info */ + while (! isType (token, TOKEN_OPEN_CURLY) && + ! isType (token, TOKEN_EOF) && + ! isType (token, TOKEN_SEMICOLON)) + readTokenFull (token, false, inheritance); + + /* remove the last added token (here we assume it's one char, "{" or ";" */ + if (inheritance && vStringLength (inheritance) > 0 && + ! isType (token, TOKEN_EOF)) + { + vStringChop (inheritance); + vStringStripTrailing (inheritance); + vStringStripLeading (inheritance); + } + + JSCRIPT_DEBUG_PRINT("Emitting tag for class '%s'", vStringValue(targetName->string)); + + makeJsTagCommon (targetName, JSTAG_CLASS, NULL, inheritance, + (is_anonymous && (targetName == className))); + + if (! is_anonymous && targetName != className) + { + /* FIXME: what to do with the secondary name? It's local to the + * class itself, so not very useful... let's hope people + * don't give it another name than the target in case of + * var MyClass = class MyClassSecondaryName { ... } + * I guess it could be an alias to MyClass, or duplicate it + * altogether, not sure. */ + makeJsTag (className, JSTAG_CLASS, NULL, inheritance); + } + + if (inheritance) + vStringDelete (inheritance); + + if (isType (token, TOKEN_OPEN_CURLY)) + parseMethods (token, targetName, true); + + deleteToken (className); + + JSCRIPT_DEBUG_LEAVE(); + return true; +} + +static bool parseStatement (tokenInfo *const token, bool is_inside_class) +{ + JSCRIPT_DEBUG_ENTER(); + tokenInfo *const name = newToken (); tokenInfo *const secondary_name = newToken (); tokenInfo *const method_body_token = newToken (); @@ -1234,7 +1789,7 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo bool has_methods = false; vString * fulltag; - vStringClear(saveScope); + vStringCopy (saveScope, token->scope); /* * Functions can be named or unnamed. * This deals with these formats: @@ -1272,6 +1827,7 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo isKeyword(token, KEYWORD_let) || isKeyword(token, KEYWORD_const) ) { + JSCRIPT_DEBUG_PRINT("var/let/const case"); is_const = isKeyword(token, KEYWORD_const); /* * Only create variables for global scope @@ -1283,8 +1839,11 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo readToken(token); } +nextVar: if ( isKeyword(token, KEYWORD_this) ) { + JSCRIPT_DEBUG_PRINT("found 'this' keyword"); + readToken(token); if (isType (token, TOKEN_PERIOD)) { @@ -1292,28 +1851,34 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo } } - copyToken(name, token); + copyToken(name, token, true); + JSCRIPT_DEBUG_PRINT("name becomes '%s'",vStringValue(name->string)); while (! isType (token, TOKEN_CLOSE_CURLY) && ! isType (token, TOKEN_SEMICOLON) && ! isType (token, TOKEN_EQUAL_SIGN) && + ! isType (token, TOKEN_COMMA) && ! isType (token, TOKEN_EOF)) { if (isType (token, TOKEN_OPEN_CURLY)) - parseBlock (token, parent); + parseBlock (token, NULL); /* Potentially the name of the function */ - readToken (token); if (isType (token, TOKEN_PERIOD)) { /* * Cannot be a global variable is it has dot references in the name */ is_global = false; + /* Assume it's an assignment to a global name (e.g. a class) using + * its fully qualified name, so strip the scope. + * FIXME: resolve the scope so we can make more than an assumption. */ + vStringClear (token->scope); + vStringClear (name->scope); do { readToken (token); - if ( isKeyword(token, KEYWORD_NONE) ) + if (! isType(token, TOKEN_KEYWORD)) { if ( is_class ) { @@ -1348,7 +1913,18 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo * } * */ - makeClassTag (name, NULL); + if (! ( isType (name, TOKEN_IDENTIFIER) + || isType (name, TOKEN_STRING) ) ) + /* + * Unexpected input. Try to reset the parsing. + * + * TOKEN_STRING is acceptable. e.g.: + * ----------------------------------- + * "a".prototype = function( mode ) {} + */ + goto cleanUp; + + makeClassTag (name, NULL, NULL); is_class = true; /* @@ -1361,15 +1937,14 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo * Handle CASE 1 */ readToken (token); - if ( isKeyword(token, KEYWORD_NONE) ) + if (! isType(token, TOKEN_KEYWORD)) { vString *const signature = vStringNew (); - vStringCopy(saveScope, token->scope); addToScope(token, name->string); + copyToken (method_body_token, token, true); readToken (method_body_token); - vStringCopy (method_body_token->scope, token->scope); while (! isType (method_body_token, TOKEN_SEMICOLON) && ! isType (method_body_token, TOKEN_CLOSE_CURLY) && @@ -1383,12 +1958,12 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo readToken (method_body_token); } - makeJsTag (token, JSTAG_METHOD, signature); + makeJsTag (token, JSTAG_METHOD, signature, NULL); vStringDelete (signature); if ( isType (method_body_token, TOKEN_OPEN_CURLY)) { - parseBlock (method_body_token, token); + parseBlock (method_body_token, token->string); is_terminated = true; } else @@ -1410,11 +1985,11 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo * 'validMethodTwo' : function(a,b) {} * } */ - parseMethods(token, name); + parseMethods(token, name, false); /* * Find to the end of the statement */ - findCmdTerm (token, false); + findCmdTerm (token, false, false); token->ignoreTag = false; is_terminated = true; goto cleanUp; @@ -1425,6 +2000,8 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo readToken (token); } while (isType (token, TOKEN_PERIOD)); } + else + readTokenFull (token, true, NULL); if ( isType (token, TOKEN_OPEN_PAREN) ) skipArgumentList(token, false, NULL); @@ -1435,7 +2012,7 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo /* if ( isType (token, TOKEN_OPEN_CURLY) ) { - is_class = parseBlock (token, name); + is_class = parseBlock (token, name->string); } */ } @@ -1451,7 +2028,9 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo goto cleanUp; } - if ( isType (token, TOKEN_SEMICOLON) ) + if ( isType (token, TOKEN_SEMICOLON) || + isType (token, TOKEN_EOF) || + isType (token, TOKEN_COMMA) ) { /* * Only create variables for global scope @@ -1462,14 +2041,18 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo * Handles this syntax: * var g_var2; */ - if (isType (token, TOKEN_SEMICOLON)) - makeJsTag (name, is_const ? JSTAG_CONSTANT : JSTAG_VARIABLE, NULL); + makeJsTag (name, is_const ? JSTAG_CONSTANT : JSTAG_VARIABLE, NULL, NULL); } /* * Statement has ended. * This deals with calls to functions, like: * alert(..); */ + if (isType (token, TOKEN_COMMA)) + { + readToken (token); + goto nextVar; + } goto cleanUp; } @@ -1486,14 +2069,23 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo readToken (token); } + if (isKeyword (token, KEYWORD_async)) + readToken (token); + if ( isKeyword (token, KEYWORD_function) ) { vString *const signature = vStringNew (); + bool is_generator = false; readToken (token); + if (isType (token, TOKEN_STAR)) + { + is_generator = true; + readToken (token); + } - if ( isKeyword (token, KEYWORD_NONE) && - ! isType (token, TOKEN_OPEN_PAREN) ) + if (! isType (token, TOKEN_KEYWORD) && + ! isType (token, TOKEN_OPEN_PAREN)) { /* * Functions of this format: @@ -1512,7 +2104,7 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo * we have established this is a valid function we will * create the secondary reference to it. */ - copyToken(secondary_name, token); + copyToken(secondary_name, token, true); readToken (token); } @@ -1529,26 +2121,40 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo */ if ( is_inside_class ) { - makeJsTag (name, JSTAG_METHOD, signature); + makeJsTag (name, is_generator ? JSTAG_GENERATOR : JSTAG_METHOD, signature, NULL); if ( vStringLength(secondary_name->string) > 0 ) - makeFunctionTag (secondary_name, signature); - parseBlock (token, name); + makeFunctionTag (secondary_name, signature, is_generator); + parseBlock (token, name->string); } else { - is_class = parseBlock (token, name); + if (! ( isType (name, TOKEN_IDENTIFIER) + || isType (name, TOKEN_STRING) + || isType (name, TOKEN_KEYWORD) ) ) + { + /* Unexpected input. Try to reset the parsing. */ + JSCRIPT_DEBUG_PRINT("Unexpected input, trying to reset"); + vStringDelete (signature); + goto cleanUp; + } + + is_class = parseBlock (token, name->string); if ( is_class ) - makeClassTag (name, signature); + makeClassTag (name, signature, NULL); else - makeFunctionTag (name, signature); + makeFunctionTag (name, signature, is_generator); if ( vStringLength(secondary_name->string) > 0 ) - makeFunctionTag (secondary_name, signature); + makeFunctionTag (secondary_name, signature, is_generator); } } vStringDelete (signature); } + else if (isKeyword (token, KEYWORD_class)) + { + is_terminated = parseES6Class (token, name); + } else if (isType (token, TOKEN_OPEN_CURLY)) { /* @@ -1560,9 +2166,9 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo * Or checks if this is a hash variable. * var z = {}; */ - has_methods = parseMethods(token, name); + has_methods = parseMethods(token, name, false); if (has_methods) - makeJsTag (name, JSTAG_CLASS, NULL); + makeJsTag (name, JSTAG_CLASS, NULL, NULL); else { /* @@ -1587,8 +2193,8 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo if (vStringLength (token->scope) > 0) { vStringCopy(fulltag, token->scope); - vStringCatS (fulltag, "."); - vStringCatS (fulltag, vStringValue(token->string)); + vStringPut (fulltag, '.'); + vStringCat (fulltag, token->string); } else { @@ -1597,18 +2203,18 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo if ( ! stringListHas(FunctionNames, vStringValue (fulltag)) && ! stringListHas(ClassNames, vStringValue (fulltag)) ) { - makeJsTag (name, is_const ? JSTAG_CONSTANT : JSTAG_VARIABLE, NULL); + makeJsTag (name, is_const ? JSTAG_CONSTANT : JSTAG_VARIABLE, NULL, NULL); } vStringDelete (fulltag); } } + /* Here we should be at the end of the block, on the close curly. + * If so, read the next token not to confuse that close curly with + * the end of the current statement. */ if (isType (token, TOKEN_CLOSE_CURLY)) { - /* - * Assume the closing parentheses terminates - * this statements. - */ - is_terminated = true; + readTokenFull(token, true, NULL); + is_terminated = isType (token, TOKEN_SEMICOLON); } } else if (isKeyword (token, KEYWORD_new)) @@ -1633,18 +2239,18 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo { if ( is_var ) { - makeJsTag (name, is_const ? JSTAG_CONSTANT : JSTAG_VARIABLE, NULL); + makeJsTag (name, is_const ? JSTAG_CONSTANT : JSTAG_VARIABLE, NULL, NULL); } else { if ( is_class ) { - makeClassTag (name, NULL); + makeClassTag (name, NULL, NULL); } else { /* FIXME: we cannot really get a meaningful * signature from a `new Function()` call, * so for now just don't set any */ - makeFunctionTag (name, NULL); + makeFunctionTag (name, NULL, false); } } } @@ -1653,7 +2259,7 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo is_terminated = false; } } - else if (isKeyword (token, KEYWORD_NONE)) + else if (! isType (token, TOKEN_KEYWORD)) { /* * Only create variables for global scope @@ -1677,8 +2283,8 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo if (vStringLength (token->scope) > 0) { vStringCopy(fulltag, token->scope); - vStringCatS (fulltag, "."); - vStringCatS (fulltag, vStringValue(token->string)); + vStringPut (fulltag, '.'); + vStringCat (fulltag, token->string); } else { @@ -1687,7 +2293,7 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo if ( ! stringListHas(FunctionNames, vStringValue (fulltag)) && ! stringListHas(ClassNames, vStringValue (fulltag)) ) { - makeJsTag (name, is_const ? JSTAG_CONSTANT : JSTAG_VARIABLE, NULL); + makeJsTag (name, is_const ? JSTAG_CONSTANT : JSTAG_VARIABLE, NULL, NULL); } vStringDelete (fulltag); } @@ -1707,7 +2313,6 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo is_terminated = false; } } - /* if we aren't already at the cmd end, advance to it and check whether * the statement was terminated */ if (! isType (token, TOKEN_CLOSE_CURLY) && @@ -1725,7 +2330,13 @@ static bool parseStatement (tokenInfo *const token, tokenInfo *const parent, boo * return 1; * } */ - is_terminated = findCmdTerm (token, true); + is_terminated = findCmdTerm (token, true, true); + /* if we're at a comma, try and read a second var */ + if (isType (token, TOKEN_COMMA)) + { + readToken (token); + goto nextVar; + } } cleanUp: @@ -1735,6 +2346,8 @@ cleanUp: deleteToken (method_body_token); vStringDelete(saveScope); + JSCRIPT_DEBUG_LEAVE(); + return is_terminated; } @@ -1752,7 +2365,7 @@ static void parseUI5 (tokenInfo *const token) * } * } * - * Handle the parsing of the initial controller (and the + * Handle the parsing of the initial controller (and the * same for "view") and then allow the methods to be * parsed as usual. */ @@ -1771,7 +2384,7 @@ static void parseUI5 (tokenInfo *const token) if (isType (token, TOKEN_STRING)) { - copyToken(name, token); + copyToken(name, token, true); readToken (token); } @@ -1780,7 +2393,7 @@ static void parseUI5 (tokenInfo *const token) do { - parseMethods (token, name); + parseMethods (token, name, false); } while (! isType (token, TOKEN_CLOSE_CURLY) && ! isType (token, TOKEN_EOF)); } @@ -1788,8 +2401,10 @@ static void parseUI5 (tokenInfo *const token) deleteToken (name); } -static bool parseLine (tokenInfo *const token, tokenInfo *const parent, bool is_inside_class) +static bool parseLine (tokenInfo *const token, bool is_inside_class) { + JSCRIPT_DEBUG_ENTER_TEXT("token is '%s' of type %02x",vStringValue(token->string),token->type); + bool is_terminated = true; /* * Detect the common statements, if, while, for, do, ... @@ -1808,7 +2423,7 @@ static bool parseLine (tokenInfo *const token, tokenInfo *const parent, bool is_ case KEYWORD_for: case KEYWORD_while: case KEYWORD_do: - is_terminated = parseLoop (token, parent); + is_terminated = parseLoop (token); break; case KEYWORD_if: case KEYWORD_else: @@ -1816,16 +2431,24 @@ static bool parseLine (tokenInfo *const token, tokenInfo *const parent, bool is_ case KEYWORD_catch: case KEYWORD_finally: /* Common semantics */ - is_terminated = parseIf (token, parent); + is_terminated = parseIf (token); break; case KEYWORD_switch: parseSwitch (token); break; case KEYWORD_return: - is_terminated = findCmdTerm (token, true); + case KEYWORD_async: + readToken (token); + is_terminated = parseLine (token, is_inside_class); + break; + case KEYWORD_function: + parseFunction (token); + break; + case KEYWORD_class: + is_terminated = parseES6Class (token, NULL); break; default: - is_terminated = parseStatement (token, parent, is_inside_class); + is_terminated = parseStatement (token, is_inside_class); break; } } @@ -1836,36 +2459,55 @@ static bool parseLine (tokenInfo *const token, tokenInfo *const parent, bool is_ * SEMICOLON terminated. parseBlock needs to know this * so that it does not read the next token. */ - is_terminated = parseStatement (token, parent, is_inside_class); + is_terminated = parseStatement (token, is_inside_class); } + + JSCRIPT_DEBUG_LEAVE(); + return is_terminated; } static void parseJsFile (tokenInfo *const token) { + JSCRIPT_DEBUG_ENTER(); + do { readToken (token); - if (isType (token, TOKEN_KEYWORD) && token->keyword == KEYWORD_function) - parseFunction (token); - else if (isType (token, TOKEN_KEYWORD) && token->keyword == KEYWORD_sap) + if (isType (token, TOKEN_KEYWORD) && token->keyword == KEYWORD_sap) parseUI5 (token); + else if (isType (token, TOKEN_KEYWORD) && (token->keyword == KEYWORD_export || + token->keyword == KEYWORD_default)) + /* skip those at top-level */; else - parseLine (token, token, false); + parseLine (token, false); } while (! isType (token, TOKEN_EOF)); + + JSCRIPT_DEBUG_LEAVE(); } static void initialize (const langType language) { Assert (ARRAY_SIZE (JsKinds) == JSTAG_COUNT); Lang_js = language; + + TokenPool = objPoolNew (16, newPoolToken, deletePoolToken, clearPoolToken, NULL); +} + +static void finalize (langType language CTAGS_ATTR_UNUSED, bool initialized) +{ + if (!initialized) + return; + + objPoolDelete (TokenPool); } static void findJsTags (void) { tokenInfo *const token = newToken (); + NextToken = NULL; ClassNames = stringListNew (); FunctionNames = stringListNew (); LastTokenType = TOKEN_UNDEFINED; @@ -1877,21 +2519,38 @@ static void findJsTags (void) ClassNames = NULL; FunctionNames = NULL; deleteToken (token); + +#ifdef HAVE_ICONV + if (JSUnicodeConverter != (iconv_t) -2 && /* not created */ + JSUnicodeConverter != (iconv_t) -1 /* creation failed */) + { + iconv_close (JSUnicodeConverter); + JSUnicodeConverter = (iconv_t) -2; + } +#endif + + Assert (NextToken == NULL); } /* Create parser definition structure */ extern parserDefinition* JavaScriptParser (void) { - static const char *const extensions [] = { "js", NULL }; + // .jsx files are JSX: https://facebook.github.io/jsx/ + // which have JS function definitions, so we just use the JS parser + static const char *const extensions [] = { "js", "jsx", NULL }; + static const char *const aliases [] = { "js", "node", "nodejs", + "seed", "gjs", NULL }; parserDefinition *const def = parserNew ("JavaScript"); def->extensions = extensions; + def->aliases = aliases; /* * New definitions for parsing instead of regex */ - def->kinds = JsKinds; + def->kindTable = JsKinds; def->kindCount = ARRAY_SIZE (JsKinds); def->parser = findJsTags; def->initialize = initialize; + def->finalize = finalize; def->keywordTable = JsKeywordTable; def->keywordCount = ARRAY_SIZE (JsKeywordTable); diff --git a/ctags/parsers/json.c b/ctags/parsers/json.c index 8965eefb..4020f2e5 100644 --- a/ctags/parsers/json.c +++ b/ctags/parsers/json.c @@ -69,7 +69,7 @@ typedef enum { static langType Lang_json; -static kindOption JsonKinds [] = { +static kindDefinition JsonKinds [] = { { true, 'o', "object", "objects" }, { true, 'a', "array", "arrays" }, { true, 'n', "number", "numbers" }, @@ -116,7 +116,7 @@ static void makeJsonTag (tokenInfo *const token, const jsonKind kind) if (! JsonKinds[kind].enabled) return; - initTagEntry (&e, vStringValue (token->string), &(JsonKinds[kind])); + initTagEntry (&e, vStringValue (token->string), kind); e.lineNumber = token->lineNumber; e.filePosition = token->filePosition; @@ -125,7 +125,7 @@ static void makeJsonTag (tokenInfo *const token, const jsonKind kind) { Assert (token->scopeKind > TAG_NONE && token->scopeKind < TAG_COUNT); - e.extensionFields.scopeKind = &(JsonKinds[token->scopeKind]); + e.extensionFields.scopeKindIndex = token->scopeKind; e.extensionFields.scopeName = vStringValue (token->scope); } @@ -379,7 +379,7 @@ extern parserDefinition* JsonParser (void) static const char *const extensions [] = { "json", NULL }; parserDefinition *const def = parserNew ("JSON"); def->extensions = extensions; - def->kinds = JsonKinds; + def->kindTable = JsonKinds; def->kindCount = ARRAY_SIZE (JsonKinds); def->parser = findJsonTags; def->initialize = initialize; diff --git a/ctags/parsers/lua.c b/ctags/parsers/lua.c index 4b41e0a4..608b71df 100644 --- a/ctags/parsers/lua.c +++ b/ctags/parsers/lua.c @@ -26,7 +26,7 @@ typedef enum { K_FUNCTION } luaKind; -static kindOption LuaKinds [] = { +static kindDefinition LuaKinds [] = { { true, 'f', "function", "functions" } }; @@ -72,7 +72,7 @@ static void extract_name (const char *begin, const char *end, vString *name) for (cp = begin ; cp != end; cp++) vStringPut (name, (int) *cp); - makeSimpleTag (name, LuaKinds, K_FUNCTION); + makeSimpleTag (name, K_FUNCTION); vStringClear (name); } } @@ -112,7 +112,7 @@ extern parserDefinition* LuaParser (void) { static const char* const extensions [] = { "lua", NULL }; parserDefinition* def = parserNew ("Lua"); - def->kinds = LuaKinds; + def->kindTable = LuaKinds; def->kindCount = ARRAY_SIZE (LuaKinds); def->extensions = extensions; def->parser = findLuaTags; diff --git a/ctags/parsers/make.c b/ctags/parsers/make.c index 9898b995..1c72bd4a 100644 --- a/ctags/parsers/make.c +++ b/ctags/parsers/make.c @@ -30,7 +30,7 @@ typedef enum { K_MACRO, K_TARGET } shKind; -static kindOption MakeKinds [] = { +static kindDefinition MakeKinds [] = { { true, 'm', "macro", "macros"}, { true, 't', "target", "targets"} }; @@ -97,12 +97,12 @@ static void newTarget (vString *const name) { return; } - makeSimpleTag (name, MakeKinds, K_TARGET); + makeSimpleTag (name, K_TARGET); } static void newMacro (vString *const name) { - makeSimpleTag (name, MakeKinds, K_MACRO); + makeSimpleTag (name, K_MACRO); } static void readIdentifier (const int first, vString *const id) @@ -225,7 +225,7 @@ extern parserDefinition* MakefileParser (void) static const char *const patterns [] = { "[Mm]akefile", "GNUmakefile", NULL }; static const char *const extensions [] = { "mak", "mk", NULL }; parserDefinition* const def = parserNew ("Make"); - def->kinds = MakeKinds; + def->kindTable = MakeKinds; def->kindCount = ARRAY_SIZE (MakeKinds); def->patterns = patterns; def->extensions = extensions; diff --git a/ctags/parsers/markdown.c b/ctags/parsers/markdown.c index 5bc2f0b0..9cc78eff 100644 --- a/ctags/parsers/markdown.c +++ b/ctags/parsers/markdown.c @@ -26,7 +26,7 @@ * DATA DEFINITIONS */ -static kindOption MarkdownKinds[] = { +static kindDefinition MarkdownKinds[] = { { true, 'v', "variable", "sections" } }; @@ -50,7 +50,7 @@ static bool issame(const char *str) static void makeMarkdownTag (const vString* const name, bool name_before) { tagEntryInfo e; - initTagEntry (&e, vStringValue(name), &(MarkdownKinds [0])); + initTagEntry (&e, vStringValue(name), 0); if (name_before) e.lineNumber--; /* we want the line before the underline chars */ @@ -93,7 +93,7 @@ extern parserDefinition* MarkdownParser (void) static const char *const extensions [] = { "md", NULL }; parserDefinition* const def = parserNew ("Markdown"); - def->kinds = MarkdownKinds; + def->kindTable = MarkdownKinds; def->kindCount = ARRAY_SIZE (MarkdownKinds); def->patterns = patterns; def->extensions = extensions; diff --git a/ctags/parsers/matlab.c b/ctags/parsers/matlab.c index a51fb147..f45ddd0a 100644 --- a/ctags/parsers/matlab.c +++ b/ctags/parsers/matlab.c @@ -30,7 +30,7 @@ typedef enum { K_STRUCT } MatlabKind; -static kindOption MatlabKinds [] = { +static kindDefinition MatlabKinds [] = { { true, 'f', "function", "Functions" }, { true, 's', "struct", "Structures" }, }; @@ -107,7 +107,7 @@ static void findMatlabTags (void) } } - makeSimpleTag (name, MatlabKinds, K_FUNCTION); + makeSimpleTag (name, K_FUNCTION); vStringClear (name); } @@ -128,18 +128,18 @@ static void findMatlabTags (void) ++cp; } - makeSimpleTag (name, MatlabKinds, K_STRUCT); + makeSimpleTag (name, K_STRUCT); vStringClear (name); } } vStringDelete (name); } -extern parserDefinition* MatlabParser (void) +extern parserDefinition* MatLabParser (void) { static const char *const extensions [] = { "m", NULL }; parserDefinition* def = parserNew ("Matlab"); - def->kinds = MatlabKinds; + def->kindTable = MatlabKinds; def->kindCount = ARRAY_SIZE (MatlabKinds); def->extensions = extensions; def->parser = findMatlabTags; diff --git a/ctags/parsers/nsis.c b/ctags/parsers/nsis.c index ba5c51c0..39441e84 100644 --- a/ctags/parsers/nsis.c +++ b/ctags/parsers/nsis.c @@ -30,7 +30,7 @@ typedef enum { K_VARIABLE } NsisKind; -static kindOption NsisKinds [] = { +static kindDefinition NsisKinds [] = { { true, 'n', "namespace", "sections"}, { true, 'f', "function", "functions"}, { true, 'v', "variable", "variables"} @@ -68,7 +68,7 @@ static void findNsisTags (void) vStringPut (name, (int) *cp); ++cp; } - makeSimpleTag (name, NsisKinds, K_FUNCTION); + makeSimpleTag (name, K_FUNCTION); vStringClear (name); } /* variables */ @@ -93,7 +93,7 @@ static void findNsisTags (void) vStringPut (name, (int) *cp); ++cp; } - makeSimpleTag (name, NsisKinds, K_VARIABLE); + makeSimpleTag (name, K_VARIABLE); vStringClear (name); } /* sections */ @@ -122,7 +122,7 @@ static void findNsisTags (void) vStringPut (name, (int) *cp); ++cp; } - makeSimpleTag (name, NsisKinds, K_SECTION); + makeSimpleTag (name, K_SECTION); vStringClear (name); } } @@ -135,7 +135,7 @@ extern parserDefinition* NsisParser (void) "nsi", "nsh", NULL }; parserDefinition* def = parserNew ("NSIS"); - def->kinds = NsisKinds; + def->kindTable = NsisKinds; def->kindCount = ARRAY_SIZE (NsisKinds); def->extensions = extensions; def->parser = findNsisTags; diff --git a/ctags/parsers/objc.c b/ctags/parsers/objc.c index 6ceac0f8..80cffe04 100644 --- a/ctags/parsers/objc.c +++ b/ctags/parsers/objc.c @@ -38,7 +38,7 @@ typedef enum { K_MACRO } objcKind; -static kindOption ObjcKinds[] = { +static kindDefinition ObjcKinds[] = { {true, 'i', "interface", "class interface"}, {true, 'I', "implementation", "class implementation"}, {true, 'P', "protocol", "Protocol"}, @@ -423,11 +423,11 @@ static objcKind parentType = K_INTERFACE; * add additional information to the tag. */ static void prepareTag (tagEntryInfo * tag, vString const *name, objcKind kind) { - initTagEntry (tag, vStringValue (name), &(ObjcKinds[kind])); + initTagEntry (tag, vStringValue (name), kind); if (parentName != NULL) { - tag->extensionFields.scopeKind = &(ObjcKinds[parentType]); + tag->extensionFields.scopeKindIndex = parentType; tag->extensionFields.scopeName = vStringValue (parentName); } } @@ -1110,7 +1110,7 @@ extern parserDefinition *ObjcParser (void) { static const char *const extensions[] = { "m", "h", NULL }; parserDefinition *def = parserNewFull ("ObjectiveC", KIND_FILE_ALT); - def->kinds = ObjcKinds; + def->kindTable = ObjcKinds; def->kindCount = ARRAY_SIZE (ObjcKinds); def->extensions = extensions; def->parser = findObjcTags; diff --git a/ctags/parsers/pascal.c b/ctags/parsers/pascal.c index c2967f99..3221dfb9 100644 --- a/ctags/parsers/pascal.c +++ b/ctags/parsers/pascal.c @@ -28,7 +28,7 @@ typedef enum { K_FUNCTION, K_PROCEDURE } pascalKind; -static kindOption PascalKinds [] = { +static kindDefinition PascalKinds [] = { { true, 'f', "function", "functions"}, { true, 'p', "procedure", "procedures"} }; @@ -43,14 +43,16 @@ static void createPascalTag (tagEntryInfo* const tag, { if (PascalKinds [kind].enabled && name != NULL && vStringLength (name) > 0) { - initTagEntry (tag, vStringValue (name), &(PascalKinds [kind])); + initTagEntry (tag, vStringValue (name), kind); tag->extensionFields.signature = arglist; tag->extensionFields.varType = vartype; } else + { /* TODO: Passing NULL as name makes an assertion behind initTagEntry failure */ - initTagEntry (tag, NULL, NULL); + /* initTagEntry (tag, NULL, NULL); */ + } } static void makePascalTag (const tagEntryInfo* const tag) @@ -349,7 +351,7 @@ extern parserDefinition* PascalParser (void) static const char *const extensions [] = { "p", "pas", NULL }; parserDefinition* def = parserNew ("Pascal"); def->extensions = extensions; - def->kinds = PascalKinds; + def->kindTable = PascalKinds; def->kindCount = ARRAY_SIZE (PascalKinds); def->parser = findPascalTags; return def; diff --git a/ctags/parsers/perl.c b/ctags/parsers/perl.c index 8eeeffd7..7218f380 100644 --- a/ctags/parsers/perl.c +++ b/ctags/parsers/perl.c @@ -39,7 +39,7 @@ typedef enum { K_SUBROUTINE_DECLARATION } perlKind; -static kindOption PerlKinds [] = { +static kindDefinition PerlKinds [] = { { true, 'c', "constant", "constants" }, { true, 'f', "format", "formats" }, { true, 'l', "label", "labels" }, @@ -319,12 +319,12 @@ static void findPerlTags (void) * isSubroutineDeclaration() may consume several lines. So * we record line positions. */ - initTagEntry(&e, vStringValue(name), &(PerlKinds[kind])); + initTagEntry(&e, vStringValue(name), kind); if (true == isSubroutineDeclaration(cp)) { if (true == PerlKinds[K_SUBROUTINE_DECLARATION].enabled) { kind = K_SUBROUTINE_DECLARATION; - e.kind = &(PerlKinds[kind]); + e.kindIndex = kind; } else { vStringClear (name); continue; @@ -345,7 +345,7 @@ static void findPerlTags (void) } } else if (vStringLength (name) > 0) { - makeSimpleTag (name, PerlKinds, kind); + makeSimpleTag (name, kind); if (isXtagEnabled(XTAG_QUALIFIED_TAGS) && qualified && K_PACKAGE != kind && package != NULL && vStringLength (package) > 0) @@ -353,7 +353,7 @@ static void findPerlTags (void) vString *const qualifiedName = vStringNew (); vStringCopy (qualifiedName, package); vStringCat (qualifiedName, name); - makeSimpleTag (qualifiedName, PerlKinds, kind); + makeSimpleTag (qualifiedName, kind); vStringDelete (qualifiedName); } } @@ -371,7 +371,7 @@ extern parserDefinition* PerlParser (void) { static const char *const extensions [] = { "pl", "pm", "plx", "perl", NULL }; parserDefinition* def = parserNew ("Perl"); - def->kinds = PerlKinds; + def->kindTable = PerlKinds; def->kindCount = ARRAY_SIZE (PerlKinds); def->extensions = extensions; def->parser = findPerlTags; diff --git a/ctags/parsers/php.c b/ctags/parsers/php.c index 5bb4ed90..5278a59e 100644 --- a/ctags/parsers/php.c +++ b/ctags/parsers/php.c @@ -113,7 +113,7 @@ typedef enum { COUNT_KIND } phpKind; -static kindOption PhpKinds[COUNT_KIND] = { +static kindDefinition PhpKinds[COUNT_KIND] = { { true, 'c', "class", "classes" }, { true, 'd', "define", "constant definitions" }, { true, 'f', "function", "functions" }, @@ -280,7 +280,7 @@ static void initPhpEntry (tagEntryInfo *const e, const tokenInfo *const token, parentKind = K_NAMESPACE; } - initTagEntry (e, vStringValue (token->string), &(PhpKinds[kind])); + initTagEntry (e, vStringValue (token->string), kind); e->lineNumber = token->lineNumber; e->filePosition = token->filePosition; @@ -298,7 +298,7 @@ static void initPhpEntry (tagEntryInfo *const e, const tokenInfo *const token, { Assert (parentKind >= 0); - e->extensionFields.scopeKind = &(PhpKinds[parentKind]); + e->extensionFields.scopeKindIndex = parentKind; e->extensionFields.scopeName = vStringValue (fullScope); } } @@ -321,7 +321,7 @@ static void makeNamespacePhpTag (const tokenInfo *const token, const vString *co { tagEntryInfo e; - initTagEntry (&e, vStringValue (name), &(PhpKinds[K_NAMESPACE])); + initTagEntry (&e, vStringValue (name), K_NAMESPACE); e.lineNumber = token->lineNumber; e.filePosition = token->filePosition; @@ -1453,7 +1453,7 @@ extern parserDefinition* PhpParser (void) { static const char *const extensions [] = { "php", "php3", "php4", "php5", "phtml", NULL }; parserDefinition* def = parserNew ("PHP"); - def->kinds = PhpKinds; + def->kindTable = PhpKinds; def->kindCount = ARRAY_SIZE (PhpKinds); def->extensions = extensions; def->parser = findPhpTags; @@ -1467,7 +1467,7 @@ extern parserDefinition* ZephirParser (void) { static const char *const extensions [] = { "zep", NULL }; parserDefinition* def = parserNew ("Zephir"); - def->kinds = PhpKinds; + def->kindTable = PhpKinds; def->kindCount = ARRAY_SIZE (PhpKinds); def->extensions = extensions; def->parser = findZephirTags; diff --git a/ctags/parsers/powershell.c b/ctags/parsers/powershell.c index 4a935e0c..9a7d0309 100644 --- a/ctags/parsers/powershell.c +++ b/ctags/parsers/powershell.c @@ -42,7 +42,7 @@ typedef enum { COUNT_KIND } powerShellKind; -static kindOption PowerShellKinds[COUNT_KIND] = { +static kindDefinition PowerShellKinds[COUNT_KIND] = { { true, 'f', "function", "functions" }, { true, 'v', "variable", "variables" } }; @@ -98,7 +98,7 @@ static const char *findValidAccessType (const char *const access) static void initPowerShellEntry (tagEntryInfo *const e, const tokenInfo *const token, const powerShellKind kind, const char *const access) { - initTagEntry (e, vStringValue (token->string), &(PowerShellKinds[kind])); + initTagEntry (e, vStringValue (token->string), kind); e->lineNumber = token->lineNumber; e->filePosition = token->filePosition; @@ -110,7 +110,7 @@ static void initPowerShellEntry (tagEntryInfo *const e, const tokenInfo *const t int parentKind = token->parentKind; Assert (parentKind >= 0); - e->extensionFields.scopeKind = &(PowerShellKinds[parentKind]); + e->extensionFields.scopeKindIndex = parentKind; e->extensionFields.scopeName = vStringValue (token->scope); } } @@ -600,7 +600,7 @@ extern parserDefinition* PowerShellParser (void) { static const char *const extensions [] = { "ps1", "psm1", NULL }; parserDefinition* def = parserNew ("PowerShell"); - def->kinds = PowerShellKinds; + def->kindTable = PowerShellKinds; def->kindCount = ARRAY_SIZE (PowerShellKinds); def->extensions = extensions; def->parser = findPowerShellTags; diff --git a/ctags/parsers/python.c b/ctags/parsers/python.c index a5f27df5..b8490692 100644 --- a/ctags/parsers/python.c +++ b/ctags/parsers/python.c @@ -42,7 +42,7 @@ typedef enum { K_CLASS, K_FUNCTION, K_METHOD, K_VARIABLE, K_IMPORT } pythonKind; -static kindOption PythonKinds[] = { +static kindDefinition PythonKinds[] = { {true, 'c', "class", "classes"}, {true, 'f', "function", "functions"}, {true, 'm', "member", "class members"}, @@ -131,18 +131,18 @@ static struct corkInfo makeFunctionTag (vString *const function, { if (is_class_parent) { - initTagEntry (&tag, vStringValue (function), &(PythonKinds[K_METHOD])); - tag.extensionFields.scopeKind = &(PythonKinds[K_CLASS]); + initTagEntry (&tag, vStringValue (function), K_METHOD); + tag.extensionFields.scopeKindIndex = K_CLASS; } else { - initTagEntry (&tag, vStringValue (function), &(PythonKinds[K_FUNCTION])); - tag.extensionFields.scopeKind = &(PythonKinds[K_FUNCTION]); + initTagEntry (&tag, vStringValue (function), K_FUNCTION); + tag.extensionFields.scopeKindIndex = K_FUNCTION; } tag.extensionFields.scopeName = vStringValue (parent); } else - initTagEntry (&tag, vStringValue (function), &(PythonKinds[K_FUNCTION])); + initTagEntry (&tag, vStringValue (function), K_FUNCTION); tag.extensionFields.signature = arglist; @@ -162,17 +162,17 @@ static int makeClassTag (vString *const class, vString *const inheritance, vString *const parent, int is_class_parent) { tagEntryInfo tag; - initTagEntry (&tag, vStringValue (class), &(PythonKinds[K_CLASS])); + initTagEntry (&tag, vStringValue (class), K_CLASS); if (vStringLength (parent) > 0) { if (is_class_parent) { - tag.extensionFields.scopeKind = &(PythonKinds[K_CLASS]); + tag.extensionFields.scopeKindIndex = K_CLASS; tag.extensionFields.scopeName = vStringValue (parent); } else { - tag.extensionFields.scopeKind = &(PythonKinds[K_FUNCTION]); + tag.extensionFields.scopeKindIndex = K_FUNCTION; tag.extensionFields.scopeName = vStringValue (parent); } } @@ -186,10 +186,10 @@ static void makeVariableTag (vString *const var, vString *const parent, bool is_class_parent) { tagEntryInfo tag; - initTagEntry (&tag, vStringValue (var), &(PythonKinds[K_VARIABLE])); + initTagEntry (&tag, vStringValue (var), K_VARIABLE); if (vStringLength (parent) > 0) { - tag.extensionFields.scopeKind = &(PythonKinds[K_CLASS]); + tag.extensionFields.scopeKindIndex = K_CLASS; tag.extensionFields.scopeName = vStringValue (parent); } addAccessFields (&tag, var, K_VARIABLE, vStringLength (parent) > 0, @@ -371,7 +371,7 @@ static void parseImports (const char *cp) if (strcmp (vStringValue (name_next), "as") != 0 && strcmp (vStringValue (name), "as") != 0) { - makeSimpleTag (name, PythonKinds, K_IMPORT); + makeSimpleTag (name, K_IMPORT); } } vStringDelete (name); @@ -456,7 +456,7 @@ static bool constructParentString(NestingLevels *nls, int indent, if (e) { vStringCatS(result, e->name); - is_class = ((e->kind - PythonKinds) == K_CLASS); + is_class = (e->kindIndex == K_CLASS); } else is_class = false; @@ -853,7 +853,7 @@ extern parserDefinition *PythonParser (void) { static const char *const extensions[] = { "py", "pyx", "pxd", "pxi" ,"scons", NULL }; parserDefinition *def = parserNew ("Python"); - def->kinds = PythonKinds; + def->kindTable = PythonKinds; def->kindCount = ARRAY_SIZE (PythonKinds); def->extensions = extensions; def->parser = findPythonTags; diff --git a/ctags/parsers/r.c b/ctags/parsers/r.c index 6723fd95..941dc0cf 100644 --- a/ctags/parsers/r.c +++ b/ctags/parsers/r.c @@ -34,7 +34,7 @@ typedef enum { KIND_COUNT } rKind; -static kindOption RKinds [KIND_COUNT] = { +static kindDefinition RKinds [KIND_COUNT] = { {true, 'f', "function", "functions"}, {true, 'l', "library", "libraries"}, {true, 's', "source", "sources"}, @@ -43,7 +43,7 @@ static kindOption RKinds [KIND_COUNT] = { static void makeRTag (const vString * const name, rKind kind) { tagEntryInfo e; - initTagEntry(&e, vStringValue(name), &(RKinds[kind])); + initTagEntry(&e, vStringValue(name), kind); Assert (kind < KIND_COUNT); @@ -168,7 +168,7 @@ extern parserDefinition *RParser (void) */ static const char *const extensions [] = { "r", "s", "q", NULL }; parserDefinition *const def = parserNew ("R"); - def->kinds = RKinds; + def->kindTable = RKinds; def->kindCount = ARRAY_SIZE (RKinds); def->extensions = extensions; def->parser = createRTags; diff --git a/ctags/parsers/rest.c b/ctags/parsers/rest.c deleted file mode 100644 index 1a9dd2e2..00000000 --- a/ctags/parsers/rest.c +++ /dev/null @@ -1,207 +0,0 @@ -/* -* -* Copyright (c) 2007-2011, Nick Treleaven -* -* This source code is released for free distribution under the terms of the -* GNU General Public License. -* -* This module contains functions for generating tags for reStructuredText (reST) files. -*/ - -/* -* INCLUDE FILES -*/ -#include "general.h" /* must always come first */ - -#include -#include - -#include "parse.h" -#include "read.h" -#include "vstring.h" -#include "nestlevel.h" -#include "routines.h" -#include "entry.h" - -/* -* DATA DEFINITIONS -*/ -typedef enum { - K_CHAPTER = 0, - K_SECTION, - K_SUBSECTION, - K_SUBSUBSECTION, - SECTION_COUNT -} restKind; - -static kindOption RestKinds[] = { - { true, 'n', "namespace", "chapters"}, - { true, 'm', "member", "sections" }, - { true, 'd', "macro", "subsections" }, - { true, 'v', "variable", "subsubsections" } -}; - -static char kindchars[SECTION_COUNT]; - -static NestingLevels *nestingLevels = NULL; - -/* -* FUNCTION DEFINITIONS -*/ - -static void popNestingLevelToKind(const int kind) -{ - NestingLevel *nl; - tagEntryInfo *e; - - while (1) - { - nl = nestingLevelsGetCurrent(nestingLevels); - e = getEntryOfNestingLevel (nl); - if ((nl && (e == NULL)) || (e && (e->kind - RestKinds) >= kind)) - nestingLevelsPop(nestingLevels); - else - break; - } -} - -static void makeRestTag (const vString* const name, const int kind) -{ - int r = CORK_NIL; - - popNestingLevelToKind(kind); - - if (vStringLength (name) > 0) - { - tagEntryInfo e; - - initTagEntry (&e, vStringValue (name), &(RestKinds [kind])); - - e.lineNumber--; /* we want the line before the '---' underline chars */ - - r = makeTagEntry (&e); - } - nestingLevelsPush(nestingLevels, r); -} - - -/* checks if str is all the same character */ -static bool issame(const char *str) -{ - char first = *str; - - while (*str) - { - char c; - - str++; - c = *str; - if (c && c != first) - return false; - } - return true; -} - - -static int get_kind(char c) -{ - int i; - - for (i = 0; i < SECTION_COUNT; i++) - { - if (kindchars[i] == c) - return i; - - if (kindchars[i] == 0) - { - kindchars[i] = c; - return i; - } - } - return -1; -} - - -/* computes the length of an UTF-8 string - * if the string doesn't look like UTF-8, return -1 */ -static int utf8_strlen(const char *buf, int buf_len) -{ - int len = 0; - const char *end = buf + buf_len; - - for (len = 0; buf < end; len ++) - { - /* perform quick and naive validation (no sub-byte checking) */ - if (! (*buf & 0x80)) - buf ++; - else if ((*buf & 0xe0) == 0xc0) - buf += 2; - else if ((*buf & 0xf0) == 0xe0) - buf += 3; - else if ((*buf & 0xf8) == 0xf0) - buf += 4; - else /* not a valid leading UTF-8 byte, abort */ - return -1; - - if (buf > end) /* incomplete last byte */ - return -1; - } - - return len; -} - - -/* TODO: parse overlining & underlining as distinct sections. */ -static void findRestTags (void) -{ - vString *name = vStringNew (); - const unsigned char *line; - - memset(kindchars, 0, sizeof kindchars); - nestingLevels = nestingLevelsNew(0); - - while ((line = readLineFromInputFile ()) != NULL) - { - int line_len = strlen((const char*) line); - int name_len_bytes = vStringLength(name); - int name_len = utf8_strlen(vStringValue(name), name_len_bytes); - - /* if the name doesn't look like UTF-8, assume one-byte charset */ - if (name_len < 0) - name_len = name_len_bytes; - - /* underlines must be the same length or more */ - if (line_len >= name_len && name_len > 0 && - ispunct(line[0]) && issame((const char*) line)) - { - char c = line[0]; - int kind = get_kind(c); - - if (kind >= 0) - { - makeRestTag(name, kind); - continue; - } - } - vStringClear (name); - if (! isspace(*line)) - vStringCatS(name, (const char*) line); - } - vStringDelete (name); - nestingLevelsFree(nestingLevels); -} - -extern parserDefinition* RestParser (void) -{ - static const char *const patterns [] = { "*.rest", "*.reST", NULL }; - static const char *const extensions [] = { "rest", NULL }; - parserDefinition* const def = parserNew ("reStructuredText"); - - def->kinds = RestKinds; - def->kindCount = ARRAY_SIZE (RestKinds); - def->patterns = patterns; - def->extensions = extensions; - def->parser = findRestTags; - def->useCork = true; - return def; -} diff --git a/ctags/parsers/rst.c b/ctags/parsers/rst.c new file mode 100644 index 00000000..938f09c5 --- /dev/null +++ b/ctags/parsers/rst.c @@ -0,0 +1,379 @@ +/* +* +* Copyright (c) 2007-2011, Nick Treleaven +* +* This source code is released for free distribution under the terms of the +* GNU General Public License version 2 or (at your option) any later version. +* +* This module contains functions for generating tags for reStructuredText (reST) files. +*/ + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ + +#include +#include + +#include "parse.h" +#include "read.h" +#include "vstring.h" +#include "nestlevel.h" +#include "entry.h" +#include "routines.h" +#include "field.h" + +/* +* DATA DEFINITIONS +*/ +typedef enum { + K_EOF = -1, + K_CHAPTER = 0, + K_SECTION, + K_SUBSECTION, + K_SUBSUBSECTION, + K_TARGET, + SECTION_COUNT +} rstKind; + +static kindDefinition RstKinds[] = { + { true, 'c', "chapter", "chapters"}, + { true, 's', "section", "sections" }, + { true, 'S', "subsection", "subsections" }, + { true, 't', "subsubsection", "subsubsections" }, + { true, 'T', "target", "targets" }, +}; + +typedef enum { + F_SECTION_MARKER, +} rstField; + +static fieldDefinition RstFields [] = { + { + .name = "sectionMarker", + .description = "character used for declaring section", + .enabled = false, + }, +}; + +static char kindchars[SECTION_COUNT]; + +static NestingLevels *nestingLevels = NULL; + +/* +* FUNCTION DEFINITIONS +*/ + +static NestingLevel *getNestingLevel(const int kind) +{ + NestingLevel *nl; + tagEntryInfo *e; + + int d = 0; + + if (kind > K_EOF) + { + d++; + /* 1. we want the line before the '---' underline chars */ + d++; + /* 2. we want the line before the next section/chapter title. */ + } + + while (1) + { + nl = nestingLevelsGetCurrent(nestingLevels); + e = getEntryOfNestingLevel (nl); + if ((nl && (e == NULL)) || (e && e->kindIndex >= kind)) + { + if (e) + e->extensionFields.endLine = (getInputLineNumber() - d); + nestingLevelsPop(nestingLevels); + } + else + break; + } + return nl; +} + +static int makeTargetRstTag(const vString* const name) +{ + tagEntryInfo e; + + initTagEntry (&e, vStringValue (name), K_TARGET); + + const NestingLevel *nl = nestingLevelsGetCurrent(nestingLevels); + tagEntryInfo *parent = NULL; + if (nl) + parent = getEntryOfNestingLevel (nl); + + if (parent) + { + e.extensionFields.scopeKindIndex = parent->kindIndex; + e.extensionFields.scopeName = parent->name; + } + + return makeTagEntry (&e); +} + +static void makeSectionRstTag(const vString* const name, const int kind, const MIOPos filepos, + char marker) +{ + const NestingLevel *const nl = getNestingLevel(kind); + tagEntryInfo *parent; + + int r = CORK_NIL; + + if (vStringLength (name) > 0) + { + tagEntryInfo e; + char m [2] = { [1] = '\0' }; + + initTagEntry (&e, vStringValue (name), kind); + + e.lineNumber--; /* we want the line before the '---' underline chars */ + e.filePosition = filepos; + + parent = getEntryOfNestingLevel (nl); + if (parent && (parent->kindIndex < kind)) + { +#if 1 + e.extensionFields.scopeKindIndex = parent->kindIndex; + e.extensionFields.scopeName = parent->name; +#else + /* TODO + + Following code makes the scope information full qualified form. + Do users want the full qualified form? + --- ./Units/rst.simple.d/expected.tags 2015-12-18 01:32:35.574255617 +0900 + +++ /home/yamato/var/ctags-github/Units/rst.simple.d/FILTERED.tmp 2016-05-05 03:05:38.165604756 +0900 + @@ -5,2 +5,2 @@ + -Subsection 1.1.1 input.rst /^Subsection 1.1.1$/;" S section:Section 1.1 + -Subsubsection 1.1.1.1 input.rst /^Subsubsection 1.1.1.1$/;" t subsection:Subsection 1.1.1 + +Subsection 1.1.1 input.rst /^Subsection 1.1.1$/;" S section:Chapter 1.Section 1.1 + +Subsubsection 1.1.1.1 input.rst /^Subsubsection 1.1.1.1$/;" t subsection:Chapter 1.Section 1.1.Subsection 1.1.1 + */ + e.extensionFields.scopeIndex = nl->corkIndex; +#endif + } + + m[0] = marker; + attachParserField (&e, RstFields [F_SECTION_MARKER].ftype, m); + r = makeTagEntry (&e); + } + nestingLevelsPush(nestingLevels, r); +} + + +/* checks if str is all the same character */ +static bool issame(const char *str) +{ + char first = *str; + + while (*str) + { + char c; + + str++; + c = *str; + if (c && c != first) + return false; + } + return true; +} + + +static int get_kind(char c) +{ + int i; + + for (i = 0; i < SECTION_COUNT; i++) + { + if (kindchars[i] == c) + return i; + + if (kindchars[i] == 0) + { + kindchars[i] = c; + return i; + } + } + return -1; +} + + +/* computes the length of an UTF-8 string + * if the string doesn't look like UTF-8, return -1 */ +static int utf8_strlen(const char *buf, int buf_len) +{ + int len = 0; + const char *end = buf + buf_len; + + for (len = 0; buf < end; len ++) + { + /* perform quick and naive validation (no sub-byte checking) */ + if (! (*buf & 0x80)) + buf ++; + else if ((*buf & 0xe0) == 0xc0) + buf += 2; + else if ((*buf & 0xf0) == 0xe0) + buf += 3; + else if ((*buf & 0xf8) == 0xf0) + buf += 4; + else /* not a valid leading UTF-8 byte, abort */ + return -1; + + if (buf > end) /* incomplete last byte */ + return -1; + } + + return len; +} + + +static const unsigned char *is_target_line (const unsigned char *line) +{ + if ((line [0] == '.') && (line [1] == '.') && (line [2] == ' ') + && (line [3] == '_')) + return line + 4; + return NULL; +} + +static int capture_target (const unsigned char *target_line) +{ + vString *name = vStringNew (); + unsigned char terminator; + int r = CORK_NIL; + + if (*target_line == '`') + terminator = '`'; + else if (!isspace (*target_line) && *target_line != '\0') + { + /* "Simple reference names are single words consisting of + * alphanumerics plus isolated (no two adjacent) internal + * hyphens, underscores, periods, colons and plus signs; no + * whitespace or other characters are allowed." + * -- http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#reference-names + */ + vStringPut (name, *target_line); + terminator = ':'; + } + else + goto out; + + target_line++; + + + bool escaped = false; + while (*target_line != '\0') + { + if (escaped) + { + vStringPut (name, *target_line); + escaped = false; + } + else + { + if (*target_line == '\\') + { + vStringPut (name, *target_line); + escaped = true; + } + else if (*target_line == terminator) + break; + else + vStringPut (name, *target_line); + } + target_line++; + } + + if (vStringLength (name) == 0) + goto out; + + r = makeTargetRstTag (name); + + out: + vStringDelete (name); + return r; +} + +/* TODO: parse overlining & underlining as distinct sections. */ +static void findRstTags (void) +{ + vString *name = vStringNew (); + MIOPos filepos; + const unsigned char *line; + const unsigned char *target_line; + + memset(&filepos, 0, sizeof(filepos)); + memset(kindchars, 0, sizeof kindchars); + nestingLevels = nestingLevelsNew(0); + + while ((line = readLineFromInputFile ()) != NULL) + { + /* Handle .. _target: + * http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#hyperlink-targets + */ + if ((target_line = is_target_line (line)) != NULL) + { + if (capture_target (target_line) != CORK_NIL) + { + vStringClear (name); + continue; + } + } + + int line_len = strlen((const char*) line); + int name_len_bytes = vStringLength(name); + /* FIXME: this isn't right, actually we need the real display width, + * taking into account double-width characters and stuff like that. + * But duh. */ + int name_len = utf8_strlen(vStringValue(name), name_len_bytes); + + /* if the name doesn't look like UTF-8, assume one-byte charset */ + if (name_len < 0) + name_len = name_len_bytes; + + /* underlines must be the same length or more */ + if (line_len >= name_len && name_len > 0 && + ispunct(line[0]) && issame((const char*) line)) + { + char c = line[0]; + int kind = get_kind(c); + + if (kind >= 0) + { + makeSectionRstTag(name, kind, filepos, c); + continue; + } + } + vStringClear (name); + if (!isspace(*line)) + { + vStringCatS(name, (const char*)line); + filepos = getInputFilePosition(); + } + } + /* Force popping all nesting levels */ + getNestingLevel (K_EOF); + vStringDelete (name); + nestingLevelsFree(nestingLevels); +} + +extern parserDefinition* RstParser (void) +{ + static const char *const extensions [] = { "rest", "reST", "rst", NULL }; + parserDefinition* const def = parserNew ("ReStructuredText"); + + def->kindTable = RstKinds; + def->kindCount = ARRAY_SIZE (RstKinds); + def->extensions = extensions; + def->parser = findRstTags; + + def->fieldTable = RstFields; + def->fieldCount = ARRAY_SIZE (RstFields); + + def->useCork = true; + + return def; +} diff --git a/ctags/parsers/ruby.c b/ctags/parsers/ruby.c index 6000b124..2e7604e4 100644 --- a/ctags/parsers/ruby.c +++ b/ctags/parsers/ruby.c @@ -35,7 +35,7 @@ typedef enum { /* * DATA DEFINITIONS */ -static kindOption RubyKinds [] = { +static kindDefinition RubyKinds [] = { { true, 'c', "class", "classes" }, { true, 'f', "method", "methods" }, { true, 'm', "module", "modules" }, @@ -196,7 +196,7 @@ static void emitRubyTag (vString* name, rubyKind kind) lvl = nestingLevelsGetCurrent (nesting); parent = getEntryOfNestingLevel (lvl); if (parent) - parent_kind = parent->kind - RubyKinds; + parent_kind = parent->kindIndex; qualified_name = vStringValue (name); unqualified_name = strrchr (qualified_name, SCOPE_SEPARATOR); @@ -216,12 +216,12 @@ static void emitRubyTag (vString* name, rubyKind kind) else unqualified_name = qualified_name; - initTagEntry (&tag, unqualified_name, &(RubyKinds [kind])); + initTagEntry (&tag, unqualified_name, kind); if (vStringLength (scope) > 0) { Assert (0 <= parent_kind && (size_t) parent_kind < (ARRAY_SIZE (RubyKinds))); - tag.extensionFields.scopeKind = &(RubyKinds [parent_kind]); + tag.extensionFields.scopeKindIndex = parent_kind; tag.extensionFields.scopeName = vStringValue (scope); } r = makeTagEntry (&tag); @@ -378,7 +378,7 @@ static void enterUnnamedScope (void) if (e_parent) { tagEntryInfo e; - initTagEntry (&e, "", e_parent->kind); + initTagEntry (&e, "", e_parent->kindIndex); e.placeholder = 1; r = makeTagEntry (&e); } @@ -484,7 +484,7 @@ static void findRubyTags (void) * end * end */ - if (e && (e->kind - RubyKinds) == K_CLASS && strlen (e->name) == 0) + if (e && e->kindIndex == K_CLASS && strlen (e->name) == 0) kind = K_SINGLETON; readAndEmitTag (&cp, kind); } @@ -555,7 +555,7 @@ extern parserDefinition* RubyParser (void) { static const char *const extensions [] = { "rb", "ruby", NULL }; parserDefinition* def = parserNewFull ("Ruby", KIND_FILE_ALT); - def->kinds = RubyKinds; + def->kindTable = RubyKinds; def->kindCount = ARRAY_SIZE (RubyKinds); def->extensions = extensions; def->parser = findRubyTags; diff --git a/ctags/parsers/rust.c b/ctags/parsers/rust.c index d6f6fd2e..b473d0f4 100644 --- a/ctags/parsers/rust.c +++ b/ctags/parsers/rust.c @@ -47,7 +47,7 @@ typedef enum { K_NONE } RustKind; -static kindOption rustKinds[] = { +static kindDefinition rustKinds[] = { {true, 'n', "module", "module"}, {true, 's', "struct", "structural type"}, {true, 'i', "interface", "trait interface"}, @@ -439,7 +439,7 @@ static void addTag (vString* ident, const char* type, const char* arg_list, int if (kind == K_NONE) return; tagEntryInfo tag; - initTagEntry(&tag, ident->buffer, &(rustKinds[kind])); + initTagEntry(&tag, ident->buffer, kind); tag.lineNumber = line; tag.filePosition = pos; @@ -449,7 +449,7 @@ static void addTag (vString* ident, const char* type, const char* arg_list, int tag.extensionFields.varType = type; if (parent_kind != K_NONE) { - tag.extensionFields.scopeKind = &(rustKinds[parent_kind]); + tag.extensionFields.scopeKindIndex = parent_kind; tag.extensionFields.scopeName = scope->buffer; } makeTagEntry(&tag); @@ -974,7 +974,7 @@ extern parserDefinition *RustParser (void) { static const char *const extensions[] = { "rs", NULL }; parserDefinition *def = parserNewFull ("Rust", KIND_FILE_ALT); - def->kinds = rustKinds; + def->kindTable = rustKinds; def->kindCount = ARRAY_SIZE (rustKinds); def->extensions = extensions; def->parser = findRustTags; diff --git a/ctags/parsers/sh.c b/ctags/parsers/sh.c index fd371a51..69a8d50c 100644 --- a/ctags/parsers/sh.c +++ b/ctags/parsers/sh.c @@ -28,7 +28,7 @@ typedef enum { K_FUNCTION } shKind; -static kindOption ShKinds [] = { +static kindDefinition ShKinds [] = { { true, 'f', "function", "functions"} }; @@ -90,7 +90,7 @@ static void findShTags (void) functionFound = true; } if (functionFound) - makeSimpleTag (name, ShKinds, K_FUNCTION); + makeSimpleTag (name, K_FUNCTION); vStringClear (name); } vStringDelete (name); @@ -102,7 +102,7 @@ extern parserDefinition* ShParser (void) "sh", "SH", "bsh", "bash", "ksh", "zsh", "ash", NULL }; parserDefinition* def = parserNew ("Sh"); - def->kinds = ShKinds; + def->kindTable = ShKinds; def->kindCount = ARRAY_SIZE (ShKinds); def->extensions = extensions; def->parser = findShTags; diff --git a/ctags/parsers/sql.c b/ctags/parsers/sql.c index dde34094..8d4ebaa8 100644 --- a/ctags/parsers/sql.c +++ b/ctags/parsers/sql.c @@ -200,7 +200,7 @@ typedef enum { SQLTAG_COUNT } sqlKind; -static kindOption SqlKinds [] = { +static kindDefinition SqlKinds [] = { { true, 'c', "cursor", "cursors" }, { false, 'd', "prototype", "prototypes" }, { true, 'f', "function", "functions" }, @@ -437,7 +437,7 @@ static void makeSqlTag (tokenInfo *const token, const sqlKind kind) { const char *const name = vStringValue (token->string); tagEntryInfo e; - initTagEntry (&e, name, &(SqlKinds [kind])); + initTagEntry (&e, name, kind); e.lineNumber = token->lineNumber; e.filePosition = token->filePosition; @@ -445,7 +445,7 @@ static void makeSqlTag (tokenInfo *const token, const sqlKind kind) if (vStringLength (token->scope) > 0) { Assert (token->scopeKind < SQLTAG_COUNT); - e.extensionFields.scopeKind = &(SqlKinds [token->scopeKind]); + e.extensionFields.scopeKindIndex = token->scopeKind; e.extensionFields.scopeName = vStringValue (token->scope); } @@ -2339,7 +2339,7 @@ extern parserDefinition* SqlParser (void) { static const char *const extensions [] = { "sql", NULL }; parserDefinition* def = parserNewFull ("SQL", KIND_FILE_ALT); - def->kinds = SqlKinds; + def->kindTable = SqlKinds; def->kindCount = ARRAY_SIZE (SqlKinds); def->extensions = extensions; def->parser = findSqlTags; diff --git a/ctags/parsers/tcl.c b/ctags/parsers/tcl.c index 2b43b7ba..4b046028 100644 --- a/ctags/parsers/tcl.c +++ b/ctags/parsers/tcl.c @@ -26,7 +26,7 @@ typedef enum { K_CLASS, K_METHOD, K_PROCEDURE, K_MODULE } tclKind; -static kindOption TclKinds [] = { +static kindDefinition TclKinds [] = { { true, 'c', "class", "classes" }, { true, 'm', "method", "methods" }, { true, 'p', "procedure", "procedures" }, @@ -48,7 +48,7 @@ static const unsigned char *makeTclTag ( vStringPut (name, (int) *cp); ++cp; } - makeSimpleTag (name, TclKinds, kind); + makeSimpleTag (name, kind); return cp; } @@ -137,7 +137,7 @@ extern parserDefinition* TclParser (void) { static const char *const extensions [] = { "tcl", "tk", "wish", "itcl", NULL }; parserDefinition* def = parserNew ("Tcl"); - def->kinds = TclKinds; + def->kindTable = TclKinds; def->kindCount = ARRAY_SIZE (TclKinds); def->extensions = extensions; def->parser = findTclTags; diff --git a/ctags/parsers/latex.c b/ctags/parsers/tex.c similarity index 95% rename from ctags/parsers/latex.c rename to ctags/parsers/tex.c index b25317c4..8bc1364e 100644 --- a/ctags/parsers/latex.c +++ b/ctags/parsers/tex.c @@ -35,7 +35,7 @@ typedef enum { K_LABEL } TeXKind; -static kindOption TeXKinds[] = { +static kindDefinition TeXKinds[] = { { true, 'f', "function", "command definitions" }, { true, 'c', "class", "environment definitions" }, { true, 'm', "member", "labels, sections and bibliography" }, @@ -106,7 +106,7 @@ static void createTag(int flags, TeXKind kind, const char * l) ++l; } while ((*l != '\0') && (*l != '}')); if (name->buffer[0] != '}') - makeSimpleTag(name, TeXKinds, kind); + makeSimpleTag(name, kind); } else if (isalpha((int) *l) || *l == '@') { @@ -115,12 +115,12 @@ static void createTag(int flags, TeXKind kind, const char * l) vStringPut (name, (int) *l); ++l; } while (isalpha((int) *l) || *l == '@'); - makeSimpleTag(name, TeXKinds, kind); + makeSimpleTag(name, kind); } else { vStringPut(name, (int) *l); - makeSimpleTag(name, TeXKinds, kind); + makeSimpleTag(name, kind); } no_tag: @@ -230,11 +230,11 @@ static void findTeXTags(void) } } -extern parserDefinition* LaTeXParser (void) +extern parserDefinition* TexParser (void) { static const char *const extensions [] = { "tex", "sty", "idx", NULL }; parserDefinition * def = parserNew ("LaTeX"); - def->kinds = TeXKinds; + def->kindTable = TeXKinds; def->kindCount = ARRAY_SIZE (TeXKinds); def->extensions = extensions; def->parser = findTeXTags; diff --git a/ctags/parsers/txt2tags.c b/ctags/parsers/txt2tags.c index 56290fb0..3d7713b3 100644 --- a/ctags/parsers/txt2tags.c +++ b/ctags/parsers/txt2tags.c @@ -38,10 +38,10 @@ typedef enum { } Txt2tagsKind; static scopeSeparator Txt2TagsSeparators [] = { - { KIND_WILDCARD, SCOPE_SEPARATOR } + { KIND_WILDCARD_INDEX, SCOPE_SEPARATOR } }; -static kindOption Txt2tagsKinds[] = { +static kindDefinition Txt2tagsKinds[] = { { true, 'm', "member", "sections", ATTACH_SEPARATORS(Txt2TagsSeparators) }, }; @@ -61,8 +61,7 @@ static int makeTxt2tagsTag (const vString* const name, { tagEntryInfo e; NestingLevel *nl; - kindOption *kind = &Txt2tagsKinds[type]; - initTagEntry (&e, vStringValue(name), kind); + initTagEntry (&e, vStringValue(name), type); nl = nestingLevelsGetCurrent (nls); if (nl) @@ -184,7 +183,7 @@ extern parserDefinition* Txt2tagsParser (void) static const char *const extensions [] = { "t2t", NULL }; parserDefinition* const def = parserNew ("Txt2tags"); - def->kinds = Txt2tagsKinds; + def->kindTable = Txt2tagsKinds; def->kindCount = ARRAY_SIZE (Txt2tagsKinds); def->patterns = patterns; def->extensions = extensions; diff --git a/ctags/parsers/verilog.c b/ctags/parsers/verilog.c index 9fd826dc..236382b9 100644 --- a/ctags/parsers/verilog.c +++ b/ctags/parsers/verilog.c @@ -55,7 +55,7 @@ static int Ungetc; static int Lang_verilog; static jmp_buf Exception; -static kindOption VerilogKinds [] = { +static kindDefinition VerilogKinds [] = { { true, 'c', "constant", "constants (define, parameter, specparam)" }, { true, 'e', "event", "events" }, { true, 'f', "function", "functions" }, @@ -215,7 +215,7 @@ static void tagNameList (const verilogKind kind, int c) if (isIdentifierCharacter (c)) { readIdentifier (name, c); - makeSimpleTag (name, VerilogKinds, kind); + makeSimpleTag (name, kind); } else break; @@ -255,7 +255,7 @@ static void findTag (vString *const name) /* Bug #961001: Verilog compiler directives are line-based. */ int c = skipWhite (vGetc ()); readIdentifier (name, c); - makeSimpleTag (name, VerilogKinds, kind); + makeSimpleTag (name, kind); /* Skip the rest of the line. */ do { c = vGetc(); @@ -323,7 +323,7 @@ extern parserDefinition* VerilogParser (void) { static const char *const extensions [] = { "v", NULL }; parserDefinition* def = parserNew ("Verilog"); - def->kinds = VerilogKinds; + def->kindTable = VerilogKinds; def->kindCount = ARRAY_SIZE (VerilogKinds); def->extensions = extensions; def->parser = findVerilogTags; diff --git a/ctags/parsers/vhdl.c b/ctags/parsers/vhdl.c index 9d26eb86..f53329da 100644 --- a/ctags/parsers/vhdl.c +++ b/ctags/parsers/vhdl.c @@ -61,7 +61,7 @@ static vString* Lastname=NULL; static vString* Keyword=NULL; static vString* TagName=NULL; -static kindOption VhdlKinds [] = { +static kindDefinition VhdlKinds [] = { { true, 'c', "variable", "constants" }, { true, 't', "typedef", "types" }, { true, 'v', "variable", "variables" }, @@ -177,7 +177,7 @@ static void tagNameList (const vhdlKind kind, int c) if (isIdentifierCharacter (c)) { readIdentifier (TagName, c); - makeSimpleTag (TagName, VhdlKinds, kind); + makeSimpleTag (TagName, kind); } } @@ -202,7 +202,7 @@ static void findTag (vString *const name) kind = (vhdlKind)lookupKeyword (vStringValue (Keyword), Lang_vhdl); if (kind == K_PROCESS || kind == K_BLOCK || kind == K_PORT) { - makeSimpleTag (Lastname, VhdlKinds, kind); + makeSimpleTag (Lastname, kind); } } } else { @@ -225,7 +225,7 @@ static void findTag (vString *const name) } else if (kind == K_PROCESS || kind == K_BLOCK) { vStringCopyS(TagName,"unnamed"); - makeSimpleTag (TagName, VhdlKinds, kind); + makeSimpleTag (TagName, kind); } else { c = skipWhite (vGetc ()); if (c=='\"') @@ -278,7 +278,7 @@ extern parserDefinition* VhdlParser (void) { static const char *const extensions [] = { "vhdl", "vhd", NULL }; parserDefinition* def = parserNew ("Vhdl"); - def->kinds = VhdlKinds; + def->kindTable = VhdlKinds; def->kindCount = ARRAY_SIZE (VhdlKinds); def->extensions = extensions; def->parser = findVhdlTags; diff --git a/src/tagmanager/tm_parser.c b/src/tagmanager/tm_parser.c index 56ceb8e2..d0aeb4b8 100644 --- a/src/tagmanager/tm_parser.c +++ b/src/tagmanager/tm_parser.c @@ -30,6 +30,20 @@ typedef struct TMTagType type; } TMParserMapEntry; +/* Allows remapping a subparser tag type to another type if there's a clash with + * the master parser tag type. Only subparser tag types explicitly listed within + * TMSubparserMapEntry maps are added to tag manager - tags with types not listed + * are discarded to prevent uncontrolled merging of tags from master parser and + * subparsers. */ +typedef struct +{ + TMTagType orig_type; + TMTagType new_type; +} TMSubparserMapEntry; + + +static GHashTable *subparser_map = NULL; + static TMParserMapEntry map_C[] = { {'c', tm_tag_class_t}, @@ -242,6 +256,7 @@ static TMParserMapEntry map_JAVASCRIPT[] = { {'p', tm_tag_member_t}, {'C', tm_tag_macro_t}, {'v', tm_tag_variable_t}, + {'g', tm_tag_function_t}, }; /* not in universal-ctags */ @@ -287,20 +302,23 @@ static TMParserMapEntry map_HAXE[] = { {'t', tm_tag_typedef_t}, }; -/* not in universal-ctags */ static TMParserMapEntry map_REST[] = { - {'n', tm_tag_namespace_t}, - {'m', tm_tag_member_t}, - {'d', tm_tag_macro_t}, - {'v', tm_tag_variable_t}, + {'c', tm_tag_namespace_t}, + {'s', tm_tag_member_t}, + {'S', tm_tag_macro_t}, + {'t', tm_tag_variable_t}, + {'T', tm_tag_undef_t}, }; static TMParserMapEntry map_HTML[] = { {'a', tm_tag_member_t}, - {'f', tm_tag_function_t}, - {'n', tm_tag_namespace_t}, - {'c', tm_tag_class_t}, - {'v', tm_tag_variable_t}, + {'h', tm_tag_namespace_t}, + {'i', tm_tag_class_t}, + {'j', tm_tag_variable_t}, +}; + +static TMSubparserMapEntry subparser_HTML_javascript_map[] = { + {tm_tag_function_t, tm_tag_function_t}, }; static TMParserMapEntry map_F77[] = { @@ -428,13 +446,14 @@ static TMParserMapEntry map_OBJC[] = { {'M', tm_tag_macro_t}, }; -/* not in universal-ctags */ static TMParserMapEntry map_ASCIIDOC[] = { - {'n', tm_tag_namespace_t}, - {'m', tm_tag_member_t}, - {'d', tm_tag_macro_t}, - {'v', tm_tag_variable_t}, - {'s', tm_tag_struct_t}, + {'c', tm_tag_namespace_t}, + {'s', tm_tag_member_t}, + {'S', tm_tag_macro_t}, + {'t', tm_tag_variable_t}, + {'T', tm_tag_struct_t}, + {'u', tm_tag_undef_t}, + {'a', tm_tag_undef_t}, }; /* not in universal-ctags */ @@ -587,6 +606,65 @@ gchar tm_parser_get_tag_kind(TMTagType type, TMParserType lang) } +static void add_subparser(TMParserType lang, TMParserType sublang, TMSubparserMapEntry *map, guint map_size) +{ + guint i; + GPtrArray *mapping; + GHashTable *lang_map = g_hash_table_lookup(subparser_map, GINT_TO_POINTER(lang)); + + if (!lang_map) + { + lang_map = g_hash_table_new(g_direct_hash, g_direct_equal); + g_hash_table_insert(subparser_map, GINT_TO_POINTER(lang), lang_map); + } + + mapping = g_ptr_array_new(); + for (i = 0; i < map_size; i++) + g_ptr_array_add(mapping, &map[i]); + + g_hash_table_insert(lang_map, GINT_TO_POINTER(sublang), mapping); +} + + +#define SUBPARSER_MAP_ENTRY(lang, sublang, map) add_subparser(TM_PARSER_##lang, TM_PARSER_##sublang, map, G_N_ELEMENTS(map)) + +static void init_subparser_map() +{ + SUBPARSER_MAP_ENTRY(HTML, JAVASCRIPT, subparser_HTML_javascript_map); +} + + +TMTagType tm_parser_get_subparser_type(TMParserType lang, TMParserType sublang, TMTagType type) +{ + guint i; + GHashTable *lang_map; + GPtrArray *mapping; + + if (!subparser_map) + { + subparser_map = g_hash_table_new(g_direct_hash, g_direct_equal); + init_subparser_map(); + } + + lang_map = g_hash_table_lookup(subparser_map, GINT_TO_POINTER(lang)); + if (!lang_map) + return tm_tag_undef_t; + + mapping = g_hash_table_lookup(lang_map, GINT_TO_POINTER(sublang)); + if (!mapping) + return tm_tag_undef_t; + + for (i = 0; i < mapping->len; i++) + { + TMSubparserMapEntry *entry = mapping->pdata[i]; + if (entry->orig_type == type) + return entry->new_type; + } + + return tm_tag_undef_t; +} + + void tm_parser_verify_type_mappings(void) { TMParserType lang; diff --git a/src/tagmanager/tm_parser.h b/src/tagmanager/tm_parser.h index 5ec38cd3..f7b0c695 100644 --- a/src/tagmanager/tm_parser.h +++ b/src/tagmanager/tm_parser.h @@ -119,6 +119,8 @@ TMTagType tm_parser_get_tag_type(gchar kind, TMParserType lang); gchar tm_parser_get_tag_kind(TMTagType type, TMParserType lang); +TMTagType tm_parser_get_subparser_type(TMParserType lang, TMParserType sublang, TMTagType type); + const gchar *tm_parser_context_separator(TMParserType lang); gboolean tm_parser_has_full_context(TMParserType lang); diff --git a/src/tagmanager/tm_source_file.c b/src/tagmanager/tm_source_file.c index b14634c2..216596e6 100644 --- a/src/tagmanager/tm_source_file.c +++ b/src/tagmanager/tm_source_file.c @@ -181,7 +181,13 @@ static gboolean init_tag(TMTag *tag, TMSourceFile *file, const ctagsTag *tag_ent if (!tag_entry) return FALSE; - type = tm_parser_get_tag_type(tag_entry->kindLetter, file->lang); + type = tm_parser_get_tag_type(tag_entry->kindLetter, tag_entry->lang); + if (file->lang != tag_entry->lang) /* this is a tag from a subparser */ + { + /* check for possible re-definition of subparser type */ + type = tm_parser_get_subparser_type(file->lang, tag_entry->lang, type); + } + if (!tag_entry->name || type == tm_tag_undef_t) return FALSE; @@ -206,6 +212,8 @@ static gboolean init_tag(TMTag *tag, TMSourceFile *file, const ctagsTag *tag_ent if ((tm_tag_macro_t == tag->type) && (NULL != tag->arglist)) tag->type = tm_tag_macro_with_arg_t; tag->file = file; + /* redefine lang also for subparsers because the rest of Geany assumes that + * tags from a single file are from a single language */ tag->lang = file->lang; return TRUE; } diff --git a/tests/ctags/Makefile.am b/tests/ctags/Makefile.am index b9ef0007..a934b517 100644 --- a/tests/ctags/Makefile.am +++ b/tests/ctags/Makefile.am @@ -1,6 +1,9 @@ dist_check_SCRIPTS = runner.sh +# Disabled tests: +# simple.cbl + NULL = test_sources = \ 1795612.js \ @@ -154,6 +157,7 @@ test_sources = \ fortran_associate.f90 \ fortran_line_continuation.f90 \ func_typedef.h \ + geany.nsi \ general.cs \ hex2dec.sql \ implied_program.f \ @@ -268,22 +272,32 @@ test_sources = \ semicolon.f90 \ shebang.js \ signature.cpp \ + simple.abc \ + simple.asciidoc \ simple.bas \ - simple.cbl \ + simple.conf \ simple.d \ + simple.diff \ + simple.docbook \ + simple.hs \ + simple.hx \ simple.html \ + simple.inp \ simple.js \ simple.json \ simple.ksh \ simple.lua \ simple.mak \ + simple.md \ simple.php \ simple.pl \ simple.ps1 \ simple.py \ simple.rb \ + simple.rst \ simple.sh \ simple.tcl \ + simple.vala \ simple.zep \ size_t_wchar_t_alias.d \ size_t_wchar_t_typedef.c \ diff --git a/tests/ctags/complex-return.js.tags b/tests/ctags/complex-return.js.tags index b275162b..01424018 100644 --- a/tests/ctags/complex-return.js.tags +++ b/tests/ctags/complex-return.js.tags @@ -1,4 +1,6 @@ # format=tagmanager +AnonymousFunction1Ì16Í()Îclass2.c2m1Ö0 +AnonymousFunction2Ì16Í(n)Îclass3.c3m1Ö0 c2m1Ì128Í()Îclass2Ö0 c2m2Ì128Í(f)Îclass2Ö0 c2m3Ì128Í(f)Îclass2Ö0 diff --git a/tests/ctags/geany.nsi b/tests/ctags/geany.nsi new file mode 100644 index 00000000..e6a85ea9 --- /dev/null +++ b/tests/ctags/geany.nsi @@ -0,0 +1,470 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; geany.nsi - this file is part of Geany, a fast and lightweight IDE +; +; Copyright 2007-2012 Enrico Tröger +; Copyright 2007-2012 Nick Treleaven +; +; This program is free software; you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation; either version 2 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; if not, write to the Free Software +; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +; +; +; Installer script for Geany (Windows Installer) +; (Script originally generated by the HM NIS Edit Script Wizard) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +; Do a Cyclic Redundancy Check to make sure the installer was not corrupted by the download +CRCCheck force +RequestExecutionLevel highest ; set execution level for Windows Vista + +;;;;;;;;;;;;;;;;;;; +; helper defines ; +;;;;;;;;;;;;;;;;;;; +!define PRODUCT_NAME "Geany" +!define PRODUCT_VERSION "1.35" +!define PRODUCT_VERSION_ID "1.35.0.0" +!define PRODUCT_PUBLISHER "The Geany developer team" +!define PRODUCT_WEB_SITE "https://www.geany.org/" +!define PRODUCT_DIR_REGKEY "Software\Geany" +!define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" +!define PRODUCT_EXE "$INSTDIR\bin\Geany.exe" +!define PRODUCT_REGNAME "Geany.ProjectFile" +!define PRODUCT_EXT ".geany" +!define RESOURCEDIR "geany-${PRODUCT_VERSION}" +!define GTK_VERSION 2.24.30 + +;;;;;;;;;;;;;;;;;;;;; +; Version resource ; +;;;;;;;;;;;;;;;;;;;;; +VIProductVersion "${PRODUCT_VERSION_ID}" +VIAddVersionKey "ProductName" "${PRODUCT_NAME}" +VIAddVersionKey "FileVersion" "${PRODUCT_VERSION}" +VIAddVersionKey "ProductVersion" "${PRODUCT_VERSION}" +VIAddVersionKey "LegalCopyright" "Copyright 2005-2018 by the Geany developer team" +VIAddVersionKey "FileDescription" "${PRODUCT_NAME} Installer" + +BrandingText "$(^NAME) installer (NSIS 2.51)" +InstallDir "$PROGRAMFILES\Geany" +Name "${PRODUCT_NAME} ${PRODUCT_VERSION}" +SetCompressor /SOLID lzma +ShowInstDetails hide +ShowUnInstDetails hide +XPStyle on +!ifdef INCLUDE_GTK +OutFile "geany-${PRODUCT_VERSION}_setup.exe" +!else +OutFile "geany-${PRODUCT_VERSION}_nogtk_setup.exe" +!endif + +Var Answer +Var UserName +Var StartmenuFolder +Var UNINSTDIR + +;;;;;;;;;;;;;;;; +; MUI Settings ; +;;;;;;;;;;;;;;;; +!include "MUI2.nsh" + +;Reserve files used in .onInit, for faster start-up +ReserveFile "${NSISDIR}\Plugins\System.dll" +ReserveFile "${NSISDIR}\Plugins\UserInfo.dll" +ReserveFile "${NSISDIR}\Plugins\InstallOptions.dll" +ReserveFile "${NSISDIR}\Plugins\LangDLL.dll" + +!define MUI_ABORTWARNING +!define MUI_ICON "icons\geany.ico" +!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall-full.ico" + +; Welcome page +!insertmacro MUI_PAGE_WELCOME +; License page +;!define MUI_LICENSEPAGE_RADIOBUTTONS +!insertmacro MUI_PAGE_LICENSE "${RESOURCEDIR}\Copying.txt" +; Components page +!insertmacro MUI_PAGE_COMPONENTS +; Directory page +!define MUI_PAGE_CUSTOMFUNCTION_LEAVE OnDirLeave +!insertmacro MUI_PAGE_DIRECTORY +; Start menu page +!define MUI_STARTMENUPAGE_DEFAULTFOLDER "Geany" +!define MUI_STARTMENUPAGE_REGISTRY_ROOT HKLM +!define MUI_STARTMENUPAGE_REGISTRY_KEY "${PRODUCT_UNINST_KEY}" +!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" +!insertmacro MUI_PAGE_STARTMENU ${PRODUCT_NAME} "$StartmenuFolder" +; Instfiles page +!insertmacro MUI_PAGE_INSTFILES +; Finish page +!define MUI_FINISHPAGE_SHOWREADME "$INSTDIR\News.txt" +!define MUI_FINISHPAGE_SHOWREADME_TEXT "Show Release Notes" +!define MUI_FINISHPAGE_SHOWREADME_NOTCHECKED +!define MUI_FINISHPAGE_RUN "$INSTDIR\bin\Geany.exe" +!define MUI_FINISHPAGE_RUN_NOTCHECKED +!insertmacro MUI_PAGE_FINISH + +!insertmacro MUI_UNPAGE_INSTFILES ; Uninstaller page +!insertmacro MUI_LANGUAGE "English" ; Language file + +;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Sections and InstTypes ; +;;;;;;;;;;;;;;;;;;;;;;;;;;; +InstType "Full" +InstType "Minimal" + +Section "!Program Files" SEC01 + SectionIn RO 1 2 + SetOverwrite ifnewer + + SetOutPath "$INSTDIR" + File "${RESOURCEDIR}\*.txt" + + SetOutPath "$INSTDIR\bin" + File "${RESOURCEDIR}\bin\Geany.exe" + File "${RESOURCEDIR}\bin\*Geany*.dll" + # non-GTK dependencies + File "gtk\bin\libgcc_s_dw*.dll" + File "gtk\bin\libstdc++-*.dll" + File "gtk\bin\libwinpthread*.dll" + + SetOutPath "$INSTDIR\libexec" + File /r "${RESOURCEDIR}\libexec\*" + + SetOutPath "$INSTDIR\data" + File "${RESOURCEDIR}\data\GPL-2" + File "${RESOURCEDIR}\data\filetype_extensions.conf" + File "${RESOURCEDIR}\data\geany.glade" +!if ${GTK_VERSION} >= 3 + File "${RESOURCEDIR}\data\geany-3.20.css" + File "${RESOURCEDIR}\data\geany.css" +!else + File "${RESOURCEDIR}\data\geany.gtkrc" +!endif + File "${RESOURCEDIR}\data\snippets.conf" + File "${RESOURCEDIR}\data\ui_toolbar.xml" + + SetOutPath "$INSTDIR\data\filedefs" + File /r "${RESOURCEDIR}\data\filedefs\*" + + SetOutPath "$INSTDIR\data\templates" + File /r "${RESOURCEDIR}\data\templates\*" + + SetOutPath "$INSTDIR\data\colorschemes" + File /r "${RESOURCEDIR}\data\colorschemes\*" + # Geany color schemes project, don't bail out if they are missing + File /nonfatal /r "..\geany-themes\colorschemes\*.conf" + + SetOutPath "$INSTDIR\share\icons" + File /r "${RESOURCEDIR}\share\icons\*" + + SetOutPath "$INSTDIR" + + CreateShortCut "$INSTDIR\Geany.lnk" "$INSTDIR\bin\Geany.exe" + !insertmacro MUI_STARTMENU_WRITE_BEGIN ${PRODUCT_NAME} + CreateDirectory "$SMPROGRAMS\$StartmenuFolder" + CreateShortCut "$SMPROGRAMS\$StartmenuFolder\Geany.lnk" "$INSTDIR\bin\Geany.exe" + !insertmacro MUI_STARTMENU_WRITE_END + + ; register the extension .geany + ; write information about file type + WriteRegStr SHCTX "Software\Classes\${PRODUCT_REGNAME}" "" "${PRODUCT_NAME} Project File" + WriteRegStr SHCTX "Software\Classes\${PRODUCT_REGNAME}\DefaultIcon" "" "${PRODUCT_EXE},0" + WriteRegStr SHCTX "Software\Classes\${PRODUCT_REGNAME}\Shell\open\command" "" '"${PRODUCT_EXE}" "%1"' + ; write information about file extensions + WriteRegStr SHCTX "Software\Classes\${PRODUCT_EXT}" "" "${PRODUCT_REGNAME}" + ; refresh shell + System::Call 'shell32.dll::SHChangeNotify(i, i, i, i) (0x08000000, 0, 0, 0)' +SectionEnd + +Section "Plugins" SEC02 + SectionIn 1 + SetOverwrite ifnewer + SetOutPath "$INSTDIR\lib\geany" + File "${RESOURCEDIR}\lib\geany\*.dll" +SectionEnd + +Section "Language Files" SEC03 + SectionIn 1 + SetOutPath "$INSTDIR\share\locale" + File /r "${RESOURCEDIR}\share\locale\*" +!ifdef INCLUDE_GTK + SetOutPath "$INSTDIR\share\locale" + File /r "gtk\share\locale\*" +!endif +SectionEnd + +Section "Documentation" SEC04 + SectionIn 1 + SetOverwrite ifnewer + SetOutPath "$INSTDIR\share\doc" + File /r "${RESOURCEDIR}\share\doc\*" + WriteIniStr "$INSTDIR\Documentation.url" "InternetShortcut" "URL" "$INSTDIR\share\doc\geany\html\index.html" + !insertmacro MUI_STARTMENU_WRITE_BEGIN ${PRODUCT_NAME} + CreateShortCut "$SMPROGRAMS\$StartmenuFolder\Documentation.lnk" "$INSTDIR\Documentation.url" + !insertmacro MUI_STARTMENU_WRITE_END +SectionEnd + +Section "Autocompletion Tags" SEC05 + SectionIn 1 + SetOverwrite ifnewer + SetOutPath "$INSTDIR\data\tags" + File /r "${RESOURCEDIR}\data\tags\*" +SectionEnd + +; Include GTK runtime library but only if desired from command line +!ifdef INCLUDE_GTK +Section "GTK ${GTK_VERSION} Runtime Environment" SEC06 + SectionIn 1 + SetOverwrite ifnewer + SetOutPath "$INSTDIR" + File "gtk\ReadMe.Dependencies.Geany.txt" + SetOutPath "$INSTDIR\bin" + File /r "gtk\bin\*" + SetOutPath "$INSTDIR\etc" + File /r "gtk\etc\*" + SetOutPath "$INSTDIR\lib" + File /r "gtk\lib\*" + SetOutPath "$INSTDIR\share" + File /r "gtk\share\*" +SectionEnd +!endif + +Section "Context Menus" SEC07 + SectionIn 1 + WriteRegStr HKCR "*\shell\OpenWithGeany" "" "Open with Geany" + WriteRegStr HKCR "*\shell\OpenWithGeany" "Icon" "$INSTDIR\bin\geany.exe" + WriteRegStr HKCR "*\shell\OpenWithGeany\command" "" '"$INSTDIR\bin\geany.exe" "%1"' +SectionEnd + +Section "Desktop Shortcuts" SEC08 + SectionIn 1 + CreateShortCut "$DESKTOP\Geany.lnk" "$INSTDIR\bin\Geany.exe" + CreateShortCut "$QUICKLAUNCH\Geany.lnk" "$INSTDIR\bin\Geany.exe" +SectionEnd + +; Development files +Section "Development files" SEC09 + SetOverwrite ifnewer + SetOutPath "$INSTDIR\include" + File /r "${RESOURCEDIR}\include\*" + + SetOutPath "$INSTDIR\lib\pkgconfig" + File "${RESOURCEDIR}\lib\pkgconfig\geany.pc" +SectionEnd + +Section -AdditionalIcons + SetOutPath $INSTDIR + !insertmacro MUI_STARTMENU_WRITE_BEGIN ${PRODUCT_NAME} + WriteIniStr "$INSTDIR\Website.url" "InternetShortcut" "URL" "${PRODUCT_WEB_SITE}" + CreateShortCut "$SMPROGRAMS\$StartmenuFolder\Website.lnk" "$INSTDIR\Website.url" + CreateShortCut "$SMPROGRAMS\$StartmenuFolder\Uninstall.lnk" "$INSTDIR\uninst.exe" + !insertmacro MUI_STARTMENU_WRITE_END +SectionEnd + +Section -Post + WriteUninstaller "$INSTDIR\uninst.exe" + WriteRegStr SHCTX "${PRODUCT_DIR_REGKEY}" Path "$INSTDIR" + WriteRegStr SHCTX "${PRODUCT_UNINST_KEY}" "StartMenu" "$SMPROGRAMS\$StartmenuFolder" + ${if} $Answer == "yes" ; if user is admin + WriteRegStr SHCTX "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)" + WriteRegStr SHCTX "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe" + WriteRegStr SHCTX "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\bin\Geany.exe" + WriteRegStr SHCTX "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}" + WriteRegStr SHCTX "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}" + WriteRegStr SHCTX "${PRODUCT_UNINST_KEY}" "URLUpdateInfo" "${PRODUCT_WEB_SITE}" + WriteRegStr SHCTX "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}" + WriteRegDWORD SHCTX "${PRODUCT_UNINST_KEY}" "NoModify" 0x00000001 + WriteRegDWORD SHCTX "${PRODUCT_UNINST_KEY}" "NoRepair" 0x00000001 + ${endif} +SectionEnd + +Section Uninstall + Delete "$INSTDIR\Website.url" + Delete "$INSTDIR\Documentation.url" + Delete "$INSTDIR\uninst.exe" + Delete "$INSTDIR\News.txt" + Delete "$INSTDIR\ReadMe.txt" + Delete "$INSTDIR\ReadMe.Dependencies.Geany.txt" + Delete "$INSTDIR\Thanks.txt" + Delete "$INSTDIR\ToDo.txt" + Delete "$INSTDIR\Authors.txt" + Delete "$INSTDIR\ChangeLog.txt" + Delete "$INSTDIR\Copying.txt" + Delete "$INSTDIR\Geany.lnk" + + ; delete start menu entry + ReadRegStr $0 SHCTX "${PRODUCT_UNINST_KEY}" "StartMenu" + RMDir /r "$0" + + Delete "$QUICKLAUNCH\Geany.lnk" + Delete "$DESKTOP\Geany.lnk" + + RMDir /r "$INSTDIR\bin" + RMDir /r "$INSTDIR\data" + RMDir /r "$INSTDIR\etc" + RMDir /r "$INSTDIR\include" + RMDir /r "$INSTDIR\lib" + RMDir /r "$INSTDIR\libexec" + RMDir /r "$INSTDIR\share" + RMDir "$INSTDIR" + + ; remove .geany file extension + ReadRegStr $R0 SHCTX "Software\Classes\${PRODUCT_EXT}" "" + ${if} $R0 == "${PRODUCT_REGNAME}" + DeleteRegKey SHCTX "${PRODUCT_EXT}" + DeleteRegKey HKCR "${PRODUCT_EXT}" + DeleteRegKey SHCTX "${PRODUCT_REGNAME}" + DeleteRegKey HKCR "${PRODUCT_REGNAME}" + ${endif} + + DeleteRegKey HKCR "*\shell\OpenWithGeany" + + DeleteRegKey SHCTX "${PRODUCT_UNINST_KEY}" + DeleteRegKey HKCU "${PRODUCT_UNINST_KEY}" + DeleteRegKey SHCTX "${PRODUCT_DIR_REGKEY}" + DeleteRegKey HKCU "${PRODUCT_DIR_REGKEY}" + + SetAutoClose true +SectionEnd + +;;;;;;;;;;;;;;;;;;;;;;;;; +; Section descriptions ; +;;;;;;;;;;;;;;;;;;;;;;;;; +!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN +!insertmacro MUI_DESCRIPTION_TEXT ${SEC01} "Required program files. You cannot skip these files." +!insertmacro MUI_DESCRIPTION_TEXT ${SEC02} "Available plugins like 'Version Diff', 'Class Builder' and 'Insert Special Characters'." +!insertmacro MUI_DESCRIPTION_TEXT ${SEC03} "Various translations of Geany's interface." +!insertmacro MUI_DESCRIPTION_TEXT ${SEC04} "Manual in Text and HTML format." +!insertmacro MUI_DESCRIPTION_TEXT ${SEC05} "Symbol lists necessary for auto completion of symbols." +!ifdef INCLUDE_GTK +!insertmacro MUI_DESCRIPTION_TEXT ${SEC06} "You need these files to run Geany. If you have already installed a GTK Runtime Environment (${GTK_VERSION} or higher), you can skip it." +!endif +!insertmacro MUI_DESCRIPTION_TEXT ${SEC07} "Add context menu item 'Open With Geany'" +!insertmacro MUI_DESCRIPTION_TEXT ${SEC08} "Create shortcuts for Geany on the desktop and in the Quicklaunch Bar" +!insertmacro MUI_DESCRIPTION_TEXT ${SEC09} "You need these files only if you want to develop own plugins for Geany. If unsure, you can skip it." +!insertmacro MUI_FUNCTION_DESCRIPTION_END + +;;;;;;;;;;;;;;;;;;;;; +; helper functions ; +;;;;;;;;;;;;;;;;;;;;; + +; (from http://jabref.svn.sourceforge.net/viewvc/jabref/trunk/jabref/src/windows/nsis/setup.nsi) +!macro IsUserAdmin Result UName + ClearErrors + UserInfo::GetName + IfErrors Win9x + Pop $0 + StrCpy ${UName} $0 + UserInfo::GetAccountType + Pop $1 + ${if} $1 == "Admin" + StrCpy ${Result} "yes" + ${else} + StrCpy ${Result} "no" + ${endif} + Goto done + +Win9x: + StrCpy ${Result} "yes" +done: +!macroend + +Function .onInit + StrCpy "$StartmenuFolder" "Geany" + + ; (from http://jabref.svn.sourceforge.net/viewvc/jabref/trunk/jabref/src/windows/nsis/setup.nsi) + ; If the user does *not* have administrator privileges, abort + StrCpy $Answer "" + StrCpy $UserName "" + !insertmacro IsUserAdmin $Answer $UserName ; macro from LyXUtils.nsh + ${if} $Answer == "yes" + SetShellVarContext all ; set that e.g. shortcuts will be created for all users + ${else} + SetShellVarContext current + ; TODO is this really what we want? $PROGRAMFILES is not much better because + ; probably the unprivileged user can't write it anyways + StrCpy $INSTDIR "$PROFILE\$(^Name)" + ${endif} + + ; prevent running multiple instances of the installer + System::Call 'kernel32::CreateMutexA(i 0, i 0, t "geany_installer") i .r1 ?e' + Pop $R0 + StrCmp $R0 0 +3 + MessageBox MB_OK|MB_ICONEXCLAMATION "The installer is already running." /SD IDOK + Abort + ; warn about a new install over an existing installation + ReadRegStr $R0 SHCTX "${PRODUCT_UNINST_KEY}" "UninstallString" + StrCmp $R0 "" finish + + MessageBox MB_YESNO|MB_ICONEXCLAMATION \ + "Geany has already been installed. $\nDo you want to remove the previous version before installing $(^Name) ?" \ + /SD IDYES IDYES remove IDNO finish + +remove: + ; run the uninstaller + ClearErrors + ; we read the installation path of the old installation from the Registry + ReadRegStr $UNINSTDIR SHCTX "${PRODUCT_DIR_REGKEY}" "Path" + IfSilent dosilent nonsilent +dosilent: + ExecWait '$R0 /S _?=$UNINSTDIR' ;Do not copy the uninstaller to a temp file + Goto finish +nonsilent: + ExecWait '$R0 _?=$UNINSTDIR' ;Do not copy the uninstaller to a temp file +finish: +FunctionEnd + +Function un.onUninstSuccess + HideWindow + MessageBox MB_ICONINFORMATION|MB_OK "$(^Name) was successfully removed from your computer." \ + /SD IDOK +FunctionEnd + +Function un.onInit + ; If the user does *not* have administrator privileges, abort + StrCpy $Answer "" + !insertmacro IsUserAdmin $Answer $UserName + ${if} $Answer == "yes" + SetShellVarContext all + ${else} + ; check if the Geany has been installed with admin permisions + ReadRegStr $0 HKLM "${PRODUCT_UNINST_KEY}" "Publisher" + ${if} $0 != "" + MessageBox MB_OK|MB_ICONSTOP "You need administrator privileges to uninstall Geany!" \ + /SD IDOK + Abort + ${endif} + SetShellVarContext current + ${endif} + + MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "Are you sure you want to completely remove $(^Name) and all of its components?" \ + /SD IDYES IDYES +2 + Abort +FunctionEnd + +Function OnDirLeave + ClearErrors + SetOutPath "$INSTDIR" ; what about IfError creating $INSTDIR? + GetTempFileName $1 "$INSTDIR" ; creates tmp file (or fails) + FileOpen $0 "$1" "w" ; error to open? + FileWriteByte $0 "0" + IfErrors notPossible possible + +notPossible: + RMDir "$INSTDIR" ; removes folder if empty + MessageBox MB_OK "The given directory is not writeable. Please choose another one!" /SD IDOK + Abort +possible: + FileClose $0 + Delete "$1" +FunctionEnd diff --git a/tests/ctags/geany.nsi.tags b/tests/ctags/geany.nsi.tags new file mode 100644 index 00000000..6ab790de --- /dev/null +++ b/tests/ctags/geany.nsi.tags @@ -0,0 +1,21 @@ +# format=tagmanager +!Program FilesÌ256Ö0 +-AdditionalIconsÌ256Ö0 +-PostÌ256Ö0 +.onInitÌ16Ö0 +AnswerÌ16384Ö0 +Autocompletion TagsÌ256Ö0 +Context MenusÌ256Ö0 +Desktop ShortcutsÌ256Ö0 +Development filesÌ256Ö0 +DocumentationÌ256Ö0 +GTK Ì256Ö0 +Language FilesÌ256Ö0 +OnDirLeaveÌ16Ö0 +PluginsÌ256Ö0 +StartmenuFolderÌ16384Ö0 +UNINSTDIRÌ16384Ö0 +UninstallÌ256Ö0 +UserNameÌ16384Ö0 +un.onInitÌ16Ö0 +un.onUninstSuccessÌ16Ö0 diff --git a/tests/ctags/js-class-related-unterminated.js.tags b/tests/ctags/js-class-related-unterminated.js.tags index aef28b1d..fa5f05f6 100644 --- a/tests/ctags/js-class-related-unterminated.js.tags +++ b/tests/ctags/js-class-related-unterminated.js.tags @@ -3,8 +3,9 @@ A BÌ1Í(a, b)ÎClsÖ0 CÌ1Í()ÎClsÖ0 ClsÌ1Ö0 +SubÌ1Ö0 SubÌ1Í()ÎCls.BÖ0 -dyn1Ì128ÎCls.B.SubÖ0 +dyn1Ì128ÎSubÖ0 m1Ì128Í(a)ÎCls.BÖ0 m2Ì128Í(b)ÎCls.BÖ0 m3Ì128Í(c)ÎCls.BÖ0 diff --git a/tests/ctags/js-let.js.tags b/tests/ctags/js-let.js.tags index d1dbf8c7..8252e8f5 100644 --- a/tests/ctags/js-let.js.tags +++ b/tests/ctags/js-let.js.tags @@ -2,7 +2,7 @@ aÌ16384Ö0 bÌ16384Ö0 funcÌ16Í()Ö0 -groupÌ16384Ö0 +groupÌ1Ö0 xÌ64ÎgroupÖ0 yÌ64ÎgroupÖ0 zÌ64ÎgroupÖ0 diff --git a/tests/ctags/js-string-continuation.js.tags b/tests/ctags/js-string-continuation.js.tags index b6d713ef..220e43e1 100644 --- a/tests/ctags/js-string-continuation.js.tags +++ b/tests/ctags/js-string-continuation.js.tags @@ -1,6 +1,6 @@ # format=tagmanager firstÌ128Í()ÎoÖ0 fourthÌ128Í()ÎoÖ0 -oÌ16384Ö0 +oÌ1Ö0 secondÌ128Í()ÎoÖ0 thirdÌ128Í()ÎoÖ0 diff --git a/tests/ctags/jsFunc_tutorial.js.tags b/tests/ctags/jsFunc_tutorial.js.tags index 30d06b26..03cb0a7d 100644 --- a/tests/ctags/jsFunc_tutorial.js.tags +++ b/tests/ctags/jsFunc_tutorial.js.tags @@ -27,7 +27,7 @@ addSalaryFunction addSalaryFunctionDT9Ì1Í(addition)Ö0 ball0Ì16384Ö0 ball1Ì16384Ö0 -ball2Ì16384Ö0 +ball2Ì1Ö0 ball3Ì16384Ö0 ball4Ì16384Ö0 ball5Ì16384Ö0 @@ -59,11 +59,11 @@ myFunction6A myFunction6AEÌ16Í()Ö0 myFunction6BÌ16Í()Ö0 myFunction6EÌ16Í()Ö0 -myObjectÌ16384Ö0 +myObjectÌ1Ö0 my_global_var1Ì16384Ö0 -object1Ì16384Ö0 -object2Ì16384Ö0 -object3Ì16384Ö0 +object1Ì1Ö0 +object2Ì1Ö0 +object3Ì1Ö0 priceÌ128ÎPT2Ö0 savedFunc6BÌ16Í()Ö0 sayName4AÌ16Í(name)Ö0 diff --git a/tests/ctags/simple.abc b/tests/ctags/simple.abc new file mode 100644 index 00000000..639d9d94 --- /dev/null +++ b/tests/ctags/simple.abc @@ -0,0 +1,78 @@ +% A Selection of Scotch, English, Irish and Foreign +% Airs adapted to the Fife, Violin, or German-Flute +% Glasgow +% Printed and Sold by James Aird +% Volume First +% 1778 (often cited as 1782) + +X:0001 +T:The Ranting Highlandman. +T:The White Cockade +M:C| +L:1/8 +Q:1/2=112 +I: :: :: +%% G A B c d e ^f g a +Z:Jack Campin * www.campin.me.uk * 2009 +K:G +AG|B2B2 B2AG|B2B2 B2g2|B2B2 B2AG|AGAB A2 +GA|B2B2 cBAG|A2B2 g2fg|a2gf g2fe|d2B2 B2:: +Bc|d2B2 g2B2|d2d2 d2e2|d2cB g2fg|a2A2 A2 +GA|B2B2 cBAG|A2B2 g2fg|a2gf g2fe|d2B2 B2:| + +X:0002 +T:Quick Step. 25th Regt. +N:bars of quavers are all beamed together in the original +M:2/4 +L:1/16 +Q:1/4=89 +I: :: :: +%% D ^F G A B c d e f ^f g a +Z:Jack Campin * www.campin.me.uk * 2009 +K:G +g2d2 d2c2|(BcdB) G2D2 |G2B2 (ABcd)| B2``G2 G2d2| +e2c2 c2e2| d2B2 B2d2 |g2d2 c2B2 | B4 A4 :| +F2A2 A2dc| B2G2 G4 |B2d2 d2=f2|(ed)(cB) c4 | +e2a2 a2g2|(gfed) g2c2 |B2AG A2D2 | G2``G2 G4 :| + +X:0003 +T:The Lads of the Village. +M:2/4 +L:1/8 +Q:1/4=104 +I: :: :: +%% D ^F G A B c d e ^f g a +Z:Jack Campin * www.campin.me.uk * 2009 +K:G +G2g>d|ecBG|a>cBG|FA`FD|G2 gd|ecBG|A>cBG|(D/G/``F/A/) G2:| +g>fgd|ecBG|g>fge|a>gfd|g>fgd|ecBG|A>cBG|(D/G/)(F/A/) G2:| + +X:0004 +T:I'll Touzle your Kurchy. +M:6/8 +L:1/8 +Q:3/8=120 +I: :: :: +%% D E ^F G A B c d e ^f g +Z:Jack Campin * www.campin.me.uk * 2009 +K:E Minor +B |E>GE GEG|B>AB e2f|g>fe dgB|A>GA BG +E |E>GE GEG|B>AB e2f|g>fe dcB|AGF E2:| +B |E>GE B2B|GEG B2B|E>GE A2G|FDF A2 +G/F/|E>GE B2B|GEG e2f|gfe dcB|AGF E2:| + +X:0005 +T:The Lady's play thing, or Gen Howe's March. +M:6/8 +L:1/8 +Q:3/8=120 +I: :: :: +%% D G A B c d e g +N:last note printed as G3 +Z:Jack Campin * www.campin.me.uk * 2009 +K:G +d/c/|B2B Bcd|A2A A2d|G2G GAB|B3 A2 +e |dgd BdB|GBG Ddc|BcB AGA|G2G G2:| +d |dBd dBd|e2e e2c|cAc cAc|d2d d2 +c |BGB BGB|c2c cBA|Bcd dcB|B3 A2 +e |dgd BdB|GBG Ddc|BcB AGA|G2G G2:| diff --git a/tests/ctags/simple.abc.tags b/tests/ctags/simple.abc.tags new file mode 100644 index 00000000..a08a95ac --- /dev/null +++ b/tests/ctags/simple.abc.tags @@ -0,0 +1,7 @@ +# format=tagmanager +X:0001 / T:The Ranting Highlandman.Ì64Ö0 +X:0001 / T:The Ranting Highlandman. / T:The White CockadeÌ64Ö0 +X:0002 / T:Quick Step. 25th Regt.Ì64Ö0 +X:0003 / T:The Lads of the Village.Ì64Ö0 +X:0004 / T:I'll Touzle your Kurchy.Ì64Ö0 +X:0005 / T:The Lady's play thing, or Gen Howe's March.Ì64Ö0 diff --git a/tests/ctags/simple.asciidoc b/tests/ctags/simple.asciidoc new file mode 100644 index 00000000..4e2ce295 --- /dev/null +++ b/tests/ctags/simple.asciidoc @@ -0,0 +1,59 @@ += Chapter 1 (Level 0) + +Intro text of chapter 1 + +== Section 1.1 + +Text of section 1.1 + +=== Subsection 1.1.1 + +Text of subsection 1.1.1 + +==== Level 3 Section 1.1.1.1 Title + +Text of subsection 1.1.1.1 + +===== Level 4 Section 1.1.1.1.1 Title + +Text of subsection 1.1.1.1.1 + +====== Level 5 Section 1.1.1.1.1.1 Title + +Text of subsection 1.1.1.1.1.1 + +== Section 1.2 + +Text of section 1.2 + +// and now two-line titles: + +Chapter 2 +========= + +Intro text of chapter 2 + +Section 2.1 +------------ + +Text of section 2.1 + +Subsection 2.1.1 +~~~~~~~~~~~~~~~ + +Level 3 Section 2.1.1.1 Title +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Text of subsection 2.1.1.1 + +Level 4 Section 2.1.1.1.1 Title ++++++++++++++++++++++++++++++++ + +Text of subsection 2.1.1.1.1 + + +Section 2.2 +----------- + +Text of section 2.2 + diff --git a/tests/ctags/simple.asciidoc.tags b/tests/ctags/simple.asciidoc.tags new file mode 100644 index 00000000..bee2f65e --- /dev/null +++ b/tests/ctags/simple.asciidoc.tags @@ -0,0 +1,13 @@ +# format=tagmanager +Chapter 1 (Level 0)Ì256Ö0 +Chapter 2Ì256Ö0 +Level 3 Section 1.1.1.1 TitleÌ16384ÎSubsection 1.1.1Ö0 +Level 3 Section 2.1.1.1 TitleÌ16384ÎSubsection 2.1.1Ö0 +Level 4 Section 1.1.1.1.1 TitleÌ2048ÎLevel 3 Section 1.1.1.1 TitleÖ0 +Level 4 Section 2.1.1.1.1 TitleÌ2048ÎLevel 3 Section 2.1.1.1 TitleÖ0 +Section 1.1Ì64ÎChapter 1 (Level 0)Ö0 +Section 1.2Ì64ÎChapter 1 (Level 0)Ö0 +Section 2.1Ì64ÎChapter 2Ö0 +Section 2.2Ì64ÎChapter 2Ö0 +Subsection 1.1.1Ì65536ÎSection 1.1Ö0 +Subsection 2.1.1Ì65536ÎSection 2.1Ö0 diff --git a/tests/ctags/simple.conf b/tests/ctags/simple.conf new file mode 100644 index 00000000..cbb643f2 --- /dev/null +++ b/tests/ctags/simple.conf @@ -0,0 +1,33 @@ +# +# TAKEN FROM https://docs.python.org/2/library/logging.config.html +# + +[loggers] +keys=root,parser + +[handlers] +keys=hand01 + +[formatters] +keys=form01 + +[logger_root] +level=NOTSET +handlers=hand01 + +[logger_parser] +level=DEBUG +handlers=hand01 +propagate=1 +qualname=compiler.parser + +[handler_hand01] +class=StreamHandler +level=NOTSET +formatter=form01 +args=(sys.stdout,) + +[formatter_form01] +format=F1 %(asctime)s %(levelname)s %(message)s +datefmt= +class=logging.Formatter diff --git a/tests/ctags/simple.conf.tags b/tests/ctags/simple.conf.tags new file mode 100644 index 00000000..8cdbee7d --- /dev/null +++ b/tests/ctags/simple.conf.tags @@ -0,0 +1,24 @@ +# format=tagmanager +argsÌ65536Îhandler_hand01Ö0 +classÌ65536Îformatter_form01Ö0 +classÌ65536Îhandler_hand01Ö0 +datefmtÌ65536Îformatter_form01Ö0 +formatÌ65536Îformatter_form01Ö0 +formatterÌ65536Îhandler_hand01Ö0 +formatter_form01Ì256Ö0 +formattersÌ256Ö0 +handler_hand01Ì256Ö0 +handlersÌ256Ö0 +handlersÌ65536Îlogger_parserÖ0 +handlersÌ65536Îlogger_rootÖ0 +keysÌ65536ÎformattersÖ0 +keysÌ65536ÎhandlersÖ0 +keysÌ65536ÎloggersÖ0 +levelÌ65536Îhandler_hand01Ö0 +levelÌ65536Îlogger_parserÖ0 +levelÌ65536Îlogger_rootÖ0 +logger_parserÌ256Ö0 +logger_rootÌ256Ö0 +loggersÌ256Ö0 +propagateÌ65536Îlogger_parserÖ0 +qualnameÌ65536Îlogger_parserÖ0 diff --git a/tests/ctags/simple.diff b/tests/ctags/simple.diff new file mode 100644 index 00000000..78e45d23 --- /dev/null +++ b/tests/ctags/simple.diff @@ -0,0 +1,170 @@ +diff --git a/main/parsers.h b/main/parsers.h +index 8000fa0..5332052 100644 +--- a/main/parsers.h ++++ b/main/parsers.h +@@ -28,6 +28,7 @@ + CsharpParser, \ + CobolParser, \ + DParser, \ ++ DiffParser, \ + DosBatchParser, \ + EiffelParser, \ + ErlangParser, \ +diff --git a/parsers/diff.c b/parsers/diff.c +new file mode 100644 +index 0000000..a1bf5f3 +--- /dev/null ++++ b/parsers/diff.c +@@ -0,0 +1,134 @@ ++/* ++* ++* Copyright (c) 2000-2001, Darren Hiebert ++* ++* This source code is released for free distribution under the terms of the ++* GNU General Public License. ++* ++* This module contains functions for generating tags for diff files (based on Sh parser). ++*/ ++ ++/* ++* INCLUDE FILES ++*/ ++#include "general.h" /* must always come first */ ++ ++#include ++#include ++ ++#include "parse.h" ++#include "read.h" ++#include "vstring.h" ++ ++/* ++* DATA DEFINITIONS ++*/ ++typedef enum { ++ K_FUNCTION ++} diffKind; ++ ++static kindOption DiffKinds [] = { ++ { TRUE, 'f', "compared file", "compared files"} ++}; ++ ++enum { ++ DIFF_DELIM_MINUS = 0, ++ DIFF_DELIM_PLUS ++}; ++ ++static const char *DiffDelims[2] = { ++ "--- ", ++ "+++ " ++}; ++ ++/* ++* FUNCTION DEFINITIONS ++*/ ++ ++static const unsigned char *stripAbsolute (const unsigned char *filename) ++{ ++ const unsigned char *tmp; ++ ++ /* strip any absolute path */ ++ if (*filename == '/' || *filename == '\\') ++ { ++ boolean skipSlash = TRUE; ++ ++ tmp = (const unsigned char*) strrchr ((const char*) filename, '/'); ++ if (tmp == NULL) ++ { /* if no / is contained try \ in case of a Windows filename */ ++ tmp = (const unsigned char*) strrchr ((const char*) filename, '\\'); ++ if (tmp == NULL) ++ { /* last fallback, probably the filename doesn't contain a path, so take it */ ++ tmp = filename; ++ skipSlash = FALSE; ++ } ++ } ++ ++ /* skip the leading slash or backslash */ ++ if (skipSlash) ++ tmp++; ++ } ++ else ++ tmp = filename; ++ ++ return tmp; ++} ++ ++static void findDiffTags (void) ++{ ++ vString *filename = vStringNew (); ++ const unsigned char *line, *tmp; ++ int delim = DIFF_DELIM_MINUS; ++ ++ while ((line = fileReadLine ()) != NULL) ++ { ++ const unsigned char* cp = line; ++ ++ if (strncmp ((const char*) cp, DiffDelims[delim], 4u) == 0) ++ { ++ cp += 4; ++ if (isspace ((int) *cp)) continue; ++ /* when original filename is /dev/null use the new one instead */ ++ if (delim == DIFF_DELIM_MINUS && ++ strncmp ((const char*) cp, "/dev/null", 9u) == 0 && ++ (cp[9] == 0 || isspace (cp[9]))) ++ { ++ delim = DIFF_DELIM_PLUS; ++ continue; ++ } ++ ++ tmp = stripAbsolute (cp); ++ ++ if (tmp != NULL) ++ { ++ while (! isspace(*tmp) && *tmp != '\0') ++ { ++ vStringPut(filename, *tmp); ++ tmp++; ++ } ++ ++ vStringTerminate(filename); ++ makeSimpleTag (filename, DiffKinds, K_FUNCTION); ++ vStringClear (filename); ++ } ++ ++ /* restore default delim */ ++ delim = DIFF_DELIM_MINUS; ++ } ++ } ++ vStringDelete (filename); ++} ++ ++extern parserDefinition* DiffParser (void) ++{ ++ static const char *const extensions [] = { "diff", "patch", NULL }; ++ parserDefinition* const def = parserNew ("Diff"); ++ def->kinds = DiffKinds; ++ def->kindCount = KIND_COUNT (DiffKinds); ++ def->extensions = extensions; ++ def->parser = findDiffTags; ++ return def; ++} ++ ++/* vi:set tabstop=8 shiftwidth=4: */ +diff --git a/source.mak b/source.mak +index 2550028..eaa9154 100644 +--- a/source.mak ++++ b/source.mak +@@ -44,6 +44,7 @@ PARSER_SOURCES = \ + $(PARSER_DIR)/clojure.c \ + $(PARSER_DIR)/css.c \ + $(PARSER_DIR)/cobol.c \ ++ $(PARSER_DIR)/diff.c \ + $(PARSER_DIR)/dosbatch.c \ + $(PARSER_DIR)/eiffel.c \ + $(PARSER_DIR)/erlang.c \ + +--- a/Units/review-needed.r/simple.ksh.t/expected.tags ++++ /dev/null +@@ -1,2 +0,0 @@ +-f1 input.ksh /^f1() {$/;" f +-f2 input.ksh /^function f2 {$/;" f diff --git a/tests/ctags/simple.diff.tags b/tests/ctags/simple.diff.tags new file mode 100644 index 00000000..0117acfa --- /dev/null +++ b/tests/ctags/simple.diff.tags @@ -0,0 +1,5 @@ +# format=tagmanager +a/Units/review-needed.r/simple.ksh.t/expected.tagsÌ16Ö0 +a/main/parsers.hÌ16Ö0 +a/source.makÌ16Ö0 +b/parsers/diff.cÌ16Ö0 diff --git a/tests/ctags/simple.docbook b/tests/ctags/simple.docbook new file mode 100644 index 00000000..9f3fff3a --- /dev/null +++ b/tests/ctags/simple.docbook @@ -0,0 +1,45 @@ +
+ + My first docbook document + + Seth + Kenlon + + opensource.com + 2017 + + + Required Chapter + + At least one chapter, reference, part, or article is required in a book. + + + +
+ Introduction + Introductory text goes here. +
+ + + Section with a title + Main body text goes here. + + + + Section with a title + Main body text goes here. + + + + Section with a title + Main body text goes here. + + +
+ Conclusion + Exciting and inspiring conclusion goes here. +
+ + Demonstration Appendix + +
\ No newline at end of file diff --git a/tests/ctags/simple.docbook.tags b/tests/ctags/simple.docbook.tags new file mode 100644 index 00000000..d4eab6a7 --- /dev/null +++ b/tests/ctags/simple.docbook.tags @@ -0,0 +1,7 @@ +# format=tagmanager +chapter1Ì16Ö0 +conclusionÌ1Ö0 +introÌ1Ö0 +sect1Ì64Ö0 +sect2Ì65536Ö0 +sect3Ì16384Ö0 diff --git a/tests/ctags/simple.hs b/tests/ctags/simple.hs new file mode 100644 index 00000000..065b2d08 --- /dev/null +++ b/tests/ctags/simple.hs @@ -0,0 +1,8 @@ +module Cards where + +data Int = -2147483648 | -2147483647 | ... | -1 | 0 | 1 | 2 | ... | 2147483647 +newtype Fd = Fd CInt +data Bool = True | False + +add :: Integer -> Integer -> Integer +add x y = x + y diff --git a/tests/ctags/simple.hs.tags b/tests/ctags/simple.hs.tags new file mode 100644 index 00000000..6ecd5d7f --- /dev/null +++ b/tests/ctags/simple.hs.tags @@ -0,0 +1,9 @@ +# format=tagmanager +BoolÌ4096Ö0 +CardsÌ256Ö0 +FalseÌ65536Ö0 +FdÌ4096Ö0 +FdÌ65536Ö0 +IntÌ4096Ö0 +TrueÌ65536Ö0 +addÌ16Ö0 diff --git a/tests/ctags/simple.html.tags b/tests/ctags/simple.html.tags index ce07d070..38856d09 100644 --- a/tests/ctags/simple.html.tags +++ b/tests/ctags/simple.html.tags @@ -1,7 +1,5 @@ # format=tagmanager -commented_anchorÌ64Ö0 -commented_function2Ì16Ö0 -messageÌ16Ö0 +messageÌ16Í()Ö0 postfixed_anchorÌ64Ö0 prefixed_anchorÌ64Ö0 simple_anchorÌ64Ö0 diff --git a/tests/ctags/simple.hx b/tests/ctags/simple.hx new file mode 100644 index 00000000..3a9e99e4 --- /dev/null +++ b/tests/ctags/simple.hx @@ -0,0 +1,23 @@ +enum Color { + Red; + Green; + Blue; + Rgb(r:Int, g:Int, b:Int); +} + +interface Printable { + public function toString():String; +} + +typedef User = { + var age : Int; + var name : String; +} + +class Main { + static var member:String = "bar"; + + static public function main():Void { + trace("Hello World"); + } +} \ No newline at end of file diff --git a/tests/ctags/simple.hx.tags b/tests/ctags/simple.hx.tags new file mode 100644 index 00000000..bb2b1f8f --- /dev/null +++ b/tests/ctags/simple.hx.tags @@ -0,0 +1,10 @@ +# format=tagmanager +ColorÌ2Ö0 +MainÌ1Ö0 +PrintableÌ32Ö0 +UserÌ4096Ö0 +ageÌ16384Ö0 +mainÌ128Ö0 +memberÌ16384Ö0 +nameÌ16384Ö0 +toStringÌ128Ö0 diff --git a/tests/ctags/simple.inp b/tests/ctags/simple.inp new file mode 100644 index 00000000..2e0819a9 --- /dev/null +++ b/tests/ctags/simple.inp @@ -0,0 +1,106 @@ +** +** Simple input file demonstrating user material in ABAQUS +** +** This input file was created with ABAQUS/CAE, and then edited +** to insert the additional commands associated with user material. +** +** You can run the code with +** abaqus job=user_element, user=Usermat.for +** +*Heading +** Job name: Job-1 Model name: Model-1 +*Preprint, echo=NO, model=NO, history=NO, contact=NO +** +** PARTS +** +*Part, name=Part-1 +*End Part +** +** +** ASSEMBLY +** +*Assembly, name=Assembly +** +*Instance, name=Part-1-1, part=Part-1 +*Node + 1, 0., 0. + 2, 2.5, 0. + 3, 0., 2.5 + 4, 2.5, 2.5 +** +** NOTE - if you use reduced integration elements +** with a UMAT, you have to define the hourglass +** stiffness for the element +** +*Element, type=CPE4R +1, 1, 2, 4, 3 +*Nset, nset=_PickedSet2, internal, generate + 1, 4, 1 +*Elset, elset=_PickedSet2, internal + 1, +** Region: (Section-1:Picked) +*Elset, elset=_PickedSet2, internal + 1, +** Section: Section-1 +** THE LINE BELOW WAS EDITED TO ASSIGN THE USER MATERIAL +*Solid Section, elset=_PickedSet2, material=usermat_elastic +1., +*hourglass stiffness +0.1 +*End Instance +** +*Nset, nset=_PickedSet4, internal, instance=Part-1-1 + 1, 2 +*Elset, elset=_PickedSet4, internal, instance=Part-1-1 + 1, +*Nset, nset=_PickedSet5, internal, instance=Part-1-1 + 1, 3 +*Elset, elset=_PickedSet5, internal, instance=Part-1-1 + 1, +*Nset, nset=_PickedSet6, internal, instance=Part-1-1 + 3, 4 +*Elset, elset=_PickedSet6, internal, instance=Part-1-1 + 1, +*End Assembly +** +** MATERIALS +** +** THE LINES BELOW WERE EDITED TO DEFINE THE USER MATERIAL +*Material, name=usermat_elastic +*user material, constants=2, type=mechanical + 1000.0, 0.3 +** This defines the number of state variables (none in this case) +*DEPVAR +1 +** ---------------------------------------------------------------- +** +** STEP: Step-1 +** +*Step, name=Step-1 +*Static +0.1, 1., 1e-05, 1. +** +** BOUNDARY CONDITIONS +** +** Name: BC-1 Type: Displacement/Rotation +*Boundary +_PickedSet4, 2, 2 +** Name: BC-2 Type: Displacement/Rotation +*Boundary +_PickedSet5, 1, 1 +** Name: BC-3 Type: Displacement/Rotation +*Boundary +_PickedSet6, 2, 2, 0.1 +** +** OUTPUT REQUESTS +** +*Restart, write, frequency=0 +** +** FIELD OUTPUT: F-Output-1 +** +*Output, field, variable=PRESELECT +** +** HISTORY OUTPUT: H-Output-1 +** +*Output, history, variable=PRESELECT +*End Step \ No newline at end of file diff --git a/tests/ctags/simple.inp.tags b/tests/ctags/simple.inp.tags new file mode 100644 index 00000000..90b65882 --- /dev/null +++ b/tests/ctags/simple.inp.tags @@ -0,0 +1,4 @@ +# format=tagmanager +AssemblyÌ64Ö0 +Part-1Ì1Ö0 +Step-1Ì32Ö0 diff --git a/tests/ctags/simple.js.tags b/tests/ctags/simple.js.tags index 17bc136d..80330f40 100644 --- a/tests/ctags/simple.js.tags +++ b/tests/ctags/simple.js.tags @@ -11,8 +11,9 @@ invalidInnerFunction my_global_var1Ì16384Ö0 my_global_var2Ì16384Ö0 my_global_var3Ì16384Ö0 -my_global_var4Ì16Í()Ö0 my_global_var4Ì16384Ö0 +onchangeÌ16Í()Îmy_global_var4Ö0 +originalvalueÌ16384Ö0 validFunctionFiveÌ16Í(a,b)ÎtestlibÖ0 validFunctionFourÌ16Í(a,b)ÎextraÖ0 validFunctionOneÌ16Í(a,b)Ö0 diff --git a/tests/ctags/simple.md b/tests/ctags/simple.md new file mode 100644 index 00000000..5ea8a056 --- /dev/null +++ b/tests/ctags/simple.md @@ -0,0 +1,51 @@ +# a + +## b + +### c + +#### d + +##### e + +###### f + +# g # +# h ## + +## i # +## j ## +## k ### + +### l # +### m ## +### n ### +### o ### + +#### p # +#### q ##### + +##### r # +##### s ###### + +###### t # +###### u ####### + +A += + +B +== + +C +=== + +D +- + +E +-- + +F +--- + diff --git a/tests/ctags/simple.md.tags b/tests/ctags/simple.md.tags new file mode 100644 index 00000000..e7d7aa99 --- /dev/null +++ b/tests/ctags/simple.md.tags @@ -0,0 +1,28 @@ +# format=tagmanager +# aÌ16384Ö0 +# g #Ì16384Ö0 +# h ##Ì16384Ö0 +## bÌ16384Ö0 +## i #Ì16384Ö0 +## j ##Ì16384Ö0 +## k ###Ì16384Ö0 +### cÌ16384Ö0 +### l #Ì16384Ö0 +### m ##Ì16384Ö0 +### n ###Ì16384Ö0 +### o ###Ì16384Ö0 +#### dÌ16384Ö0 +#### p #Ì16384Ö0 +#### q #####Ì16384Ö0 +##### eÌ16384Ö0 +##### r #Ì16384Ö0 +##### s ######Ì16384Ö0 +###### fÌ16384Ö0 +###### t #Ì16384Ö0 +###### u #######Ì16384Ö0 +AÌ16384Ö0 +BÌ16384Ö0 +CÌ16384Ö0 +DÌ16384Ö0 +EÌ16384Ö0 +FÌ16384Ö0 diff --git a/tests/ctags/simple.rst b/tests/ctags/simple.rst new file mode 100644 index 00000000..b5e4f527 --- /dev/null +++ b/tests/ctags/simple.rst @@ -0,0 +1,39 @@ +Chapter 1 +========= + +Intro text of chapter 1 + +Section 1.1 +----------- + +Text of section 1.1 + +Subsection 1.1.1 +**************** + +Text of subsection 1.1.1 + +Subsubsection 1.1.1.1 +~~~~~~~~~~~~~~~~~~~~~ + +Text of subsubsection 1.1.1.1 + +Section 1.2 +----------- + +Text of section 1.2 + +Chapter 2 +========= + +Intro text of chapter 2 + +Section 2.1 +----------- + +Text of section 2.1 + +Section 2.2 +----------- + +Text of section 2.2 diff --git a/tests/ctags/simple.rst.tags b/tests/ctags/simple.rst.tags new file mode 100644 index 00000000..a7924bb6 --- /dev/null +++ b/tests/ctags/simple.rst.tags @@ -0,0 +1,9 @@ +# format=tagmanager +Chapter 1Ì256Ö0 +Chapter 2Ì256Ö0 +Section 1.1Ì64ÎChapter 1Ö0 +Section 1.2Ì64ÎChapter 1Ö0 +Section 2.1Ì64ÎChapter 2Ö0 +Section 2.2Ì64ÎChapter 2Ö0 +Subsection 1.1.1Ì65536ÎSection 1.1Ö0 +Subsubsection 1.1.1.1Ì16384ÎSubsection 1.1.1Ö0 diff --git a/tests/ctags/simple.vala b/tests/ctags/simple.vala new file mode 100644 index 00000000..e25dcb34 --- /dev/null +++ b/tests/ctags/simple.vala @@ -0,0 +1,46 @@ +class Demo.HelloWorld : GLib.Object { + + /* atomic types */ + unichar c = 'u'; + float percentile = 0.75f; + const double MU_BOHR = 927.400915E-26; + bool the_box_has_crashed = false; + + /* defining a struct */ + struct Vector { + public double x; + public double y; + public double z; + } + + /* defining an enum */ + enum WindowType { + TOPLEVEL, + POPUP + } + + public signal void sig_1(int a); + + private int _age = 32; // underscore prefix to avoid name clash with property + + /* Property */ + public int age { + get { return _age; } + set { _age = value; } + } + + public static int main(string[] args) { + stdout.printf("Hello, World\n"); + + return 0; + } +} + +public interface ITest : GLib.Object { + public abstract int data_1 { get; set; } + public abstract void method_1(); +} + +namespace NameSpaceName { + class Foo {} +} \ No newline at end of file diff --git a/tests/ctags/simple.vala.tags b/tests/ctags/simple.vala.tags new file mode 100644 index 00000000..7ea06473 --- /dev/null +++ b/tests/ctags/simple.vala.tags @@ -0,0 +1,22 @@ +# format=tagmanager +Demo.HelloWorldÌ1Ö0 +FooÌ1ÎNameSpaceNameÖ0 +ITestÌ32Ö0 +MU_BOHRÌ8ÎDemo.HelloWorldÖ0Ïconst double +NameSpaceNameÌ256Ö0 +POPUPÌ4ÎDemo.HelloWorld.WindowTypeÖ0 +TOPLEVELÌ4ÎDemo.HelloWorld.WindowTypeÖ0 +VectorÌ2048ÎDemo.HelloWorldÖ0 +WindowTypeÌ2ÎDemo.HelloWorldÖ0 +_ageÌ8ÎDemo.HelloWorldÖ0Ïint +ageÌ8ÎDemo.HelloWorldÖ0Ïint +cÌ8ÎDemo.HelloWorldÖ0Ïunichar +data_1Ì8ÎITestÖ0Ïabstract int +mainÌ128Í(string[] args)ÎDemo.HelloWorldÖ0Ïpublic int +method_1Ì128Í()ÎITestÖ0Ïpublic abstract void +percentileÌ8ÎDemo.HelloWorldÖ0Ïfloat +sig_1Ì128Í(int a)ÎDemo.HelloWorldÖ0Ïpublic signal void +the_box_has_crashedÌ8ÎDemo.HelloWorldÖ0Ïbool +xÌ8ÎDemo.HelloWorld.VectorÖ0Ïdouble +yÌ8ÎDemo.HelloWorld.VectorÖ0Ïdouble +zÌ8ÎDemo.HelloWorld.VectorÖ0Ïdouble