From ccb15a31be106273a19a6c3d4dfa29f3adbb17a8 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ji=C5=99=C3=AD=20Techet?= Date: Fri, 7 Nov 2014 17:09:53 +0100 Subject: [PATCH] Add the go ctags parser Make go one of the builtin filetypes, add the parser and update the related source and config files. While there, remove Rust from [Groups] in filetype_extensions.conf because it's already a builtin filetype as well. The parser itself is stolen from the fishman/ctags repo. --- data/Makefile.am | 2 +- data/filetype_extensions.conf | 2 +- data/{filetypes.Go.conf => filetypes.go} | 1 - src/filetypes.c | 1 + src/filetypes.h | 1 + src/highlighting.c | 2 + src/highlightingmappings.h | 7 + src/symbols.c | 12 + tagmanager/ctags/Makefile.am | 1 + tagmanager/ctags/go.c | 687 +++++++++++++++++++++++++++++++ tagmanager/ctags/makefile.win32 | 2 +- tagmanager/ctags/parsers.h | 3 +- tagmanager/src/tm_parser.h | 1 + tests/ctags/Makefile.am | 1 + tests/ctags/test.go | 47 +++ tests/ctags/test.go.tags | 34 ++ wscript | 1 + 17 files changed, 800 insertions(+), 5 deletions(-) rename data/{filetypes.Go.conf => filetypes.go} (99%) create mode 100644 tagmanager/ctags/go.c create mode 100644 tests/ctags/test.go create mode 100644 tests/ctags/test.go.tags diff --git a/data/Makefile.am b/data/Makefile.am index 30c0c1e02..42d76c3e4 100644 --- a/data/Makefile.am +++ b/data/Makefile.am @@ -33,7 +33,7 @@ filetypes = \ filetypes.freebasic \ filetypes.Genie.conf \ filetypes.glsl \ - filetypes.Go.conf \ + filetypes.go \ filetypes.Graphviz.conf \ filetypes.haskell \ filetypes.haxe \ diff --git a/data/filetype_extensions.conf b/data/filetype_extensions.conf index 8de1a9b5a..0688ed070 100644 --- a/data/filetype_extensions.conf +++ b/data/filetype_extensions.conf @@ -69,7 +69,7 @@ None=*; # Note: restarting is required after editing groups [Groups] -Programming=Clojure;CUDA;Cython;Genie;Go;Rust;Scala; +Programming=Clojure;CUDA;Cython;Genie;Scala; Script=Graphviz; Markup= Misc= diff --git a/data/filetypes.Go.conf b/data/filetypes.go similarity index 99% rename from data/filetypes.Go.conf rename to data/filetypes.go index 4ad48020e..9d3d61103 100644 --- a/data/filetypes.Go.conf +++ b/data/filetypes.go @@ -13,7 +13,6 @@ docComment=a addindex addtogroup anchor arg attention author authors b brief bug [lexer_properties=C] [settings] -lexer_filetype=C # default extension used when saving files extension=go diff --git a/src/filetypes.c b/src/filetypes.c index cb608fa50..dd1a31892 100644 --- a/src/filetypes.c +++ b/src/filetypes.c @@ -190,6 +190,7 @@ static void init_builtin_filetypes(void) FT_INIT( BATCH, NONE, "Batch", NULL, SCRIPT, SCRIPT ); FT_INIT( POWERSHELL, NONE, "PowerShell", NULL, SOURCE_FILE, SCRIPT ); FT_INIT( RUST, RUST, "Rust", NULL, SOURCE_FILE, COMPILED ); + FT_INIT( GO, GO, "Go", NULL, SOURCE_FILE, COMPILED ); } diff --git a/src/filetypes.h b/src/filetypes.h index 3308abba6..27f4ab650 100644 --- a/src/filetypes.h +++ b/src/filetypes.h @@ -100,6 +100,7 @@ typedef enum GEANY_FILETYPES_BATCH, GEANY_FILETYPES_POWERSHELL, GEANY_FILETYPES_RUST, + GEANY_FILETYPES_GO, /* ^ append items here */ GEANY_MAX_BUILT_IN_FILETYPES /* Don't use this, use filetypes_array->len instead */ } diff --git a/src/highlighting.c b/src/highlighting.c index ee8b8aeb8..acff85af6 100644 --- a/src/highlighting.c +++ b/src/highlighting.c @@ -1029,6 +1029,7 @@ void highlighting_init_styles(guint filetype_idx, GKeyFile *config, GKeyFile *co init_styleset_case(F77); init_styleset_case(FORTH); init_styleset_case(FORTRAN); + init_styleset_case(GO); init_styleset_case(HASKELL); init_styleset_case(HAXE); init_styleset_case(AS); @@ -1112,6 +1113,7 @@ void highlighting_set_styles(ScintillaObject *sci, GeanyFiletype *ft) styleset_case(F77); styleset_case(FORTH); styleset_case(FORTRAN); + styleset_case(GO); styleset_case(HASKELL); styleset_case(HAXE); styleset_case(AS); diff --git a/src/highlightingmappings.h b/src/highlightingmappings.h index 3b16b0e35..fe7d3a519 100644 --- a/src/highlightingmappings.h +++ b/src/highlightingmappings.h @@ -624,6 +624,13 @@ static const HLKeyword highlighting_keywords_FORTH[] = #define highlighting_properties_FORTRAN highlighting_properties_F77 +/* Go */ +#define highlighting_lexer_GO SCLEX_CPP +#define highlighting_styles_GO highlighting_styles_C +#define highlighting_keywords_GO highlighting_keywords_C +#define highlighting_properties_GO highlighting_properties_C + + /* Haskell */ #define highlighting_lexer_HASKELL SCLEX_HASKELL static const HLStyle highlighting_styles_HASKELL[] = diff --git a/src/symbols.c b/src/symbols.c index d2055e87d..d326f8fbf 100644 --- a/src/symbols.c +++ b/src/symbols.c @@ -762,6 +762,18 @@ static void add_top_level_items(GeanyDocument *doc) NULL); break; } + case GEANY_FILETYPES_GO: + { + 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_type), _("Types"), "classviewer-struct", + &(tv_iters.tag_variable), _("Variables"), "classviewer-var", + &(tv_iters.tag_other), _("Other"), "classviewer-other", NULL, + NULL); + break; + } case GEANY_FILETYPES_PERL: { tag_list_add_groups(tag_store, diff --git a/tagmanager/ctags/Makefile.am b/tagmanager/ctags/Makefile.am index a110b920c..524ca8c73 100644 --- a/tagmanager/ctags/Makefile.am +++ b/tagmanager/ctags/Makefile.am @@ -24,6 +24,7 @@ parsers = \ diff.c \ docbook.c \ fortran.c \ + go.c \ haskell.c \ haxe.c \ html.c \ diff --git a/tagmanager/ctags/go.c b/tagmanager/ctags/go.c new file mode 100644 index 000000000..4eefab146 --- /dev/null +++ b/tagmanager/ctags/go.c @@ -0,0 +1,687 @@ +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ + +#include "entry.h" +#include "keyword.h" +#include "read.h" +#include "main.h" +#include "vstring.h" +#include "options.h" + +/* + * MACROS + */ +#define isType(token,t) (boolean) ((token)->type == (t)) +#define isKeyword(token,k) (boolean) ((token)->keyword == (k)) + +/* + * DATA DECLARATIONS + */ + +typedef enum eKeywordId { + KEYWORD_NONE = -1, + KEYWORD_package, + KEYWORD_import, + KEYWORD_const, + KEYWORD_type, + KEYWORD_var, + KEYWORD_func, + KEYWORD_struct, + KEYWORD_interface, + KEYWORD_map, + KEYWORD_chan +} keywordId; + +/* Used to determine whether keyword is valid for the current language and + * what its ID is. + */ +typedef struct sKeywordDesc { + const char *name; + keywordId id; +} keywordDesc; + +typedef enum eTokenType { + TOKEN_NONE = -1, + // Token not important for top-level Go parsing + TOKEN_OTHER, + TOKEN_KEYWORD, + TOKEN_IDENTIFIER, + TOKEN_STRING, + TOKEN_OPEN_PAREN, + TOKEN_CLOSE_PAREN, + TOKEN_OPEN_CURLY, + TOKEN_CLOSE_CURLY, + TOKEN_OPEN_SQUARE, + TOKEN_CLOSE_SQUARE, + TOKEN_SEMICOLON, + TOKEN_STAR, + TOKEN_LEFT_ARROW, + TOKEN_DOT, + TOKEN_COMMA, + TOKEN_EOF +} tokenType; + +typedef struct sTokenInfo { + tokenType type; + keywordId keyword; + vString *string; /* the name of the token */ + unsigned long lineNumber; /* line number of tag */ + MIOPos filePosition; /* file position of line containing name */ +} tokenInfo; + +/* +* DATA DEFINITIONS +*/ + +static int Lang_go; +static vString *scope; + +typedef enum { + GOTAG_UNDEFINED = -1, + GOTAG_PACKAGE, + GOTAG_FUNCTION, + GOTAG_CONST, + GOTAG_TYPE, + GOTAG_VAR, +} goKind; + +static kindOption GoKinds[] = { + {TRUE, 'p', "namespace", "packages"}, + {TRUE, 'f', "function", "functions"}, + {TRUE, 'c', "macro", "constants"}, + {TRUE, 't', "typedef", "types"}, + {TRUE, 'v', "variable", "variables"} +}; + +static keywordDesc GoKeywordTable[] = { + {"package", KEYWORD_package}, + {"import", KEYWORD_import}, + {"const", KEYWORD_const}, + {"type", KEYWORD_type}, + {"var", KEYWORD_var}, + {"func", KEYWORD_func}, + {"struct", KEYWORD_struct}, + {"interface", KEYWORD_interface}, + {"map", KEYWORD_map}, + {"chan", KEYWORD_chan} +}; + +/* +* FUNCTION DEFINITIONS +*/ + +// XXX UTF-8 +static boolean isStartIdentChar (const int c) +{ + return (boolean) + (isalpha (c) || c == '_' || c > 128); +} + +static boolean isIdentChar (const int c) +{ + return (boolean) + (isStartIdentChar (c) || isdigit (c)); +} + +static void initialize (const langType language) +{ + size_t i; + const size_t count = + sizeof (GoKeywordTable) / sizeof (GoKeywordTable[0]); + Lang_go = language; + for (i = 0; i < count; ++i) + { + const keywordDesc *const p = &GoKeywordTable[i]; + addKeyword (p->name, language, (int) p->id); + } +} + +static tokenInfo *newToken (void) +{ + tokenInfo *const token = xMalloc (1, tokenInfo); + token->type = TOKEN_NONE; + token->keyword = KEYWORD_NONE; + token->string = vStringNew (); + token->lineNumber = getSourceLineNumber (); + token->filePosition = getInputFilePosition (); + return token; +} + +static void deleteToken (tokenInfo * const token) +{ + if (token != NULL) + { + vStringDelete (token->string); + eFree (token); + } +} + +/* + * Parsing functions + */ + +static void parseString (vString *const string, const int delimiter) +{ + boolean end = FALSE; + while (!end) + { + int c = fileGetc (); + if (c == EOF) + end = TRUE; + else if (c == '\\' && delimiter != '`') + { + c = fileGetc (); + if (c != '\'' && c != '\"') + vStringPut (string, '\\'); + vStringPut (string, c); + } + else if (c == delimiter) + end = TRUE; + else + vStringPut (string, c); + } + vStringTerminate (string); +} + +static void parseIdentifier (vString *const string, const int firstChar) +{ + int c = firstChar; + do + { + vStringPut (string, c); + c = fileGetc (); + } while (isIdentChar (c)); + vStringTerminate (string); + fileUngetc (c); /* always unget, LF might add a semicolon */ +} + +static void readToken (tokenInfo *const token) +{ + int c; + static tokenType lastTokenType = TOKEN_NONE; + + token->type = TOKEN_NONE; + token->keyword = KEYWORD_NONE; + vStringClear (token->string); + +getNextChar: + do + { + c = fileGetc (); + token->lineNumber = getSourceLineNumber (); + token->filePosition = getInputFilePosition (); + if (c == '\n' && (lastTokenType == TOKEN_IDENTIFIER || + lastTokenType == TOKEN_STRING || + lastTokenType == TOKEN_OTHER || + lastTokenType == TOKEN_CLOSE_PAREN || + lastTokenType == TOKEN_CLOSE_CURLY || + lastTokenType == TOKEN_CLOSE_SQUARE)) + { + token->type = TOKEN_SEMICOLON; + goto done; + } + } + while (c == '\t' || c == ' ' || c == '\r' || c == '\n'); + + switch (c) + { + case EOF: + token->type = TOKEN_EOF; + break; + + case ';': + token->type = TOKEN_SEMICOLON; + break; + + case '/': + { + boolean hasNewline = FALSE; + int d = fileGetc (); + switch (d) + { + case '/': + fileSkipToCharacter ('\n'); + /* Line comments start with the + * character sequence // and + * continue through the next + * newline. A line comment acts + * like a newline. */ + fileUngetc ('\n'); + goto getNextChar; + case '*': + do + { + do + { + d = fileGetc (); + if (d == '\n') + { + hasNewline = TRUE; + } + } while (d != EOF && d != '*'); + + c = fileGetc (); + if (c == '/') + break; + else + fileUngetc (c); + } while (c != EOF && c != '\0'); + + fileUngetc (hasNewline ? '\n' : ' '); + goto getNextChar; + default: + token->type = TOKEN_OTHER; + fileUngetc (d); + break; + } + } + break; + + case '"': + case '\'': + case '`': + token->type = TOKEN_STRING; + parseString (token->string, c); + token->lineNumber = getSourceLineNumber (); + token->filePosition = getInputFilePosition (); + break; + + case '<': + { + int d = fileGetc (); + if (d == '-') + token->type = TOKEN_LEFT_ARROW; + else + { + fileUngetc (d); + token->type = TOKEN_OTHER; + } + } + break; + + case '(': + token->type = TOKEN_OPEN_PAREN; + break; + + case ')': + token->type = TOKEN_CLOSE_PAREN; + break; + + case '{': + token->type = TOKEN_OPEN_CURLY; + break; + + case '}': + token->type = TOKEN_CLOSE_CURLY; + break; + + case '[': + token->type = TOKEN_OPEN_SQUARE; + break; + + case ']': + token->type = TOKEN_CLOSE_SQUARE; + break; + + case '*': + token->type = TOKEN_STAR; + break; + + case '.': + token->type = TOKEN_DOT; + break; + + case ',': + token->type = TOKEN_COMMA; + break; + + default: + if (isStartIdentChar (c)) + { + parseIdentifier (token->string, c); + token->lineNumber = getSourceLineNumber (); + token->filePosition = getInputFilePosition (); + token->keyword = lookupKeyword (vStringValue (token->string), Lang_go); + if (isKeyword (token, KEYWORD_NONE)) + token->type = TOKEN_IDENTIFIER; + else + token->type = TOKEN_KEYWORD; + } + else + token->type = TOKEN_OTHER; + break; + } + +done: + lastTokenType = token->type; +} + +static void skipToMatched (tokenInfo *const token) +{ + int nest_level = 0; + tokenType open_token; + tokenType close_token; + + switch (token->type) + { + 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; + } + + /* + * This routine will skip to a matching closing token. + * It will also handle nested tokens like the (, ) below. + * ( name varchar(30), text binary(10) ) + */ + if (isType (token, open_token)) + { + 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); + } +} + +static void 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; + } + + // TypeName = QualifiedIdent. + // QualifiedIdent = [ PackageName "." ] identifier . + // PackageName = identifier . + if (isType (token, TOKEN_IDENTIFIER)) + { + readToken (token); + if (isType (token, TOKEN_DOT)) + { + readToken (token); + if (isType (token, TOKEN_IDENTIFIER)) + readToken (token); + } + return; + } + + // StructType = "struct" "{" { FieldDecl ";" } "}" + // InterfaceType = "interface" "{" { MethodSpec ";" } "}" . + if (isKeyword (token, KEYWORD_struct) || isKeyword (token, KEYWORD_interface)) + { + readToken (token); + // skip over "{}" + skipToMatched (token); + return; + } + + // ArrayType = "[" ArrayLength "]" ElementType . + // SliceType = "[" "]" ElementType . + // ElementType = Type . + if (isType (token, TOKEN_OPEN_SQUARE)) + { + skipToMatched (token); + goto again; + } + + // PointerType = "*" BaseType . + // BaseType = Type . + // ChannelType = ( "chan" [ "<-" ] | "<-" "chan" ) ElementType . + if (isType (token, TOKEN_STAR) || isKeyword (token, KEYWORD_chan) || isType (token, TOKEN_LEFT_ARROW)) + { + readToken (token); + goto again; + } + + // MapType = "map" "[" KeyType "]" ElementType . + // KeyType = Type . + if (isKeyword (token, KEYWORD_map)) + { + readToken (token); + // skip over "[]" + skipToMatched (token); + goto again; + } + + // FunctionType = "func" Signature . + // Signature = Parameters [ Result ] . + // Result = Parameters | Type . + // Parameters = "(" [ ParameterList [ "," ] ] ")" . + if (isKeyword (token, KEYWORD_func)) + { + readToken (token); + // Parameters, skip over "()" + skipToMatched (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; + } +} + +static void makeTag (tokenInfo *const token, const goKind kind) +{ + const char *const name = vStringValue (token->string); + + tagEntryInfo e; + initTagEntry (&e, name); + + if (!GoKinds [kind].enabled) + return; + + e.lineNumber = token->lineNumber; + e.filePosition = token->filePosition; + e.kindName = GoKinds [kind].name; + e.kind = GoKinds [kind].letter; + + makeTagEntry (&e); + + if (scope && Option.include.qualifiedTags) + { + vString *qualifiedName = vStringNew (); + vStringCopy (qualifiedName, scope); + vStringCatS (qualifiedName, "."); + vStringCat (qualifiedName, token->string); + e.name = vStringValue (qualifiedName); + makeTagEntry (&e); + vStringDelete (qualifiedName); + } +} + +static void parsePackage (tokenInfo *const token) +{ + tokenInfo *const name = newToken (); + + readToken (name); + if (isType (name, TOKEN_IDENTIFIER)) + { + makeTag (name, GOTAG_PACKAGE); + if (!scope && Option.include.qualifiedTags) + { + scope = vStringNew (); + vStringCopy (scope, name->string); + } + } + + deleteToken (name); +} + +static void parseFunctionOrMethod (tokenInfo *const token) +{ + // FunctionDecl = "func" identifier Signature [ Body ] . + // Body = Block. + // + // MethodDecl = "func" Receiver MethodName Signature [ Body ] . + // Receiver = "(" [ identifier ] [ "*" ] BaseTypeName ")" . + // BaseTypeName = identifier . + tokenInfo *const name = newToken (); + + // Skip over receiver. + readToken (name); + if (isType (name, TOKEN_OPEN_PAREN)) + skipToMatched (name); + + if (isType (name, TOKEN_IDENTIFIER)) + { + // Skip over parameters. + readToken (token); + skipToMatched (token); + + // Skip over result. + skipType (token); + + // Skip over function body. + if (isType (token, TOKEN_OPEN_CURLY)) + skipToMatched (token); + + makeTag (name, GOTAG_FUNCTION); + } + + deleteToken (name); +} + +static void parseConstTypeVar (tokenInfo *const token, goKind kind) +{ + // ConstDecl = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) . + // ConstSpec = IdentifierList [ [ Type ] "=" ExpressionList ] . + // IdentifierList = identifier { "," identifier } . + // ExpressionList = Expression { "," Expression } . + // TypeDecl = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) . + // TypeSpec = identifier Type . + // VarDecl = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) . + // VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) . + tokenInfo *const name = newToken (); + boolean usesParens = FALSE; + + readToken (name); + + if (isType (name, TOKEN_OPEN_PAREN)) + { + usesParens = TRUE; + readToken (name); + } + +again: + while (1) + { + if (isType (name, TOKEN_IDENTIFIER)) + { + makeTag (name, kind); + readToken (token); + } + if (!isType (token, TOKEN_COMMA)) + break; + readToken (name); + } + + skipType (token); + while (!isType (token, TOKEN_SEMICOLON) && !isType (token, TOKEN_CLOSE_PAREN) + && !isType (token, TOKEN_EOF)) + { + readToken (token); + skipToMatched (token); + } + + if (usesParens) + { + if (!isType (token, TOKEN_CLOSE_PAREN)) // we are at TOKEN_SEMICOLON + { + readToken (name); + if (!isType (name, TOKEN_CLOSE_PAREN) && !isType (name, TOKEN_EOF)) + goto again; + } + } + + deleteToken (name); +} + +static void parseGoFile (tokenInfo *const token) +{ + do + { + readToken (token); + + if (isType (token, TOKEN_KEYWORD)) + { + switch (token->keyword) + { + case KEYWORD_package: + parsePackage (token); + break; + case KEYWORD_func: + parseFunctionOrMethod (token); + break; + case KEYWORD_const: + parseConstTypeVar (token, GOTAG_CONST); + break; + case KEYWORD_type: + parseConstTypeVar (token, GOTAG_TYPE); + break; + case KEYWORD_var: + parseConstTypeVar (token, GOTAG_VAR); + break; + default: + break; + } + } + else if (isType (token, TOKEN_OPEN_PAREN) || isType (token, TOKEN_OPEN_CURLY) || + isType (token, TOKEN_OPEN_SQUARE)) + { + skipToMatched (token); + } + } while (token->type != TOKEN_EOF); +} + +static void findGoTags (void) +{ + tokenInfo *const token = newToken (); + + parseGoFile (token); + + deleteToken (token); + vStringDelete (scope); + scope = NULL; +} + +extern parserDefinition *GoParser (void) +{ + static const char *const extensions[] = { "go", NULL }; + parserDefinition *def = parserNew ("Go"); + def->kinds = GoKinds; + def->kindCount = KIND_COUNT (GoKinds); + def->extensions = extensions; + def->parser = findGoTags; + def->initialize = initialize; + return def; +} diff --git a/tagmanager/ctags/makefile.win32 b/tagmanager/ctags/makefile.win32 index 598620945..17a869d1f 100644 --- a/tagmanager/ctags/makefile.win32 +++ b/tagmanager/ctags/makefile.win32 @@ -49,7 +49,7 @@ actionscript.o nsis.o objc.o \ haskell.o haxe.o html.o python.o lregex.o asciidoc.o rest.o sh.o ctags.o entry.o get.o keyword.o nestlevel.o \ options.o \ parse.o basic.o read.o sort.o strlist.o latex.o markdown.o matlab.o docbook.o tcl.o ruby.o rust.o asm.o sql.o txt2tags.o css.o \ -vstring.o r.o +vstring.o r.o go.o $(AR) rc $@ $^ $(RANLIB) $@ diff --git a/tagmanager/ctags/parsers.h b/tagmanager/ctags/parsers.h index 8597ac0b9..28f7309ba 100644 --- a/tagmanager/ctags/parsers.h +++ b/tagmanager/ctags/parsers.h @@ -61,7 +61,8 @@ ObjcParser, \ AsciidocParser, \ AbaqusParser, \ - RustParser + RustParser, \ + GoParser #endif /* _PARSERS_H */ diff --git a/tagmanager/src/tm_parser.h b/tagmanager/src/tm_parser.h index 1b90e66d3..4f1f780ec 100644 --- a/tagmanager/src/tm_parser.h +++ b/tagmanager/src/tm_parser.h @@ -68,6 +68,7 @@ typedef enum TM_PARSER_ASCIIDOC, TM_PARSER_ABAQUS, TM_PARSER_RUST, + TM_PARSER_GO, TM_PARSER_COUNT } TMParserType; diff --git a/tests/ctags/Makefile.am b/tests/ctags/Makefile.am index 009d67161..d6e2dd7a5 100644 --- a/tests/ctags/Makefile.am +++ b/tests/ctags/Makefile.am @@ -242,6 +242,7 @@ test_sources = \ strings.rb \ structure.f \ tabindent.py \ + test.go \ test.py \ test.vhd \ test_input.rs \ diff --git a/tests/ctags/test.go b/tests/ctags/test.go new file mode 100644 index 000000000..b72720d42 --- /dev/null +++ b/tests/ctags/test.go @@ -0,0 +1,47 @@ +package main + +import "fmt" + +type ( + T1 map[string]int + T2 <-chan float32 + T3 chan []string + T4 chan<- *[12]string + T5 interface { + Reader() + Writer() + foo() + } +) + +type T6 struct { + a, b, c, d int + e float32 + //ignored int +} + +const (A = iota;B;C; + D = iota << (1 + iota*2) + E + F=3.14*(1+2*3)/34e7;I=1) + +type (T7 func (a struct{_ int; _ float32}, b int) (int, map[string]int);T8 float32) + +var (a, b, c int +d T5 +e T4 +f interface{}) + +func f1() {};func f2() {};type/*no newline here*/T9 int/*var ignored int +const ignored int*/const (G=6); var g int + +func (t *T1) f3() (a, b int){ + return 1, 2 +}; var h int + +func (tt * T7) f4(a func () func ()) (func (), int) {return func (){}, 1};func f5(){};const H=1 + +func main() { + go func (){}() + fmt.Println("Hello, 世界") +} diff --git a/tests/ctags/test.go.tags b/tests/ctags/test.go.tags new file mode 100644 index 000000000..83b805e81 --- /dev/null +++ b/tests/ctags/test.go.tags @@ -0,0 +1,34 @@ +# format=tagmanager +AÌ65536Ö0 +BÌ65536Ö0 +CÌ65536Ö0 +DÌ65536Ö0 +EÌ65536Ö0 +FÌ65536Ö0 +GÌ65536Ö0 +HÌ65536Ö0 +IÌ65536Ö0 +T1Ì4096Ö0 +T2Ì4096Ö0 +T3Ì4096Ö0 +T4Ì4096Ö0 +T5Ì4096Ö0 +T6Ì4096Ö0 +T7Ì4096Ö0 +T8Ì4096Ö0 +T9Ì4096Ö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 +gÌ16384Ö0 +hÌ16384Ö0 +mainÌ16Ö0 +mainÌ256Ö0 diff --git a/wscript b/wscript index 343b68e0a..493b8a7bd 100644 --- a/wscript +++ b/wscript @@ -82,6 +82,7 @@ ctags_sources = set([ 'tagmanager/ctags/entry.c', 'tagmanager/ctags/fortran.c', 'tagmanager/ctags/get.c', + 'tagmanager/ctags/go.c', 'tagmanager/ctags/haskell.c', 'tagmanager/ctags/haxe.c', 'tagmanager/ctags/html.c', -- 2.11.4.GIT