From 9353c8352248f15c26db0869da0bbf796a894b6a Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Tue, 31 Mar 2020 14:59:13 +0900 Subject: [PATCH 1/8] main,refactor: rename getParserField to getParserFieldForIndex In the future we will introduce a function to get a parser field with a field type as key. That will be exported to parsers. This change is for avoiding the confliction in the future. Signed-off-by: Masatake YAMATO --- main/entry.c | 4 ++-- main/entry_p.h | 2 +- main/field.c | 4 ++-- main/fmt.c | 2 +- main/writer-ctags.c | 4 ++-- main/writer-json.c | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/main/entry.c b/main/entry.c index fa58ef79d0..7c79081ffd 100644 --- a/main/entry.c +++ b/main/entry.c @@ -1004,7 +1004,7 @@ extern void attachParserFieldToCorkEntry (int index, attachParserField (tag, true, ftype, value); } -extern const tagField* getParserField (const tagEntryInfo * tag, int index) +extern const tagField* getParserFieldForIndex (const tagEntryInfo * tag, int index) { if (index < 0 || tag->usedParserFields <= ((unsigned int)index) ) @@ -1025,7 +1025,7 @@ static void copyParserFields (const tagEntryInfo *const tag, tagEntryInfo* slot) for (i = 0; i < tag->usedParserFields; i++) { - const tagField *f = getParserField (tag, i); + const tagField *f = getParserFieldForIndex (tag, i); Assert(f); value = f->value; diff --git a/main/entry_p.h b/main/entry_p.h index f5cd2c7061..9028fa81ba 100644 --- a/main/entry_p.h +++ b/main/entry_p.h @@ -63,7 +63,7 @@ void uncorkTagFile(void); extern void makeFileTag (const char *const fileName); -extern const tagField* getParserField (const tagEntryInfo * tag, int index); +extern const tagField* getParserFieldForIndex (const tagEntryInfo * tag, int index); CTAGS_INLINE roleBitsType makeRoleBit(int roleIndex) diff --git a/main/field.c b/main/field.c index 1f17f5f6c5..ac727ba1b5 100644 --- a/main/field.c +++ b/main/field.c @@ -561,7 +561,7 @@ static const char* renderFieldCommon (fieldType type, if (index >= 0) { - const tagField *f = getParserField (tag, index); + const tagField *f = getParserFieldForIndex (tag, index); value = f->value; } @@ -612,7 +612,7 @@ extern bool doesFieldHaveTabOrNewlineChar (fieldType type, const tagEntryInfo * if (index >= 0) { - const tagField *f = getParserField (tag, index); + const tagField *f = getParserFieldForIndex (tag, index); value = f->value; } diff --git a/main/fmt.c b/main/fmt.c index 9163f8e970..f1d6384e21 100644 --- a/main/fmt.c +++ b/main/fmt.c @@ -69,7 +69,7 @@ static int printTagField (fmtSpec* fspec, MIO* fp, const tagEntryInfo * tag) for (findex = 0; findex < tag->usedParserFields; findex++) { - f = getParserField(tag, findex); + f = getParserFieldForIndex(tag, findex); if (isParserFieldCompatibleWithFtype (f, ftype)) break; } diff --git a/main/writer-ctags.c b/main/writer-ctags.c index 143f17e451..276ef04721 100644 --- a/main/writer-ctags.c +++ b/main/writer-ctags.c @@ -153,7 +153,7 @@ static bool hasTagEntryTabOrNewlineChar (const tagEntryInfo * const tag) for (unsigned int i = 0; i < tag->usedParserFields; i++) { - const tagField *f = getParserField(tag, i); + const tagField *f = getParserFieldForIndex(tag, i); fieldType ftype = f->ftype; if (isFieldEnabled (ftype)) { @@ -203,7 +203,7 @@ static int addParserFields (tagWriter *writer, MIO * mio, const tagEntryInfo *co for (i = 0; i < tag->usedParserFields; i++) { - const tagField *f = getParserField(tag, i); + const tagField *f = getParserFieldForIndex(tag, i); fieldType ftype = f->ftype; if (! isFieldEnabled (ftype)) continue; diff --git a/main/writer-json.c b/main/writer-json.c index cc72cd57af..5deb38fca6 100644 --- a/main/writer-json.c +++ b/main/writer-json.c @@ -130,7 +130,7 @@ static void addParserFields (json_t *response, const tagEntryInfo *const tag) for (i = 0; i < tag->usedParserFields; i++) { - const tagField *f = getParserField(tag, i); + const tagField *f = getParserFieldForIndex(tag, i); fieldType ftype = f->ftype; if (! isFieldEnabled (ftype)) continue; From 49c6e400f614944d6febb59350badfeb63d50d3c Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Tue, 31 Mar 2020 15:08:35 +0900 Subject: [PATCH 2/8] main: clear the userData field of NestingLevel Signed-off-by: Masatake YAMATO --- main/nestlevel.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/main/nestlevel.c b/main/nestlevel.c index d762179811..32a284d98d 100644 --- a/main/nestlevel.c +++ b/main/nestlevel.c @@ -18,6 +18,8 @@ #include "routines.h" #include "nestlevel.h" +#include + /* TODO: Alignment */ #define NL_SIZE(nls) (sizeof(NestingLevel) + (nls)->userDataSize) #define NL_NTH(nls,n) (NestingLevel *)(((char *)((nls)->levels)) + ((n) * NL_SIZE (nls))) @@ -61,6 +63,9 @@ extern NestingLevel * nestingLevelsPush(NestingLevels *nls, int corkIndex) nls->n++; nl->corkIndex = corkIndex; + if (nls->userDataSize > 0) + memset (nl->userData, 0, nls->userDataSize); + return nl; } From 526f5c1fe9995fbdc9bd69bfe4c29af881009bc8 Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Tue, 31 Mar 2020 15:32:47 +0900 Subject: [PATCH 3/8] main: add a hook for deleting user data of nesting level Signed-off-by: Masatake YAMATO --- main/nestlevel.c | 15 +++++++++++++-- main/nestlevel.h | 3 +++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/main/nestlevel.c b/main/nestlevel.c index 32a284d98d..9bc4d70165 100644 --- a/main/nestlevel.c +++ b/main/nestlevel.c @@ -28,21 +28,30 @@ * FUNCTION DEFINITIONS */ -extern NestingLevels *nestingLevelsNew(size_t userDataSize) +extern NestingLevels *nestingLevelsNewFull(size_t userDataSize, + void (* deleteUserData)(NestingLevel *)) { NestingLevels *nls = xCalloc (1, NestingLevels); nls->userDataSize = userDataSize; + nls->deleteUserData = deleteUserData; return nls; } +extern NestingLevels *nestingLevelsNew(size_t userDataSize) +{ + return nestingLevelsNewFull (userDataSize, NULL); +} + extern void nestingLevelsFree(NestingLevels *nls) { int i; NestingLevel *nl; - for (i = 0; i < nls->allocated; i++) + for (i = 0; i < nls->n; i++) { nl = NL_NTH(nls, i); + if (nls->deleteUserData) + nls->deleteUserData (nl); nl->corkIndex = CORK_NIL; } if (nls->levels) eFree(nls->levels); @@ -85,6 +94,8 @@ extern void nestingLevelsPop(NestingLevels *nls) NestingLevel *nl = nestingLevelsGetCurrent(nls); Assert (nl != NULL); + if (nls->deleteUserData) + nls->deleteUserData (nl); nl->corkIndex = CORK_NIL; nls->n--; } diff --git a/main/nestlevel.h b/main/nestlevel.h index a209ae706c..c35fd12094 100644 --- a/main/nestlevel.h +++ b/main/nestlevel.h @@ -35,12 +35,15 @@ struct NestingLevels int n; /* number of levels in use */ int allocated; size_t userDataSize; + void (* deleteUserData) (NestingLevel *); }; /* * FUNCTION PROTOTYPES */ extern NestingLevels *nestingLevelsNew(size_t userDataSize); +extern NestingLevels *nestingLevelsNewFull(size_t userDataSize, + void (* deleteUserData)(NestingLevel *)); extern void nestingLevelsFree(NestingLevels *nls); extern NestingLevel *nestingLevelsPush(NestingLevels *nls, int corkIndex); extern NestingLevel * nestingLevelsTruncate(NestingLevels *nls, int depth, int corkIndex); From c4711e83446a22045af124b0a7cd9215facb9a88 Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Tue, 31 Mar 2020 13:29:31 +0900 Subject: [PATCH 4/8] Ruby: define a parser specific field for recording mixin --- Tmain/list-fields-with-prefix.d/stdout-expected.txt | 1 + Tmain/list-fields.d/stdout-expected.txt | 1 + parsers/ruby.c | 12 ++++++++++++ 3 files changed, 14 insertions(+) diff --git a/Tmain/list-fields-with-prefix.d/stdout-expected.txt b/Tmain/list-fields-with-prefix.d/stdout-expected.txt index 6047415233..6133d5e644 100644 --- a/Tmain/list-fields-with-prefix.d/stdout-expected.txt +++ b/Tmain/list-fields-with-prefix.d/stdout-expected.txt @@ -31,3 +31,4 @@ x UCTAGSxpath no NONE s-- no xpath for the - UCTAGSshell yes Passwd s-- no login shell - UCTAGSdecorators no Python s-- no decorators on functions and classes - UCTAGSsectionMarker no ReStructuredText s-- no character used for declaring section +- UCTAGSmixin yes Ruby s-- no how the class or module is mixed in (mixin:HOW:MODULE) diff --git a/Tmain/list-fields.d/stdout-expected.txt b/Tmain/list-fields.d/stdout-expected.txt index fd252d028b..a90ab74ae8 100644 --- a/Tmain/list-fields.d/stdout-expected.txt +++ b/Tmain/list-fields.d/stdout-expected.txt @@ -49,6 +49,7 @@ z kind no NONE s-- no [tags output] prepend "kind:" to k/ (or K/) field output, - shell yes Passwd s-- no login shell - decorators no Python s-- no decorators on functions and classes - sectionMarker no ReStructuredText s-- no character used for declaring section +- mixin yes Ruby s-- no how the class or module is mixed in (mixin:HOW:MODULE) # Foo input.java /^abstract public class Foo extends Bar$/ x input.java /^ public int x;$/ diff --git a/parsers/ruby.c b/parsers/ruby.c index d8793ed581..c25ffc5e90 100644 --- a/parsers/ruby.c +++ b/parsers/ruby.c @@ -47,6 +47,16 @@ static kindDefinition RubyKinds [] = { #endif }; +typedef enum { + F_MIXIN, +} rubyField; + +static fieldDefinition RubyFields[] = { + { .name = "mixin", + .description = "how the class or module is mixed in (mixin:HOW:MODULE)", + .enabled = true }, +}; + static NestingLevels* nesting = NULL; #define SCOPE_SEPARATOR '.' @@ -645,6 +655,8 @@ extern parserDefinition* RubyParser (void) def->kindCount = ARRAY_SIZE (RubyKinds); def->extensions = extensions; def->parser = findRubyTags; + def->fieldTable = RubyFields; + def->fieldCount = ARRAY_SIZE (RubyFields); def->useCork = true; return def; } From 7af218145c7d6321fb097665f17f424eba7e0676 Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Tue, 31 Mar 2020 16:03:30 +0900 Subject: [PATCH 5/8] Ruby: add more code to attach mixin field Signed-off-by: Masatake YAMATO --- parsers/ruby.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/parsers/ruby.c b/parsers/ruby.c index c25ffc5e90..295590be64 100644 --- a/parsers/ruby.c +++ b/parsers/ruby.c @@ -23,6 +23,7 @@ #include "nestlevel.h" #include "read.h" #include "routines.h" +#include "strlist.h" #include "vstring.h" /* @@ -57,6 +58,10 @@ static fieldDefinition RubyFields[] = { .enabled = true }, }; +struct blockData { + stringList *mixin; +}; + static NestingLevels* nesting = NULL; #define SCOPE_SEPARATOR '.' @@ -454,6 +459,35 @@ static int readAndEmitTag (const unsigned char** cp, rubyKind expected_kind) return r; } +static void readAndStoreMixinSpec (const unsigned char** cp, const char *how_mixin) +{ + + NestingLevel *nl = nestingLevelsGetCurrent (nesting); + tagEntryInfo *e = getEntryOfNestingLevel (nl); + + if (! (e->kindIndex == K_CLASS || e->kindIndex == K_MODULE)) + return; + + if (isspace (**cp)) + { + vString *spec = vStringNewInit (how_mixin); + vStringPut(spec, ':'); + + size_t len = vStringLength (spec); + parseIdentifier (cp, spec, K_MODULE); + if (len == vStringLength (spec)) + { + vStringDelete (spec); + return; + } + + struct blockData *bdata = nestingLevelGetUserData (nl); + if (bdata->mixin == NULL) + bdata->mixin = stringListNew (); + stringListAdd (bdata->mixin, spec); + } +} + static void enterUnnamedScope (void) { int r = CORK_NIL; @@ -470,12 +504,38 @@ static void enterUnnamedScope (void) nestingLevelsPush (nesting, r); } +static void attachMixinField (int corkIndex, stringList *mixinSpec) +{ + vString *mixinField = stringListItem (mixinSpec, 0); + for (unsigned int i = 1; i < stringListCount (mixinSpec); i++) + { + vStringPut (mixinField, ','); + vStringCat (mixinField, stringListItem (mixinSpec, 1)); + } + + attachParserFieldToCorkEntry (corkIndex, RubyFields [F_MIXIN].ftype, + vStringValue (mixinField)); +} + +static void deleteBlockData (NestingLevel *nl) +{ + struct blockData *bdata = nestingLevelGetUserData (nl); + + if (nl->corkIndex != CORK_NIL + && bdata->mixin != NULL + && stringListCount (bdata->mixin) > 0) + attachMixinField (nl->corkIndex, bdata->mixin); + + if (bdata->mixin) + stringListDelete (bdata->mixin); +} + static void findRubyTags (void) { const unsigned char *line; bool inMultiLineComment = false; - nesting = nestingLevelsNew (0); + nesting = nestingLevelsNewFull (sizeof (struct blockData), deleteBlockData); /* FIXME: this whole scheme is wrong, because Ruby isn't line-based. * You could perfectly well write: From 0912d1e53432e40d284686f899673b42cb5e026f Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Tue, 31 Mar 2020 16:03:59 +0900 Subject: [PATCH 6/8] Ruby: fill the mixin field with "include X" Signed-off-by: Masatake YAMATO --- parsers/ruby.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/parsers/ruby.c b/parsers/ruby.c index 295590be64..903e27a796 100644 --- a/parsers/ruby.c +++ b/parsers/ruby.c @@ -623,6 +623,10 @@ static void findRubyTags (void) } } } + else if (canMatchKeywordWithAssign (&cp, "include")) + { + readAndStoreMixinSpec (&cp, "include"); + } else if (canMatchKeywordWithAssign (&cp, "def")) { rubyKind kind = K_METHOD; From 205052c4584eec60c22ca9ff5ea6063c8fd2bd48 Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Tue, 31 Mar 2020 16:12:24 +0900 Subject: [PATCH 7/8] Units,Ruby: add a case for mixin field --- .../ruby-mixin-field.d/args.ctags | 1 + .../ruby-mixin-field.d/expected.tags | 6 ++++++ .../parser-ruby.r/ruby-mixin-field.d/input.rb | 21 +++++++++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 Units/parser-ruby.r/ruby-mixin-field.d/args.ctags create mode 100644 Units/parser-ruby.r/ruby-mixin-field.d/expected.tags create mode 100644 Units/parser-ruby.r/ruby-mixin-field.d/input.rb diff --git a/Units/parser-ruby.r/ruby-mixin-field.d/args.ctags b/Units/parser-ruby.r/ruby-mixin-field.d/args.ctags new file mode 100644 index 0000000000..5ee5f79f70 --- /dev/null +++ b/Units/parser-ruby.r/ruby-mixin-field.d/args.ctags @@ -0,0 +1 @@ +--sort=no diff --git a/Units/parser-ruby.r/ruby-mixin-field.d/expected.tags b/Units/parser-ruby.r/ruby-mixin-field.d/expected.tags new file mode 100644 index 0000000000..bf91b765c6 --- /dev/null +++ b/Units/parser-ruby.r/ruby-mixin-field.d/expected.tags @@ -0,0 +1,6 @@ +X input.rb /^module X$/;" m +hi input.rb /^ def hi$/;" f module:X +Y input.rb /^module Y$/;" m +hoi input.rb /^ def hoi$/;" f module:Y +A input.rb /^class A$/;" c mixin:include:X,include:Y +hi input.rb /^ def hi$/;" f class:A diff --git a/Units/parser-ruby.r/ruby-mixin-field.d/input.rb b/Units/parser-ruby.r/ruby-mixin-field.d/input.rb new file mode 100644 index 0000000000..ca6454bea0 --- /dev/null +++ b/Units/parser-ruby.r/ruby-mixin-field.d/input.rb @@ -0,0 +1,21 @@ +# Taken from a comment in #2476 submitted by @AmaiKinono + +module X + def hi + p "Calling 'hi' in X." + end +end + +module Y + def hoi + p "Calling 'hoi' in Y." + end +end + +class A + include X + def hi + p "Calling 'hi' in A." + end + include Y +end From f9a77b5de6b8860911a9500204de5a38df374154 Mon Sep 17 00:00:00 2001 From: Masatake YAMATO Date: Thu, 2 Apr 2020 01:47:55 +0900 Subject: [PATCH 8/8] Ruby: recognize "include" in a singleton method as a mixin instruction Signed-off-by: Masatake YAMATO --- Units/parser-ruby.r/ruby-mixin-field.d/expected.tags | 2 ++ Units/parser-ruby.r/ruby-mixin-field.d/input.rb | 6 ++++++ parsers/ruby.c | 8 ++++++++ 3 files changed, 16 insertions(+) diff --git a/Units/parser-ruby.r/ruby-mixin-field.d/expected.tags b/Units/parser-ruby.r/ruby-mixin-field.d/expected.tags index bf91b765c6..e11479eeca 100644 --- a/Units/parser-ruby.r/ruby-mixin-field.d/expected.tags +++ b/Units/parser-ruby.r/ruby-mixin-field.d/expected.tags @@ -4,3 +4,5 @@ Y input.rb /^module Y$/;" m hoi input.rb /^ def hoi$/;" f module:Y A input.rb /^class A$/;" c mixin:include:X,include:Y hi input.rb /^ def hi$/;" f class:A +B input.rb /^class B$/;" c mixin:include:X +prep input.rb /^ def self.prep$/;" S class:B diff --git a/Units/parser-ruby.r/ruby-mixin-field.d/input.rb b/Units/parser-ruby.r/ruby-mixin-field.d/input.rb index ca6454bea0..d1d8d0a3c2 100644 --- a/Units/parser-ruby.r/ruby-mixin-field.d/input.rb +++ b/Units/parser-ruby.r/ruby-mixin-field.d/input.rb @@ -19,3 +19,9 @@ def hi end include Y end + +class B + def self.prep + include X + end +end diff --git a/parsers/ruby.c b/parsers/ruby.c index 903e27a796..31921f9f7e 100644 --- a/parsers/ruby.c +++ b/parsers/ruby.c @@ -465,6 +465,14 @@ static void readAndStoreMixinSpec (const unsigned char** cp, const char *how_mix NestingLevel *nl = nestingLevelsGetCurrent (nesting); tagEntryInfo *e = getEntryOfNestingLevel (nl); + if (e->kindIndex == K_SINGLETON) + { + nl = nestingLevelsGetNth (nesting, nesting->n - 2); + if (nl == NULL) + return; + e = getEntryOfNestingLevel (nl); + } + if (! (e->kindIndex == K_CLASS || e->kindIndex == K_MODULE)) return;