[geany/geany] e43349: Sync go parser with fishman-ctags
Jiří Techet
git-noreply at xxxxx
Thu May 28 14:22:54 UTC 2015
Branch: refs/heads/master
Author: Jiří Techet <techet at gmail.com>
Committer: Jiří Techet <techet at gmail.com>
Date: Thu, 28 May 2015 14:22:54 UTC
Commit: e433490672da19a19554bc7f40aa6e90c67fb08b
https://github.com/geany/geany/commit/e433490672da19a19554bc7f40aa6e90c67fb08b
Log Message:
-----------
Sync go parser with fishman-ctags
New features include:
* struct/interface detection
* struct member parsing
* function prototype parsing
Modified Paths:
--------------
tagmanager/ctags/go.c
tests/ctags/test.go
tests/ctags/test.go.tags
Modified: tagmanager/ctags/go.c
249 lines changed, 201 insertions(+), 48 deletions(-)
===================================================================
@@ -13,6 +13,7 @@
/*
* MACROS
*/
+#define MAX_SIGNATURE_LENGTH 512
#define isType(token,t) (boolean) ((token)->type == (t))
#define isKeyword(token,k) (boolean) ((token)->keyword == (k))
@@ -77,6 +78,7 @@ typedef struct sTokenInfo {
static int Lang_go;
static vString *scope;
+static vString *signature = NULL;
typedef enum {
GOTAG_UNDEFINED = -1,
@@ -85,6 +87,9 @@ typedef enum {
GOTAG_CONST,
GOTAG_TYPE,
GOTAG_VAR,
+ GOTAG_STRUCT,
+ GOTAG_INTERFACE,
+ GOTAG_MEMBER
} goKind;
static kindOption GoKinds[] = {
@@ -92,7 +97,10 @@ static kindOption GoKinds[] = {
{TRUE, 'f', "function", "functions"},
{TRUE, 'c', "macro", "constants"},
{TRUE, 't', "typedef", "types"},
- {TRUE, 'v', "variable", "variables"}
+ {TRUE, 'v', "variable", "variables"},
+ {TRUE, 's', "struct", "structs"},
+ {TRUE, 'i', "interface", "interfaces"},
+ {TRUE, 'm', "member", "struct members"}
};
static keywordDesc GoKeywordTable[] = {
@@ -149,6 +157,17 @@ static tokenInfo *newToken (void)
return token;
}
+static tokenInfo *copyToken (tokenInfo *other)
+{
+ tokenInfo *const token = xMalloc (1, tokenInfo);
+ token->type = other->type;
+ token->keyword = other->keyword;
+ token->string = vStringNewCopy (other->string);
+ token->lineNumber = other->lineNumber;
+ token->filePosition = other->filePosition;
+ return token;
+}
+
static void deleteToken (tokenInfo * const token)
{
if (token != NULL)
@@ -201,6 +220,8 @@ static void readToken (tokenInfo *const token)
{
int c;
static tokenType lastTokenType = TOKEN_NONE;
+ boolean firstWhitespace = TRUE;
+ boolean whitespace;
token->type = TOKEN_NONE;
token->keyword = KEYWORD_NONE;
@@ -219,11 +240,16 @@ static void readToken (tokenInfo *const token)
lastTokenType == TOKEN_CLOSE_CURLY ||
lastTokenType == TOKEN_CLOSE_SQUARE))
{
- token->type = TOKEN_SEMICOLON;
- goto done;
+ c = ';'; // semicolon injection
+ }
+ whitespace = c == '\t' || c == ' ' || c == '\r' || c == '\n';
+ if (signature && whitespace && firstWhitespace && vStringLength (signature) < MAX_SIGNATURE_LENGTH)
+ {
+ firstWhitespace = FALSE;
+ vStringPut(signature, ' ');
}
}
- while (c == '\t' || c == ' ' || c == '\r' || c == '\n');
+ while (whitespace);
switch (c)
{
@@ -354,71 +380,79 @@ static void readToken (tokenInfo *const token)
break;
}
-done:
+ if (signature && vStringLength (signature) < MAX_SIGNATURE_LENGTH)
+ {
+ if (token->type == TOKEN_LEFT_ARROW)
+ vStringCatS(signature, "<-");
+ else if (token->type == TOKEN_STRING)
+ {
+ // only struct member annotations can appear in function prototypes
+ // so only `` type strings are possible
+ vStringPut(signature, '`');
+ vStringCat(signature, token->string);
+ vStringPut(signature, '`');
+ }
+ else if (token->type == TOKEN_IDENTIFIER || token->type == TOKEN_KEYWORD)
+ vStringCat(signature, token->string);
+ else if (c != EOF)
+ vStringPut(signature, c);
+ }
+
lastTokenType = token->type;
}
-static void skipToMatched (tokenInfo *const token)
+static boolean skipToMatchedNoRead (tokenInfo *const token)
{
int nest_level = 0;
- tokenType open_token;
+ tokenType open_token = token->type;
tokenType close_token;
- switch (token->type)
+ switch (open_token)
{
case TOKEN_OPEN_PAREN:
- open_token = TOKEN_OPEN_PAREN;
close_token = TOKEN_CLOSE_PAREN;
break;
case TOKEN_OPEN_CURLY:
- open_token = TOKEN_OPEN_CURLY;
close_token = TOKEN_CLOSE_CURLY;
break;
case TOKEN_OPEN_SQUARE:
- open_token = TOKEN_OPEN_SQUARE;
close_token = TOKEN_CLOSE_SQUARE;
break;
default:
- return;
+ return FALSE;
}
/*
* This routine will skip to a matching closing token.
- * It will also handle nested tokens like the (, ) below.
- * ( name varchar(30), text binary(10) )
+ * It will also handle nested tokens.
*/
- if (isType (token, open_token))
+ nest_level++;
+ while (nest_level > 0 && !isType (token, TOKEN_EOF))
{
- nest_level++;
- while (!(isType (token, close_token) && (nest_level == 0)) &&
- !isType (token, TOKEN_EOF))
- {
- readToken (token);
- if (isType (token, open_token))
- {
- nest_level++;
- }
- if (isType (token, close_token))
- {
- if (nest_level > 0)
- {
- nest_level--;
- }
- }
- }
readToken (token);
+ if (isType (token, open_token))
+ nest_level++;
+ else if (isType (token, close_token))
+ nest_level--;
}
+
+ return TRUE;
+}
+
+static void skipToMatched (tokenInfo *const token)
+{
+ if (skipToMatchedNoRead (token))
+ readToken (token);
}
-static void skipType (tokenInfo *const token)
+static boolean skipType (tokenInfo *const token)
{
-again:
// Type = TypeName | TypeLit | "(" Type ")" .
// Skips also function multiple return values "(" Type {"," Type} ")"
if (isType (token, TOKEN_OPEN_PAREN))
{
skipToMatched (token);
- return;
+ return TRUE;
}
// TypeName = QualifiedIdent.
@@ -433,7 +467,7 @@ static void skipType (tokenInfo *const token)
if (isType (token, TOKEN_IDENTIFIER))
readToken (token);
}
- return;
+ return TRUE;
}
// StructType = "struct" "{" { FieldDecl ";" } "}"
@@ -443,7 +477,7 @@ static void skipType (tokenInfo *const token)
readToken (token);
// skip over "{}"
skipToMatched (token);
- return;
+ return TRUE;
}
// ArrayType = "[" ArrayLength "]" ElementType .
@@ -452,7 +486,7 @@ static void skipType (tokenInfo *const token)
if (isType (token, TOKEN_OPEN_SQUARE))
{
skipToMatched (token);
- goto again;
+ return skipType (token);
}
// PointerType = "*" BaseType .
@@ -461,7 +495,7 @@ static void skipType (tokenInfo *const token)
if (isType (token, TOKEN_STAR) || isKeyword (token, KEYWORD_chan) || isType (token, TOKEN_LEFT_ARROW))
{
readToken (token);
- goto again;
+ return skipType (token);
}
// MapType = "map" "[" KeyType "]" ElementType .
@@ -471,7 +505,7 @@ static void skipType (tokenInfo *const token)
readToken (token);
// skip over "[]"
skipToMatched (token);
- goto again;
+ return skipType (token);
}
// FunctionType = "func" Signature .
@@ -486,11 +520,15 @@ static void skipType (tokenInfo *const token)
// Result is parameters or type or nothing. skipType treats anything
// surrounded by parentheses as a type, and does nothing if what
// follows is not a type.
- goto again;
+ return skipType (token);
}
+
+ return FALSE;
}
-static void makeTag (tokenInfo *const token, const goKind kind)
+static void makeTag (tokenInfo *const token, const goKind kind,
+ tokenInfo *const parent_token, const goKind parent_kind,
+ const char *argList)
{
const char *const name = vStringValue (token->string);
@@ -504,7 +542,14 @@ static void makeTag (tokenInfo *const token, const goKind kind)
e.filePosition = token->filePosition;
e.kindName = GoKinds [kind].name;
e.kind = GoKinds [kind].letter;
+ if (argList)
+ e.extensionFields.arglist = argList;
+ if (parent_kind != GOTAG_UNDEFINED && parent_token != NULL)
+ {
+ e.extensionFields.scope[0] = GoKinds[parent_kind].name;
+ e.extensionFields.scope[1] = vStringValue (parent_token->string);
+ }
makeTagEntry (&e);
if (scope && Option.include.qualifiedTags)
@@ -524,7 +569,7 @@ static void parsePackage (tokenInfo *const token)
readToken (token);
if (isType (token, TOKEN_IDENTIFIER))
{
- makeTag (token, GOTAG_PACKAGE);
+ makeTag (token, GOTAG_PACKAGE, NULL, GOTAG_UNDEFINED, NULL);
if (!scope && Option.include.qualifiedTags)
{
scope = vStringNew ();
@@ -549,11 +594,25 @@ static void parseFunctionOrMethod (tokenInfo *const token)
if (isType (token, TOKEN_IDENTIFIER))
{
- makeTag (token, GOTAG_FUNCTION);
-
+ tokenInfo *functionToken = copyToken (token);
+
+ // Start recording signature
+ signature = vStringNew ();
+
// Skip over parameters.
readToken (token);
- skipToMatched (token);
+ skipToMatchedNoRead (token);
+
+ vStringStripLeading (signature);
+ vStringStripTrailing (signature);
+ makeTag (functionToken, GOTAG_FUNCTION, NULL, GOTAG_UNDEFINED, signature->buffer);
+ deleteToken (functionToken);
+ vStringDelete(signature);
+
+ // Stop recording signature
+ signature = NULL;
+
+ readToken (token);
// Skip over result.
skipType (token);
@@ -564,6 +623,75 @@ static void parseFunctionOrMethod (tokenInfo *const token)
}
}
+static void parseStructMembers (tokenInfo *const token, tokenInfo *const parent_token)
+{
+ // StructType = "struct" "{" { FieldDecl ";" } "}" .
+ // FieldDecl = (IdentifierList Type | AnonymousField) [ Tag ] .
+ // AnonymousField = [ "*" ] TypeName .
+ // Tag = string_lit .
+
+ readToken (token);
+ if (!isType (token, TOKEN_OPEN_CURLY))
+ return;
+
+ readToken (token);
+ while (!isType (token, TOKEN_EOF) && !isType (token, TOKEN_CLOSE_CURLY))
+ {
+ tokenInfo *memberCandidate = NULL;
+ boolean first = TRUE;
+
+ while (!isType (token, TOKEN_EOF))
+ {
+ if (isType (token, TOKEN_IDENTIFIER))
+ {
+ if (first)
+ {
+ // could be anonymous field like in 'struct {int}' - we don't know yet
+ memberCandidate = copyToken (token);
+ first = FALSE;
+ }
+ else
+ {
+ if (memberCandidate)
+ {
+ // if we are here, there was a comma and memberCandidate isn't an anonymous field
+ makeTag (memberCandidate, GOTAG_MEMBER, parent_token, GOTAG_STRUCT, NULL);
+ deleteToken (memberCandidate);
+ memberCandidate = NULL;
+ }
+ makeTag (token, GOTAG_MEMBER, parent_token, GOTAG_STRUCT, NULL);
+ }
+ readToken (token);
+ }
+ if (!isType (token, TOKEN_COMMA))
+ break;
+ readToken (token);
+ }
+
+ // in the case of an anonymous field, we already read part of the
+ // type into memberCandidate and skipType() should return FALSE so no tag should
+ // be generated in this case.
+ if (skipType (token) && memberCandidate)
+ makeTag (memberCandidate, GOTAG_MEMBER, parent_token, GOTAG_STRUCT, NULL);
+
+ if (memberCandidate)
+ deleteToken (memberCandidate);
+
+ while (!isType (token, TOKEN_SEMICOLON) && !isType (token, TOKEN_CLOSE_CURLY)
+ && !isType (token, TOKEN_EOF))
+ {
+ readToken (token);
+ skipToMatched (token);
+ }
+
+ if (!isType (token, TOKEN_CLOSE_CURLY))
+ {
+ // we are at TOKEN_SEMICOLON
+ readToken (token);
+ }
+ }
+}
+
static void parseConstTypeVar (tokenInfo *const token, goKind kind)
{
// ConstDecl = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
@@ -586,11 +714,26 @@ static void parseConstTypeVar (tokenInfo *const token, goKind kind)
do
{
+ tokenInfo *typeToken = NULL;
+
while (!isType (token, TOKEN_EOF))
{
if (isType (token, TOKEN_IDENTIFIER))
{
- makeTag (token, kind);
+ if (kind == GOTAG_TYPE)
+ {
+ typeToken = copyToken (token);
+ readToken (token);
+ if (isKeyword (token, KEYWORD_struct))
+ makeTag (typeToken, GOTAG_STRUCT, NULL, GOTAG_UNDEFINED, NULL);
+ else if (isKeyword (token, KEYWORD_interface))
+ makeTag (typeToken, GOTAG_INTERFACE, NULL, GOTAG_UNDEFINED, NULL);
+ else
+ makeTag (typeToken, kind, NULL, GOTAG_UNDEFINED, NULL);
+ break;
+ }
+ else
+ makeTag (token, kind, NULL, GOTAG_UNDEFINED, NULL);
readToken (token);
}
if (!isType (token, TOKEN_COMMA))
@@ -598,7 +741,17 @@ static void parseConstTypeVar (tokenInfo *const token, goKind kind)
readToken (token);
}
- skipType (token);
+ if (typeToken)
+ {
+ if (isKeyword (token, KEYWORD_struct))
+ parseStructMembers (token, typeToken);
+ else
+ skipType (token);
+ deleteToken (typeToken);
+ }
+ else
+ skipType (token);
+
while (!isType (token, TOKEN_SEMICOLON) && !isType (token, TOKEN_CLOSE_PAREN)
&& !isType (token, TOKEN_EOF))
{
Modified: tests/ctags/test.go
9 lines changed, 6 insertions(+), 3 deletions(-)
===================================================================
@@ -15,8 +15,11 @@ type (
)
type T6 struct {
- a, b, c, d int
- e float32
+ _a, _b, _c, _d int
+ int
+ T1 `annotation`
+ *T2
+ _e float32
//ignored int
}
@@ -44,4 +47,4 @@ func (tt * T7) f4(a func () func ()) (func (), int) {return func (){}, 1};func f
func main() {
go func (){}()
fmt.Println("Hello, 世界")
-}
+}
\ No newline at end of file
Modified: tests/ctags/test.go.tags
21 lines changed, 13 insertions(+), 8 deletions(-)
===================================================================
@@ -12,23 +12,28 @@ T1
T2�4096�0
T3�4096�0
T4�4096�0
-T5�4096�0
-T6�4096�0
+T5�32�0
+T6�2048�0
T7�4096�0
T8�4096�0
T9�4096�0
+_a�64�T6�0
+_b�64�T6�0
+_c�64�T6�0
+_d�64�T6�0
+_e�64�T6�0
a�16384�0
b�16384�0
c�16384�0
d�16384�0
e�16384�0
f�16384�0
-f1�16�0
-f2�16�0
-f3�16�0
-f4�16�0
-f5�16�0
+f1�16�()�0
+f2�16�()�0
+f3�16�()�0
+f4�16�(a func () func ())�0
+f5�16�()�0
g�16384�0
h�16384�0
-main�16�0
+main�16�()�0
main�256�0
--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).
More information about the Commits
mailing list