Merge pull request #481 from techee/go

Go ctags parser, Scintilla lexer and Geany support improvements

Closes #238.
This commit is contained in:
Colomban Wendling 2015-05-28 16:50:01 +02:00
commit f1c98e5423
10 changed files with 274 additions and 68 deletions

View File

@ -4,13 +4,17 @@
[keywords]
# all items must be in one line
primary=break case chan const continue default defer else fallthrough for func go goto if import interface map package range return select struct switch type var
secondary=byte bool rune int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 float32 float64 complex64 complex128 uintptr string
primary=break case chan const continue default defer else fallthrough for func go goto if import interface map package range return select struct switch type var true false iota nil
secondary=bool byte complex64 complex128 error float32 float64 int int8 int16 int32 int64 rune string uint uint8 uint16 uint32 uint64 uintptr
# these are the Doxygen keywords
docComment=a addindex addtogroup anchor arg attention author authors b brief bug c callergraph callgraph category cite class code cond copybrief copydetails copydoc copyright date def defgroup deprecated details dir dontinclude dot dotfile e else elseif em endcode endcond enddot endhtmlonly endif endinternal endlatexonly endlink endmanonly endmsc endrtfonly endverbatim endxmlonly enum example exception extends file fn headerfile hideinitializer htmlinclude htmlonly if ifnot image implements include includelineno ingroup interface internal invariant latexonly li line link mainpage manonly memberof msc mscfile n name namespace nosubgrouping note overload p package page par paragraph param post pre private privatesection property protected protectedsection protocol public publicsection ref related relatedalso relates relatesalso remark remarks result return returns retval rtfonly sa section see short showinitializer since skip skipline snippet struct subpage subsection subsubsection tableofcontents test throw throws todo tparam typedef union until var verbatim verbinclude version warning weakgroup xmlonly xrefitem
[lexer_properties=C]
[lexer_properties]
lexer.cpp.backquoted.strings=1
styling.within.preprocessor=1
lexer.cpp.allow.dollars=0
fold.preprocessor=0
[settings]

View File

@ -2649,6 +2649,7 @@ void document_highlight_tags(GeanyDocument *doc)
case GEANY_FILETYPES_OBJECTIVEC:
case GEANY_FILETYPES_VALA:
case GEANY_FILETYPES_RUST:
case GEANY_FILETYPES_GO:
{
/* index of the keyword set in the Scintilla lexer, for

View File

@ -1790,7 +1790,7 @@ static gboolean append_calltip(GString *str, const TMTag *tag, filetype_id ft_id
if (! tag->arglist)
return FALSE;
if (ft_id != GEANY_FILETYPES_PASCAL)
if (ft_id != GEANY_FILETYPES_PASCAL && ft_id != GEANY_FILETYPES_GO)
{ /* usual calltips: "retval tagname (arglist)" */
if (tag->var_type)
{
@ -1815,14 +1815,15 @@ static gboolean append_calltip(GString *str, const TMTag *tag, filetype_id ft_id
g_string_append(str, tag->arglist);
}
else
{ /* special case Pascal calltips: "tagname (arglist) : retval" */
{ /* special case Pascal/Go calltips: "tagname (arglist) : retval"
* (with ':' omitted for Go) */
g_string_append(str, tag->name);
g_string_append_c(str, ' ');
g_string_append(str, tag->arglist);
if (!EMPTY(tag->var_type))
{
g_string_append(str, " : ");
g_string_append(str, ft_id == GEANY_FILETYPES_PASCAL ? " : " : " ");
g_string_append(str, tag->var_type);
}
}

View File

@ -698,7 +698,7 @@ static void add_top_level_items(GeanyDocument *doc)
&(tv_iters.tag_variable), _("Variables"), "classviewer-var",
&(tv_iters.tag_macro), _("Macros"), "classviewer-macro",
&(tv_iters.tag_member), _("Methods"), "classviewer-member",
&(tv_iters.tag_other), _("Other"), "classviewer-other", NULL,
&(tv_iters.tag_other), _("Other"), "classviewer-other",
NULL);
break;
}
@ -707,10 +707,13 @@ static void add_top_level_items(GeanyDocument *doc)
tag_list_add_groups(tag_store,
&(tv_iters.tag_namespace), _("Package"), "classviewer-namespace",
&(tv_iters.tag_function), _("Functions"), "classviewer-method",
&(tv_iters.tag_macro), _("Constants"), "classviewer-macro",
&(tv_iters.tag_interface), _("Interfaces"), "classviewer-struct",
&(tv_iters.tag_struct), _("Structs"), "classviewer-struct",
&(tv_iters.tag_type), _("Types"), "classviewer-struct",
&(tv_iters.tag_macro), _("Constants"), "classviewer-macro",
&(tv_iters.tag_variable), _("Variables"), "classviewer-var",
&(tv_iters.tag_other), _("Other"), "classviewer-other", NULL,
&(tv_iters.tag_member), _("Members"), "classviewer-member",
&(tv_iters.tag_other), _("Other"), "classviewer-other",
NULL);
break;
}

View File

@ -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 @@ getNextChar:
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 @@ getNextChar:
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 skipType (tokenInfo *const token)
static void skipToMatched (tokenInfo *const token)
{
if (skipToMatchedNoRead (token))
readToken (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 @@ again:
if (isType (token, TOKEN_IDENTIFIER))
readToken (token);
}
return;
return TRUE;
}
// StructType = "struct" "{" { FieldDecl ";" } "}"
@ -443,7 +477,7 @@ again:
readToken (token);
// skip over "{}"
skipToMatched (token);
return;
return TRUE;
}
// ArrayType = "[" ArrayLength "]" ElementType .
@ -452,7 +486,7 @@ again:
if (isType (token, TOKEN_OPEN_SQUARE))
{
skipToMatched (token);
goto again;
return skipType (token);
}
// PointerType = "*" BaseType .
@ -461,7 +495,7 @@ again:
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 @@ again:
readToken (token);
// skip over "[]"
skipToMatched (token);
goto again;
return skipType (token);
}
// FunctionType = "func" Signature .
@ -486,11 +520,15 @@ again:
// 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 *varType)
{
const char *const name = vStringValue (token->string);
@ -504,7 +542,16 @@ 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 (varType)
e.extensionFields.varType = varType;
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 +571,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, NULL);
if (!scope && Option.include.qualifiedTags)
{
scope = vStringNew ();
@ -549,21 +596,115 @@ static void parseFunctionOrMethod (tokenInfo *const token)
if (isType (token, TOKEN_IDENTIFIER))
{
makeTag (token, GOTAG_FUNCTION);
vString *argList;
tokenInfo *functionToken = copyToken (token);
// Start recording signature
signature = vStringNew ();
// Skip over parameters.
readToken (token);
skipToMatched (token);
skipToMatchedNoRead (token);
vStringStripLeading (signature);
vStringStripTrailing (signature);
argList = signature;
signature = vStringNew ();
readToken (token);
// Skip over result.
skipType (token);
// Remove the extra { we have just read
vStringStripTrailing (signature);
vStringChop (signature);
vStringStripLeading (signature);
vStringStripTrailing (signature);
makeTag (functionToken, GOTAG_FUNCTION, NULL, GOTAG_UNDEFINED, argList->buffer, signature->buffer);
deleteToken (functionToken);
vStringDelete(signature);
vStringDelete(argList);
// Stop recording signature
signature = NULL;
// Skip over function body.
if (isType (token, TOKEN_OPEN_CURLY))
skipToMatched (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, NULL);
deleteToken (memberCandidate);
memberCandidate = NULL;
}
makeTag (token, GOTAG_MEMBER, parent_token, GOTAG_STRUCT, NULL, 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, 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 +727,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, NULL);
else if (isKeyword (token, KEYWORD_interface))
makeTag (typeToken, GOTAG_INTERFACE, NULL, GOTAG_UNDEFINED, NULL, NULL);
else
makeTag (typeToken, kind, NULL, GOTAG_UNDEFINED, NULL, NULL);
break;
}
else
makeTag (token, kind, NULL, GOTAG_UNDEFINED, NULL, NULL);
readToken (token);
}
if (!isType (token, TOKEN_COMMA))
@ -598,7 +754,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))
{

View File

@ -243,6 +243,7 @@ test_sources = \
refcurs.sql \
regexp.js \
return-hint.zep \
return-types.go \
rules.t2t \
sample.t2t \
secondary_fcn_name.js \

View File

@ -0,0 +1,16 @@
package main
func foo() *struct {a int; b func()} {
return nil
}
func bar() (a int, b func(a int), c interface{}, d string) {
return 0, nil, nil, ""
}
func baz() *[20*3+1]map[chan<- /* map[int]int */<-chan int]map[interface{}]*string {
return nil
}
func main() {
}

View File

@ -0,0 +1,6 @@
# format=tagmanager
barÌ16Í()Ö0Ï(a int, b func(a int), c interface{}, d string)
bazÌ16Í()Ö0Ï*[20*3+1]map[chan<- <-chan int]map[interface{}]*string
fooÌ16Í()Ö0Ï*struct {a int; b func()}
mainÌ16Í()Ö0Ï
mainÌ256Ö0

View File

@ -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, 世界")
}
}

View File

@ -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Ï(a, b int)
f4Ì16Í(a func () func ())Ö0Ï(func (), int)
f5Ì16Í()Ö0Ï
gフ16384ヨ0
hフ16384ヨ0
mainフ16ヨ0
mainÌ16Í()Ö0Ï
mainフ256ヨ0