Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ruby: introduce mixin field #2478

Merged
merged 8 commits into from
Apr 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}