Skip to content

Commit

Permalink
Merge pull request #2478 from masatake/ruby-mixin-include
Browse files Browse the repository at this point in the history
Ruby: introduce mixin field
  • Loading branch information
masatake authored Apr 1, 2020
2 parents 71529ec + f9a77b5 commit 6c50aae
Show file tree
Hide file tree
Showing 14 changed files with 153 additions and 12 deletions.
1 change: 1 addition & 0 deletions Tmain/list-fields-with-prefix.d/stdout-expected.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
1 change: 1 addition & 0 deletions Tmain/list-fields.d/stdout-expected.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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;$/
Expand Down
1 change: 1 addition & 0 deletions Units/parser-ruby.r/ruby-mixin-field.d/args.ctags
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--sort=no
8 changes: 8 additions & 0 deletions Units/parser-ruby.r/ruby-mixin-field.d/expected.tags
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
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
B input.rb /^class B$/;" c mixin:include:X
prep input.rb /^ def self.prep$/;" S class:B
27 changes: 27 additions & 0 deletions Units/parser-ruby.r/ruby-mixin-field.d/input.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# 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

class B
def self.prep
include X
end
end
4 changes: 2 additions & 2 deletions main/entry.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) )
Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion main/entry_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions main/field.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion main/fmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
20 changes: 18 additions & 2 deletions main/nestlevel.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include "routines.h"
#include "nestlevel.h"

#include <string.h>

/* TODO: Alignment */
#define NL_SIZE(nls) (sizeof(NestingLevel) + (nls)->userDataSize)
#define NL_NTH(nls,n) (NestingLevel *)(((char *)((nls)->levels)) + ((n) * NL_SIZE (nls)))
Expand All @@ -26,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);
Expand All @@ -61,6 +72,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;
}

Expand All @@ -80,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--;
}
Expand Down
3 changes: 3 additions & 0 deletions main/nestlevel.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions main/writer-ctags.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))
{
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion main/writer-json.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
86 changes: 85 additions & 1 deletion parsers/ruby.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "nestlevel.h"
#include "read.h"
#include "routines.h"
#include "strlist.h"
#include "vstring.h"

/*
Expand All @@ -47,6 +48,20 @@ 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 },
};

struct blockData {
stringList *mixin;
};

static NestingLevels* nesting = NULL;

#define SCOPE_SEPARATOR '.'
Expand Down Expand Up @@ -444,6 +459,43 @@ 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_SINGLETON)
{
nl = nestingLevelsGetNth (nesting, nesting->n - 2);
if (nl == NULL)
return;
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;
Expand All @@ -460,12 +512,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:
Expand Down Expand Up @@ -553,6 +631,10 @@ static void findRubyTags (void)
}
}
}
else if (canMatchKeywordWithAssign (&cp, "include"))
{
readAndStoreMixinSpec (&cp, "include");
}
else if (canMatchKeywordWithAssign (&cp, "def"))
{
rubyKind kind = K_METHOD;
Expand Down Expand Up @@ -645,6 +727,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;
}

0 comments on commit 6c50aae

Please sign in to comment.