From c84eb2c1c978bf1af165630208f0df596e07065e Mon Sep 17 00:00:00 2001 From: SiegeLord Date: Thu, 17 Oct 2013 20:00:42 -0400 Subject: [PATCH 01/12] Add a Rust ctags generator. Thanks to Colomban Wendling for reviewing and finding and fixing many issues. --- tagmanager/ctags/Makefile.am | 1 + tagmanager/ctags/parsers.h | 4 +- tagmanager/ctags/rust.c | 909 +++++++++++++++++++++++++++++++++++ 3 files changed, 913 insertions(+), 1 deletion(-) create mode 100644 tagmanager/ctags/rust.c diff --git a/tagmanager/ctags/Makefile.am b/tagmanager/ctags/Makefile.am index 25184ef9a1..a110b920c1 100644 --- a/tagmanager/ctags/Makefile.am +++ b/tagmanager/ctags/Makefile.am @@ -43,6 +43,7 @@ parsers = \ r.c \ rest.c \ ruby.c \ + rust.c \ sh.c \ sql.c \ tcl.c \ diff --git a/tagmanager/ctags/parsers.h b/tagmanager/ctags/parsers.h index e572a51c8f..fc3ed1d3a1 100644 --- a/tagmanager/ctags/parsers.h +++ b/tagmanager/ctags/parsers.h @@ -59,7 +59,8 @@ CobolParser, \ ObjcParser, \ AsciidocParser, \ - AbaqusParser + AbaqusParser, \ + RustParser /* langType of each parser 0 CParser @@ -107,6 +108,7 @@ langType of each parser 42 ObjcParser 43 AsciidocParser 44 AbaqusParser +45 Rust */ #endif /* _PARSERS_H */ diff --git a/tagmanager/ctags/rust.c b/tagmanager/ctags/rust.c new file mode 100644 index 0000000000..7b92c5feca --- /dev/null +++ b/tagmanager/ctags/rust.c @@ -0,0 +1,909 @@ +/* +* +* This source code is released for free distribution under the terms of the +* GNU General Public License. +* +* This module contains functions for generating tags for Rust files. +*/ + +/* +* INCLUDE FILES +*/ +#include "general.h" /* must always come first */ +#include "main.h" + +#include + +#include "keyword.h" +#include "parse.h" +#include "entry.h" +#include "options.h" +#include "read.h" +#include "vstring.h" + +/* +* MACROS +*/ +#define MAX_STRING_LENGTH 64 + +/* +* DATA DECLARATIONS +*/ + +typedef enum { + K_MOD, + K_STRUCT, + K_TRAIT, + K_IMPL, + K_FN, + K_ENUM, + K_TYPE, + K_STATIC, + K_MACRO, + K_FIELD, + K_VARIANT, + K_METHOD, + K_NONE +} RustKind; + +static kindOption rustKinds[] = { + {TRUE, 'n', "namespace", "module"}, + {TRUE, 's', "struct", "structural type"}, + {TRUE, 'i', "interface", "trait interface"}, + {TRUE, 'c', "class", "implementation"}, + {TRUE, 'f', "function", "Function"}, + {TRUE, 'g', "enum", "Enum"}, + {TRUE, 't', "typedef", "Type Alias"}, + {TRUE, 'v', "variable", "Global variable"}, + {TRUE, 'M', "macro", "Macro Definition"}, + {TRUE, 'm', "field", "A struct field"}, + {TRUE, 'e', "enumerator", "An enum variant"}, + {TRUE, 'F', "method", "A method"}, +}; + +typedef enum { + TOKEN_WHITESPACE, + TOKEN_STRING, + TOKEN_IDENT, + TOKEN_LSHIFT, + TOKEN_RSHIFT, + TOKEN_EOF +} tokenType; + +typedef struct { + /* Characters */ + int cur_c; + int next_c; + + /* Tokens */ + int cur_token; + vString* token_str; + unsigned long line; + MIOPos pos; +} lexerState; + +/* +* FUNCTION PROTOTYPES +*/ + +static void parseBlock (lexerState *lexer, boolean delim, int kind, vString *scope); + +/* +* FUNCTION DEFINITIONS +*/ + +/* Resets the scope string to the old length */ +static void resetScope (vString *scope, size_t old_len) +{ + scope->length = old_len; + scope->buffer[old_len] = '\0'; +} + +/* Adds a name to the end of the scope string */ +static void addToScope (vString *scope, vString *name) +{ + if (vStringLength(scope) > 0) + vStringCatS(scope, "::"); + vStringCat(scope, name); +} + +/* Write the lexer's current token to string, taking care of special tokens */ +static void writeCurTokenToStr (lexerState *lexer, vString *out_str) +{ + switch (lexer->cur_token) + { + case TOKEN_IDENT: + vStringCat(out_str, lexer->token_str); + break; + case TOKEN_STRING: + vStringPut(out_str, '"'); + vStringCat(out_str, lexer->token_str); + vStringPut(out_str, '"'); + break; + case TOKEN_WHITESPACE: + vStringPut(out_str, ' '); + break; + case TOKEN_LSHIFT: + vStringCatS(out_str, "<<"); + break; + case TOKEN_RSHIFT: + vStringCatS(out_str, ">>"); + break; + default: + vStringPut(out_str, (char) lexer->cur_token); + } +} + +/* Reads a character from the file */ +static void advanceChar (lexerState *lexer) +{ + lexer->cur_c = lexer->next_c; + lexer->next_c = fileGetc(); +} + +/* Reads N characters from the file */ +static void advanceNChar (lexerState *lexer, int n) +{ + while (n--) + advanceChar(lexer); +} + + +static boolean isWhitespace (int c) +{ + return c == ' ' || c == '\t' || c == '\r' || c == '\n'; +} + +static boolean isAscii (int c) +{ + return (c >= 0) && (c < 0x80); +} + +/* This isn't quite right for Unicode identifiers */ +static boolean isIdentifierStart (int c) +{ + return (isAscii(c) && (isalpha(c) || c == '_')) || !isAscii(c); +} + +/* This isn't quite right for Unicode identifiers */ +static boolean isIdentifierContinue (int c) +{ + return (isAscii(c) && (isalnum(c) || c == '_')) || !isAscii(c); +} + +static void scanWhitespace (lexerState *lexer) +{ + while (!fileEOF() && isWhitespace(lexer->cur_c)) + advanceChar(lexer); +} + +/* Normal line comments start with two /'s and continue until the next \n + * (NOT any other newline character!). Additionally, a shebang in the beginning + * of the file also counts as a line comment. + * Block comments start with / followed by a * and end with a * followed by a /. + * Unlike in C/C++ they nest. */ +static void scanComments (lexerState *lexer) +{ + /* // or #! */ + if (lexer->next_c == '/' || lexer->next_c == '!') + { + advanceNChar(lexer, 2); + while (!fileEOF() && lexer->cur_c != '\n') + advanceChar(lexer); + } + else if (lexer->next_c == '*') + { + int level = 1; + advanceNChar(lexer, 2); + while (!fileEOF() && level > 0) + { + if (lexer->cur_c == '*' && lexer->next_c == '/') + { + level--; + advanceNChar(lexer, 2); + } + else if (lexer->cur_c == '/' && lexer->next_c == '*') + { + level++; + advanceNChar(lexer, 2); + } + else + { + advanceChar(lexer); + } + } + } +} + +static void scanIdentifier (lexerState *lexer) +{ + vStringClear(lexer->token_str); + do + { + vStringPut(lexer->token_str, (char) lexer->cur_c); + advanceChar(lexer); + } while(!fileEOF() && isIdentifierContinue(lexer->cur_c)); +} + +/* Double-quoted strings, we only care about the \" escape. These + * last past the end of the line, so be careful not too store too much + * of them (see MAX_STRING_LENGTH). The only place we look at their + * contents is in the function definitions, and there the valid strings are + * things like "C" and "Rust" */ +static void scanString (lexerState *lexer) +{ + vStringClear(lexer->token_str); + advanceChar(lexer); + while (!fileEOF() && lexer->cur_c != '"') + { + if (lexer->cur_c == '\\' && lexer->next_c == '"') + advanceChar(lexer); + if (vStringLength(lexer->token_str) < MAX_STRING_LENGTH) + vStringPut(lexer->token_str, (char) lexer->cur_c); + advanceChar(lexer); + } + advanceChar(lexer); +} + +/* Raw strings look like this: r"" or r##""## where the number of + * hashes must match */ +static void scanRawString (lexerState *lexer) +{ + size_t num_initial_hashes = 0; + vStringClear(lexer->token_str); + advanceChar(lexer); + /* Count how many leading hashes there are */ + while (!fileEOF() && lexer->cur_c == '#') + { + num_initial_hashes++; + advanceChar(lexer); + } + if (lexer->cur_c != '"') + return; + advanceChar(lexer); + while (!fileEOF()) + { + if (vStringLength(lexer->token_str) < MAX_STRING_LENGTH) + vStringPut(lexer->token_str, (char) lexer->cur_c); + /* Count how many trailing hashes there are. If the number is equal or more + * than the number of leading hashes, break. */ + if (lexer->cur_c == '"') + { + size_t num_trailing_hashes = 0; + advanceChar(lexer); + while (!fileEOF() && lexer->cur_c == '#' && num_trailing_hashes < num_initial_hashes) + { + num_trailing_hashes++; + + if (vStringLength(lexer->token_str) < MAX_STRING_LENGTH) + vStringPut(lexer->token_str, (char) lexer->cur_c); + advanceChar(lexer); + } + if (num_trailing_hashes == num_initial_hashes) + { + /* Strip the trailing hashes and quotes */ + if (vStringLength(lexer->token_str) < MAX_STRING_LENGTH && vStringLength(lexer->token_str) > num_trailing_hashes + 1) + { + lexer->token_str->length = vStringLength(lexer->token_str) - num_trailing_hashes - 1; + lexer->token_str->buffer[lexer->token_str->length] = '\0'; + } + break; + } + } + else + { + advanceChar(lexer); + } + } +} + +/* Advances the parser one token, optionally skipping whitespace + * (otherwise it is concatenated and returned as a single whitespace token). + * Whitespace is needed to properly render function signatures. Unrecognized + * token starts are stored literally, e.g. token may equal to a character '#'. */ +static int advanceToken (lexerState *lexer, boolean skip_whitspace) +{ + boolean have_whitespace = FALSE; + lexer->line = getSourceLineNumber(); + lexer->pos = getInputFilePosition(); + while (!fileEOF()) + { + if (isWhitespace(lexer->cur_c)) + { + scanWhitespace(lexer); + have_whitespace = TRUE; + } + else if (lexer->cur_c == '/' && (lexer->next_c == '/' || lexer->next_c == '*')) + { + scanComments(lexer); + have_whitespace = TRUE; + } + else + { + if (have_whitespace && !skip_whitspace) + return lexer->cur_token = TOKEN_WHITESPACE; + break; + } + } + lexer->line = getSourceLineNumber(); + lexer->pos = getInputFilePosition(); + while (!fileEOF()) + { + if (lexer->cur_c == '"') + { + scanString(lexer); + return lexer->cur_token = TOKEN_STRING; + } + else if (lexer->cur_c == 'r' && (lexer->next_c == '#' || lexer->next_c == '"')) + { + scanRawString(lexer); + return lexer->cur_token = TOKEN_STRING; + } + else if (isIdentifierStart(lexer->cur_c)) + { + scanIdentifier(lexer); + return lexer->cur_token = TOKEN_IDENT; + } + /* These shift tokens aren't too important for tag-generation per se, + * but they confuse the skipUntil code which tracks the <> pairs. */ + else if (lexer->cur_c == '>' && lexer->next_c == '>') + { + advanceNChar(lexer, 2); + return lexer->cur_token = TOKEN_RSHIFT; + } + else if (lexer->cur_c == '<' && lexer->next_c == '<') + { + advanceNChar(lexer, 2); + return lexer->cur_token = TOKEN_LSHIFT; + } + else + { + int c = lexer->cur_c; + advanceChar(lexer); + return lexer->cur_token = c; + } + } + return lexer->cur_token = TOKEN_EOF; +} + +static void initLexer (lexerState *lexer) +{ + advanceNChar(lexer, 2); + lexer->token_str = vStringNew(); + + if (lexer->cur_c == '#' && lexer->next_c == '!') + scanComments(lexer); + advanceToken(lexer, TRUE); +} + +static void deInitLexer (lexerState *lexer) +{ + vStringDelete(lexer->token_str); + lexer->token_str = NULL; +} + +static void addTag (vString* ident, const char* type, const char* arg_list, int kind, unsigned long line, MIOPos pos, vString *scope, int parent_kind) +{ + if (kind == K_NONE) + return; + tagEntryInfo tag; + initTagEntry(&tag, ident->buffer); + + tag.lineNumber = line; + tag.filePosition = pos; + tag.sourceFileName = getSourceFileName(); + + tag.kindName = rustKinds[kind].name; + tag.kind = rustKinds[kind].letter; + + tag.extensionFields.arglist = arg_list; + tag.extensionFields.varType = type; + if (parent_kind != K_NONE) + { + tag.extensionFields.scope[0] = rustKinds[parent_kind].name; + tag.extensionFields.scope[1] = scope->buffer; + } + makeTagEntry(&tag); +} + +/* Skip tokens until one of the goal tokens is hit. Escapes when level = 0 if there are no goal tokens. + * Keeps track of balanced <>'s, ()'s, []'s, and {}'s and ignores the goal tokens within those pairings */ +static void skipUntil (lexerState *lexer, int goal_tokens[], int num_goal_tokens) +{ + int angle_level = 0; + int paren_level = 0; + int brace_level = 0; + int bracket_level = 0; + while (lexer->cur_token != TOKEN_EOF) + { + if (angle_level == 0 && paren_level == 0 && brace_level == 0 + && bracket_level == 0) + { + int ii = 0; + for(ii = 0; ii < num_goal_tokens; ii++) + { + if (lexer->cur_token == goal_tokens[ii]) + { + break; + } + } + if (ii < num_goal_tokens) + break; + } + switch (lexer->cur_token) + { + case '<': + angle_level++; + break; + case '(': + paren_level++; + break; + case '{': + brace_level++; + break; + case '[': + bracket_level++; + break; + case '>': + angle_level--; + break; + case ')': + paren_level--; + break; + case '}': + brace_level--; + break; + case ']': + bracket_level--; + break; + case TOKEN_RSHIFT: + if (angle_level >= 2) + angle_level -= 2; + break; + /* TOKEN_LSHIFT is never interpreted as two <'s in valid Rust code */ + default: + break; + } + /* Has to be after the token switch to catch the case when we start with the initial level token */ + if (num_goal_tokens == 0 && angle_level == 0 && paren_level == 0 && brace_level == 0 + && bracket_level == 0) + break; + advanceToken(lexer, TRUE); + } +} + +/* Function format: + * "fn" [] "(" [] ")" ["->" ] "{" [] "}"*/ +static void parseFn (lexerState *lexer, vString *scope, int parent_kind) +{ + int kind = (parent_kind == K_TRAIT || parent_kind == K_IMPL) ? K_METHOD : K_FN; + vString *name; + vString *arg_list; + unsigned long line; + MIOPos pos; + int paren_level = 0; + boolean found_paren = FALSE; + boolean valid_signature = TRUE; + + advanceToken(lexer, TRUE); + if (lexer->cur_token != TOKEN_IDENT) + return; + + name = vStringNewCopy(lexer->token_str); + arg_list = vStringNew(); + + line = lexer->line; + pos = lexer->pos; + + advanceToken(lexer, TRUE); + + /* HACK: This is a bit coarse as far as what tag entry means by + * 'arglist'... */ + while (lexer->cur_token != '{' && lexer->cur_token != ';') + { + if (lexer->cur_token == '}') + { + valid_signature = FALSE; + break; + } + else if (lexer->cur_token == '(') + { + found_paren = TRUE; + paren_level++; + } + else if (lexer->cur_token == ')') + { + paren_level--; + if (paren_level < 0) + { + valid_signature = FALSE; + break; + } + } + else if (lexer->cur_token == TOKEN_EOF) + { + valid_signature = FALSE; + break; + } + writeCurTokenToStr(lexer, arg_list); + advanceToken(lexer, FALSE); + } + if (!found_paren || paren_level != 0) + valid_signature = FALSE; + + if (valid_signature) + { + vStringStripTrailing(arg_list); + addTag(name, NULL, arg_list->buffer, kind, line, pos, scope, parent_kind); + addToScope(scope, name); + parseBlock(lexer, TRUE, kind, scope); + } + + vStringDelete(name); + vStringDelete(arg_list); +} + +/* Mod format: + * "mod" "{" [] "}" + * "mod" ";"*/ +static void parseMod (lexerState *lexer, vString *scope, int parent_kind) +{ + advanceToken(lexer, TRUE); + if (lexer->cur_token != TOKEN_IDENT) + return; + + addTag(lexer->token_str, NULL, NULL, K_MOD, lexer->line, lexer->pos, scope, parent_kind); + addToScope(scope, lexer->token_str); + + advanceToken(lexer, TRUE); + + parseBlock(lexer, TRUE, K_MOD, scope); +} + +/* Trait format: + * "trait" [] "{" [] "}" + */ +static void parseTrait (lexerState *lexer, vString *scope, int parent_kind) +{ + int goal_tokens[] = {'{'}; + + advanceToken(lexer, TRUE); + if (lexer->cur_token != TOKEN_IDENT) + return; + + addTag(lexer->token_str, NULL, NULL, K_TRAIT, lexer->line, lexer->pos, scope, parent_kind); + addToScope(scope, lexer->token_str); + + advanceToken(lexer, TRUE); + + skipUntil(lexer, goal_tokens, 1); + + parseBlock(lexer, TRUE, K_TRAIT, scope); +} + +/* Skips type blocks of the form , ...> */ +static void skipTypeBlock (lexerState *lexer) +{ + if (lexer->cur_token == '<') + { + skipUntil(lexer, NULL, 0); + advanceToken(lexer, TRUE); + } +} + +/* Essentially grabs the last ident before 'for', '<' and '{', which + * tends to correspond to what we want as the impl tag entry name */ +static void parseQualifiedType (lexerState *lexer, vString* name) +{ + while (lexer->cur_token != TOKEN_EOF) + { + if (lexer->cur_token == TOKEN_IDENT) + { + if (strcmp(lexer->token_str->buffer, "for") == 0) + break; + vStringClear(name); + vStringCat(name, lexer->token_str); + } + else if (lexer->cur_token == '<' || lexer->cur_token == '{') + { + break; + } + advanceToken(lexer, TRUE); + } + skipTypeBlock(lexer); +} + +/* Impl format: + * "impl" [] [] ["for" []] "{" [] "}" + */ +static void parseImpl (lexerState *lexer, vString *scope, int parent_kind) +{ + unsigned long line; + MIOPos pos; + vString *name; + + advanceToken(lexer, TRUE); + + line = lexer->line; + pos = lexer->pos; + + skipTypeBlock(lexer); + + name = vStringNew(); + + parseQualifiedType(lexer, name); + + if (lexer->cur_token == TOKEN_IDENT && strcmp(lexer->token_str->buffer, "for") == 0) + { + advanceToken(lexer, TRUE); + parseQualifiedType(lexer, name); + } + + addTag(name, NULL, NULL, K_IMPL, line, pos, scope, parent_kind); + addToScope(scope, name); + + parseBlock(lexer, TRUE, K_IMPL, scope); + + vStringDelete(name); +} + +/* Static format: + * "static" ["mut"] + */ +static void parseStatic (lexerState *lexer, vString *scope, int parent_kind) +{ + advanceToken(lexer, TRUE); + if (lexer->cur_token != TOKEN_IDENT) + return; + if (strcmp(lexer->token_str->buffer, "mut") == 0) + { + advanceToken(lexer, TRUE); + } + if (lexer->cur_token != TOKEN_IDENT) + return; + + addTag(lexer->token_str, NULL, NULL, K_STATIC, lexer->line, lexer->pos, scope, parent_kind); +} + +/* Type format: + * "type" + */ +static void parseType (lexerState *lexer, vString *scope, int parent_kind) +{ + advanceToken(lexer, TRUE); + if (lexer->cur_token != TOKEN_IDENT) + return; + + addTag(lexer->token_str, NULL, NULL, K_TYPE, lexer->line, lexer->pos, scope, parent_kind); +} + +/* Structs and enums are very similar syntax-wise. + * It is possible to parse variants a bit more cleverly (e.g. make tuple variants functions and + * struct variants structs) but it'd be too clever and the signature wouldn't make too much sense without + * the enum's definition (e.g. for the type bounds) + * + * Struct/Enum format: + * "struct/enum" [] "{" [,]+ "}" + * "struct/enum" [] ";" + * */ +static void parseStructOrEnum (lexerState *lexer, vString *scope, int parent_kind, boolean is_struct) +{ + int kind = is_struct ? K_STRUCT : K_ENUM; + int field_kind = is_struct ? K_FIELD : K_VARIANT; + int goal_tokens1[] = {';', '{'}; + + advanceToken(lexer, TRUE); + if (lexer->cur_token != TOKEN_IDENT) + return; + + addTag(lexer->token_str, NULL, NULL, kind, lexer->line, lexer->pos, scope, parent_kind); + addToScope(scope, lexer->token_str); + + skipUntil(lexer, goal_tokens1, 2); + + if (lexer->cur_token == '{') + { + vString *field_name = vStringNew(); + while (lexer->cur_token != TOKEN_EOF) + { + if (lexer->cur_token == TOKEN_IDENT) + { + int goal_tokens2[] = {'}', ','}; + if (strcmp(lexer->token_str->buffer, "priv") == 0) + { + advanceToken(lexer, TRUE); + if (lexer->cur_token != TOKEN_IDENT) + { + /* Something's up with this field, skip to the next one */ + skipUntil(lexer, goal_tokens2, 2); + continue; + } + } + + vStringClear(field_name); + vStringCat(field_name, lexer->token_str); + addTag(field_name, NULL, NULL, field_kind, lexer->line, lexer->pos, scope, kind); + skipUntil(lexer, goal_tokens2, 2); + } + if (lexer->cur_token == '}') + { + advanceToken(lexer, TRUE); + break; + } + advanceToken(lexer, TRUE); + } + vStringDelete(field_name); + } +} + +/* Skip the body of the macro. Can't use skipUntil here as + * the body of the macro may have arbitrary code which confuses it (e.g. + * bitshift operators/function return arrows) */ +static void skipMacro (lexerState *lexer) +{ + int level = 0; + int plus_token = 0; + int minus_token = 0; + + advanceToken(lexer, TRUE); + if (lexer->cur_token == '(') + { + plus_token = '('; + minus_token = ')'; + } + else if (lexer->cur_token == '{') + { + plus_token = '{'; + minus_token = '}'; + } + else + { + return; + } + + while (lexer->cur_token != TOKEN_EOF) + { + if (lexer->cur_token == plus_token) + level++; + else if (lexer->cur_token == minus_token) + level--; + if (level == 0) + break; + advanceToken(lexer, TRUE); + } + advanceToken(lexer, TRUE); +} + +/* + * Macro rules format: + * "macro_rules" "!" + */ +static void parseMacroRules (lexerState *lexer, vString *scope, int parent_kind) +{ + advanceToken(lexer, TRUE); + + if (lexer->cur_token != '!') + return; + + advanceToken(lexer, TRUE); + + if (lexer->cur_token != TOKEN_IDENT) + return; + + addTag(lexer->token_str, NULL, NULL, K_MACRO, lexer->line, lexer->pos, scope, parent_kind); + + skipMacro(lexer); +} + +/* + * Rust is very liberal with nesting, so this function is used pretty much for any block + */ +static void parseBlock (lexerState *lexer, boolean delim, int kind, vString *scope) +{ + int level = 1; + if (delim) + { + if (lexer->cur_token != '{') + return; + advanceToken(lexer, TRUE); + } + while (lexer->cur_token != TOKEN_EOF) + { + if (lexer->cur_token == TOKEN_IDENT) + { + size_t old_scope_len = vStringLength(scope); + if (strcmp(lexer->token_str->buffer, "fn") == 0) + { + parseFn(lexer, scope, kind); + } + else if(strcmp(lexer->token_str->buffer, "mod") == 0) + { + parseMod(lexer, scope, kind); + } + else if(strcmp(lexer->token_str->buffer, "static") == 0) + { + parseStatic(lexer, scope, kind); + } + else if(strcmp(lexer->token_str->buffer, "trait") == 0) + { + parseTrait(lexer, scope, kind); + } + else if(strcmp(lexer->token_str->buffer, "type") == 0) + { + parseType(lexer, scope, kind); + } + else if(strcmp(lexer->token_str->buffer, "impl") == 0) + { + parseImpl(lexer, scope, kind); + } + else if(strcmp(lexer->token_str->buffer, "struct") == 0) + { + parseStructOrEnum(lexer, scope, kind, TRUE); + } + else if(strcmp(lexer->token_str->buffer, "enum") == 0) + { + parseStructOrEnum(lexer, scope, kind, FALSE); + } + else if(strcmp(lexer->token_str->buffer, "macro_rules") == 0) + { + parseMacroRules(lexer, scope, kind); + } + else + { + advanceToken(lexer, TRUE); + if (lexer->cur_token == '!') + { + skipMacro(lexer); + } + } + resetScope(scope, old_scope_len); + } + else if (lexer->cur_token == '{') + { + level++; + advanceToken(lexer, TRUE); + } + else if (lexer->cur_token == '}') + { + level--; + advanceToken(lexer, TRUE); + } + else if (lexer->cur_token == '\'') + { + /* Skip over the 'static lifetime, as it confuses the static parser above */ + advanceToken(lexer, TRUE); + if (lexer->cur_token == TOKEN_IDENT && strcmp(lexer->token_str->buffer, "static") == 0) + advanceToken(lexer, TRUE); + } + else + { + advanceToken(lexer, TRUE); + } + if (delim && level <= 0) + break; + } +} + +static void findRustTags (void) +{ + lexerState lexer; + vString* scope = vStringNew(); + initLexer(&lexer); + + parseBlock(&lexer, FALSE, K_NONE, scope); + vStringDelete(scope); + + deInitLexer(&lexer); +} + +extern parserDefinition *RustParser (void) +{ + static const char *const extensions[] = { "rs", NULL }; + parserDefinition *def = parserNew ("Rust"); + def->kinds = rustKinds; + def->kindCount = KIND_COUNT (rustKinds); + def->extensions = extensions; + def->parser = findRustTags; + + return def; +} From ad59468f3e4efb8e49503a1e5e687196f5013ede Mon Sep 17 00:00:00 2001 From: Colomban Wendling Date: Sun, 12 Jan 2014 13:50:45 +0100 Subject: [PATCH 02/12] rust: Don't use fileEOF() as it behaves unexpectedly fileEOF() actually returns TRUE anywhere after the last newline when using the file*() API, which includes a last line without newline. This is an implementation detail which makes this function not usable with the rest of the file*() API. fileEOF() should actually probably be called iFileEOF(), but that's outside the scope of this parser. However, even if fileEOF() did work properly with fileGetc(), the very last byte in the input would have been omitted because we actually read ahead by one byte, which means the actual read reaches EOF one byte before our "current byte" does. Checking whether we reached EOF by checking whether our current byte isn't equal to `EOF` fixes it, and isn't worse since it's actually how fileEOF() is implemented anyway. --- tagmanager/ctags/rust.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tagmanager/ctags/rust.c b/tagmanager/ctags/rust.c index 7b92c5feca..70de97151c 100644 --- a/tagmanager/ctags/rust.c +++ b/tagmanager/ctags/rust.c @@ -173,7 +173,7 @@ static boolean isIdentifierContinue (int c) static void scanWhitespace (lexerState *lexer) { - while (!fileEOF() && isWhitespace(lexer->cur_c)) + while (isWhitespace(lexer->cur_c)) advanceChar(lexer); } @@ -188,14 +188,14 @@ static void scanComments (lexerState *lexer) if (lexer->next_c == '/' || lexer->next_c == '!') { advanceNChar(lexer, 2); - while (!fileEOF() && lexer->cur_c != '\n') + while (lexer->cur_c != EOF && lexer->cur_c != '\n') advanceChar(lexer); } else if (lexer->next_c == '*') { int level = 1; advanceNChar(lexer, 2); - while (!fileEOF() && level > 0) + while (lexer->cur_c != EOF && level > 0) { if (lexer->cur_c == '*' && lexer->next_c == '/') { @@ -222,7 +222,7 @@ static void scanIdentifier (lexerState *lexer) { vStringPut(lexer->token_str, (char) lexer->cur_c); advanceChar(lexer); - } while(!fileEOF() && isIdentifierContinue(lexer->cur_c)); + } while(lexer->cur_c != EOF && isIdentifierContinue(lexer->cur_c)); } /* Double-quoted strings, we only care about the \" escape. These @@ -234,7 +234,7 @@ static void scanString (lexerState *lexer) { vStringClear(lexer->token_str); advanceChar(lexer); - while (!fileEOF() && lexer->cur_c != '"') + while (lexer->cur_c != EOF && lexer->cur_c != '"') { if (lexer->cur_c == '\\' && lexer->next_c == '"') advanceChar(lexer); @@ -253,7 +253,7 @@ static void scanRawString (lexerState *lexer) vStringClear(lexer->token_str); advanceChar(lexer); /* Count how many leading hashes there are */ - while (!fileEOF() && lexer->cur_c == '#') + while (lexer->cur_c == '#') { num_initial_hashes++; advanceChar(lexer); @@ -261,7 +261,7 @@ static void scanRawString (lexerState *lexer) if (lexer->cur_c != '"') return; advanceChar(lexer); - while (!fileEOF()) + while (lexer->cur_c != EOF) { if (vStringLength(lexer->token_str) < MAX_STRING_LENGTH) vStringPut(lexer->token_str, (char) lexer->cur_c); @@ -271,7 +271,7 @@ static void scanRawString (lexerState *lexer) { size_t num_trailing_hashes = 0; advanceChar(lexer); - while (!fileEOF() && lexer->cur_c == '#' && num_trailing_hashes < num_initial_hashes) + while (lexer->cur_c == '#' && num_trailing_hashes < num_initial_hashes) { num_trailing_hashes++; @@ -306,7 +306,7 @@ static int advanceToken (lexerState *lexer, boolean skip_whitspace) boolean have_whitespace = FALSE; lexer->line = getSourceLineNumber(); lexer->pos = getInputFilePosition(); - while (!fileEOF()) + while (lexer->cur_c != EOF) { if (isWhitespace(lexer->cur_c)) { @@ -327,7 +327,7 @@ static int advanceToken (lexerState *lexer, boolean skip_whitspace) } lexer->line = getSourceLineNumber(); lexer->pos = getInputFilePosition(); - while (!fileEOF()) + while (lexer->cur_c != EOF) { if (lexer->cur_c == '"') { From b17b4be1260e8828f62c56c87cf8bf886cbbf3b4 Mon Sep 17 00:00:00 2001 From: dobkeratops Date: Thu, 17 Oct 2013 20:07:18 -0400 Subject: [PATCH 03/12] Added the Rust filetype --- data/filetypes.rust | 51 +++++++++++++++++++++++++++++++++++++++++++++ src/filetypes.c | 9 ++++++++ src/filetypes.h | 1 + 3 files changed, 61 insertions(+) create mode 100644 data/filetypes.rust diff --git a/data/filetypes.rust b/data/filetypes.rust new file mode 100644 index 0000000000..a69cb23fbd --- /dev/null +++ b/data/filetypes.rust @@ -0,0 +1,51 @@ +# For complete documentation of this file, please see Geany's main documentation +[styling=C] + +[keywords] +# all items must be in one line +primary=fn type as break const copy do else enum extern fail for if impl let log loop match mod mut priv pub ref return static struct trait unsafe use while in continue alignof be offsetof pure sizeof typeof yield +secondary=bool int uint i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str Self self + +[lexer_properties] +styling.within.preprocessor=1 +lexer.cpp.track.preprocessor=0 + +[settings] +lexer_filetype=C + +# default extension used when saving files +extension=rs + +# the following characters are these which a "word" can contains, see documentation +#wordchars=_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 + +# single comments, like # in this file +comment_single=// +# multiline comments +comment_open=/* +comment_close=*/ + +# set to false if a comment character/string should start at column 0 of a line, true uses any +# indentation of the line, e.g. setting to true causes the following on pressing CTRL+d + #command_example(); +# setting to false would generate this +# command_example(); +# This setting works only for single line comments +comment_use_indent=true + +# context action command (please see Geany's main documentation for details) +context_action_cmd= + +[indentation] +#width=4 +# 0 is spaces, 1 is tabs, 2 is tab & spaces +#type=1 + +[build_settings] +# %f will be replaced by the complete filename +# %e will be replaced by the filename without extension +# (use only one of it at one time) +compiler=rustc "%f" +linker=rustc -o "%e" "%f" +run_cmd="./%e" + diff --git a/src/filetypes.c b/src/filetypes.c index a971a8165b..b201623a07 100644 --- a/src/filetypes.c +++ b/src/filetypes.c @@ -522,6 +522,15 @@ static void init_builtin_filetypes(void) ft->name = g_strdup("PowerShell"); filetype_make_title(ft, TITLE_SOURCE_FILE); ft->group = GEANY_FILETYPE_GROUP_SCRIPT; + +#define RUST + ft = filetypes[GEANY_FILETYPES_RUST]; + ft->lang = 45; + ft->name = g_strdup("Rust"); + filetype_make_title(ft, TITLE_SOURCE_FILE); + ft->mime_type = g_strdup("text/x-rustsrc"); + ft->group = GEANY_FILETYPE_GROUP_COMPILED; + } diff --git a/src/filetypes.h b/src/filetypes.h index 49fff2fef5..2161f8b055 100644 --- a/src/filetypes.h +++ b/src/filetypes.h @@ -94,6 +94,7 @@ typedef enum GEANY_FILETYPES_ABAQUS, GEANY_FILETYPES_BATCH, GEANY_FILETYPES_POWERSHELL, + GEANY_FILETYPES_RUST, /* ^ append items here */ GEANY_MAX_BUILT_IN_FILETYPES /* Don't use this, use filetypes_array->len instead */ } From 4d33223c9877c81bdb5da0bacb31392e843f4d33 Mon Sep 17 00:00:00 2001 From: SiegeLord Date: Wed, 16 Oct 2013 12:25:57 -0400 Subject: [PATCH 04/12] Import the Rust lexer from Scintilla --- scintilla/Makefile.am | 1 + scintilla/lexers/LexRust.cxx | 768 +++++++++++++++++++++++++++++++++++ scintilla/makefile.win32 | 1 + scintilla/src/Catalogue.cxx | 1 + 4 files changed, 771 insertions(+) create mode 100644 scintilla/lexers/LexRust.cxx diff --git a/scintilla/Makefile.am b/scintilla/Makefile.am index d35411516a..a0673ce9ce 100644 --- a/scintilla/Makefile.am +++ b/scintilla/Makefile.am @@ -36,6 +36,7 @@ lexers/LexPython.cxx \ lexers/LexPO.cxx \ lexers/LexR.cxx \ lexers/LexRuby.cxx \ +lexers/LexRust.cxx \ lexers/LexSQL.cxx \ lexers/LexTCL.cxx \ lexers/LexTxt2tags.cxx \ diff --git a/scintilla/lexers/LexRust.cxx b/scintilla/lexers/LexRust.cxx new file mode 100644 index 0000000000..0d9aa1e5e9 --- /dev/null +++ b/scintilla/lexers/LexRust.cxx @@ -0,0 +1,768 @@ +/** @file LexRust.cxx + ** Lexer for Rust. + ** + ** Copyright (c) 2013 by SiegeLord + ** Converted to lexer object and added further folding features/properties by "Udo Lechner" + **/ +// Copyright 1998-2005 by Neil Hodgson +// The License.txt file describes the conditions under which this software may be distributed. + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ILexer.h" +#include "Scintilla.h" +#include "SciLexer.h" + +#include "WordList.h" +#include "LexAccessor.h" +#include "Accessor.h" +#include "StyleContext.h" +#include "CharacterSet.h" +#include "LexerModule.h" +#include "OptionSet.h" +#include "PropSetSimple.h" + +#ifdef SCI_NAMESPACE +using namespace Scintilla; +#endif + +static bool IsStreamCommentStyle(int style) { + return style == SCE_RUST_COMMENTBLOCK || + style == SCE_RUST_COMMENTBLOCKDOC; +} + +// Options used for LexerRust +struct OptionsRust { + bool fold; + bool foldSyntaxBased; + bool foldComment; + bool foldCommentMultiline; + bool foldCommentExplicit; + std::string foldExplicitStart; + std::string foldExplicitEnd; + bool foldExplicitAnywhere; + bool foldCompact; + int foldAtElseInt; + bool foldAtElse; + OptionsRust() { + fold = false; + foldSyntaxBased = true; + foldComment = false; + foldCommentMultiline = true; + foldCommentExplicit = true; + foldExplicitStart = ""; + foldExplicitEnd = ""; + foldExplicitAnywhere = false; + foldCompact = true; + foldAtElseInt = -1; + foldAtElse = false; + } +}; + +static const char * const rustWordLists[] = { + "Primary keywords and identifiers", + "Built in types", + "Other keywords", + "Keywords 4", + "Keywords 5", + "Keywords 6", + "Keywords 7", + 0, + }; + +struct OptionSetRust : public OptionSet { + OptionSetRust() { + DefineProperty("fold", &OptionsRust::fold); + + DefineProperty("fold.comment", &OptionsRust::foldComment); + + DefineProperty("fold.compact", &OptionsRust::foldCompact); + + DefineProperty("fold.at.else", &OptionsRust::foldAtElse); + + DefineProperty("fold.rust.syntax.based", &OptionsRust::foldSyntaxBased, + "Set this property to 0 to disable syntax based folding."); + + DefineProperty("fold.rust.comment.multiline", &OptionsRust::foldCommentMultiline, + "Set this property to 0 to disable folding multi-line comments when fold.comment=1."); + + DefineProperty("fold.rust.comment.explicit", &OptionsRust::foldCommentExplicit, + "Set this property to 0 to disable folding explicit fold points when fold.comment=1."); + + DefineProperty("fold.rust.explicit.start", &OptionsRust::foldExplicitStart, + "The string to use for explicit fold start points, replacing the standard //{."); + + DefineProperty("fold.rust.explicit.end", &OptionsRust::foldExplicitEnd, + "The string to use for explicit fold end points, replacing the standard //}."); + + DefineProperty("fold.rust.explicit.anywhere", &OptionsRust::foldExplicitAnywhere, + "Set this property to 1 to enable explicit fold points anywhere, not just in line comments."); + + DefineProperty("lexer.rust.fold.at.else", &OptionsRust::foldAtElseInt, + "This option enables Rust folding on a \"} else {\" line of an if statement."); + + DefineWordListSets(rustWordLists); + } +}; + +class LexerRust : public ILexer { + WordList keywords[7]; + OptionsRust options; + OptionSetRust osRust; +public: + virtual ~LexerRust() { + } + void SCI_METHOD Release() { + delete this; + } + int SCI_METHOD Version() const { + return lvOriginal; + } + const char * SCI_METHOD PropertyNames() { + return osRust.PropertyNames(); + } + int SCI_METHOD PropertyType(const char *name) { + return osRust.PropertyType(name); + } + const char * SCI_METHOD DescribeProperty(const char *name) { + return osRust.DescribeProperty(name); + } + int SCI_METHOD PropertySet(const char *key, const char *val); + const char * SCI_METHOD DescribeWordListSets() { + return osRust.DescribeWordListSets(); + } + int SCI_METHOD WordListSet(int n, const char *wl); + void SCI_METHOD Lex(unsigned int startPos, int length, int initStyle, IDocument *pAccess); + void SCI_METHOD Fold(unsigned int startPos, int length, int initStyle, IDocument *pAccess); + void * SCI_METHOD PrivateCall(int, void *) { + return 0; + } + static ILexer *LexerFactoryRust() { + return new LexerRust(); + } +}; + +int SCI_METHOD LexerRust::PropertySet(const char *key, const char *val) { + if (osRust.PropertySet(&options, key, val)) { + return 0; + } + return -1; +} + +int SCI_METHOD LexerRust::WordListSet(int n, const char *wl) { + int firstModification = -1; + if (n < 7) { + WordList *wordListN = &keywords[n]; + WordList wlNew; + wlNew.Set(wl); + if (*wordListN != wlNew) { + wordListN->Set(wl); + firstModification = 0; + } + } + return firstModification; +} + +static bool IsWhitespace(int c) { + return c == ' ' || c == '\t' || c == '\r' || c == '\n'; +} + +/* This isn't quite right for Unicode identifiers */ +static bool IsIdentifierStart(int ch) { + return (IsASCII(ch) && (isalpha(ch) || ch == '_')) || !IsASCII(ch); +} + +/* This isn't quite right for Unicode identifiers */ +static bool IsIdentifierContinue(int ch) { + return (IsASCII(ch) && (isalnum(ch) || ch == '_')) || !IsASCII(ch); +} + +static void ScanWhitespace(Accessor& styler, int& pos, int max) { + while (IsWhitespace(styler.SafeGetCharAt(pos, '\0')) && pos < max) { + if (pos == styler.LineEnd(styler.GetLine(pos))) + styler.SetLineState(styler.GetLine(pos), 0); + pos++; + } + styler.ColourTo(pos-1, SCE_RUST_DEFAULT); +} + +static void GrabString(char* s, Accessor& styler, int start, int len) { + for (int ii = 0; ii < len; ii++) + s[ii] = styler[ii + start]; + s[len] = '\0'; +} + +static void ScanIdentifier(Accessor& styler, int& pos, WordList *keywords) { + int start = pos; + while (IsIdentifierContinue(styler.SafeGetCharAt(pos, '\0'))) + pos++; + + if (styler.SafeGetCharAt(pos, '\0') == '!') { + pos++; + styler.ColourTo(pos - 1, SCE_RUST_MACRO); + } else { + char s[1024]; + int len = pos - start; + len = len > 1024 ? 1024 : len; + GrabString(s, styler, start, len); + bool keyword = false; + for (int ii = 0; ii < 7; ii++) { + if (keywords[ii].InList(s)) { + styler.ColourTo(pos - 1, SCE_RUST_WORD + ii); + keyword = true; + break; + } + } + if (!keyword) { + styler.ColourTo(pos - 1, SCE_RUST_IDENTIFIER); + } + } +} + +static void ScanDigits(Accessor& styler, int& pos, int base) { + for (;;) { + int c = styler.SafeGetCharAt(pos, '\0'); + if (IsADigit(c, base) || c == '_') + pos++; + else + break; + } +} + +static bool ScanExponent(Accessor& styler, int& pos) { + int c = styler.SafeGetCharAt(pos, '\0'); + if (c == 'e' || c == 'E') { + pos++; + c = styler.SafeGetCharAt(pos, '\0'); + if (c == '-' || c == '+') + pos++; + int old_pos = pos; + ScanDigits(styler, pos, 10); + if (old_pos == pos) { + return false; + } + } + return true; +} + +static void ScanNumber(Accessor& styler, int& pos) { + int base = 10; + int c = styler.SafeGetCharAt(pos, '\0'); + int n = styler.SafeGetCharAt(pos + 1, '\0'); + bool error = false; + if (c == '0' && n == 'x') { + pos += 2; + base = 16; + } else if (c == '0' && n == 'b') { + pos += 2; + base = 2; + } + int old_pos = pos; + ScanDigits(styler, pos, base); + c = styler.SafeGetCharAt(pos, '\0'); + if (c == 'u' || c == 'i') { + pos++; + c = styler.SafeGetCharAt(pos, '\0'); + int n = styler.SafeGetCharAt(pos + 1, '\0'); + if (c == '8') { + pos++; + } else if (c == '1' && n == '6') { + pos += 2; + } else if (c == '3' && n == '2') { + pos += 2; + } else if (c == '6' && n == '4') { + pos += 2; + } + } else if (c == '.') { + error = base != 10; + pos++; + ScanDigits(styler, pos, 10); + error |= !ScanExponent(styler, pos); + c = styler.SafeGetCharAt(pos, '\0'); + if (c == 'f') { + pos++; + c = styler.SafeGetCharAt(pos, '\0'); + int n = styler.SafeGetCharAt(pos + 1, '\0'); + if (c == '3' && n == '2') { + pos += 2; + } else if (c == '6' && n == '4') { + pos += 2; + } else { + error = true; + } + } + } + if (old_pos == pos) { + error = true; + } + if (error) + styler.ColourTo(pos - 1, SCE_RUST_LEXERROR); + else + styler.ColourTo(pos - 1, SCE_RUST_NUMBER); +} + +static bool IsOneCharOperator(int c) { + return c == ';' || c == ',' || c == '(' || c == ')' + || c == '{' || c == '}' || c == '[' || c == ']' + || c == '@' || c == '#' || c == '~' || c == '+' + || c == '*' || c == '/' || c == '^' || c == '%' + || c == '.' || c == ':' || c == '!' || c == '<' + || c == '>' || c == '=' || c == '-' || c == '&' + || c == '|' || c == '$'; +} + +static bool IsTwoCharOperator(int c, int n) { + return (c == '.' && n == '.') || (c == ':' && n == ':') + || (c == '!' && n == '=') || (c == '<' && n == '<') + || (c == '<' && n == '=') || (c == '>' && n == '>') + || (c == '>' && n == '=') || (c == '=' && n == '=') + || (c == '=' && n == '>') || (c == '-' && n == '>') + || (c == '&' && n == '&') || (c == '|' && n == '|') + || (c == '-' && n == '=') || (c == '&' && n == '=') + || (c == '|' && n == '=') || (c == '+' && n == '=') + || (c == '*' && n == '=') || (c == '/' && n == '=') + || (c == '^' && n == '=') || (c == '%' && n == '='); +} + +static bool IsThreeCharOperator(int c, int n, int n2) { + return (c == '<' && n == '<' && n2 == '=') + || (c == '>' && n == '>' && n2 == '='); +} + +static bool IsValidCharacterEscape(int c) { + return c == 'n' || c == 'r' || c == 't' || c == '\\' + || c == '\'' || c == '"' || c == '0'; +} + +static bool IsValidStringEscape(int c) { + return IsValidCharacterEscape(c) || c == '\n'; +} + +static bool ScanNumericEscape(Accessor &styler, int& pos, int num_digits, bool stop_asap) { + for (;;) { + int c = styler.SafeGetCharAt(pos, '\0'); + if (!IsADigit(c, 16)) + break; + num_digits--; + pos++; + if (num_digits == 0 && stop_asap) + return true; + } + if (num_digits == 0) { + return true; + } else { + return false; + } +} + +/* This is overly permissive for character literals in order to accept UTF-8 encoded + * character literals. */ +static void ScanCharacterLiteralOrLifetime(Accessor &styler, int& pos) { + pos++; + int c = styler.SafeGetCharAt(pos, '\0'); + int n = styler.SafeGetCharAt(pos + 1, '\0'); + bool done = false; + bool valid_lifetime = IsIdentifierStart(c); + bool valid_char = true; + bool first = true; + while (!done) { + switch (c) { + case '\\': + done = true; + if (IsValidCharacterEscape(n)) { + pos += 2; + } else if (n == 'x') { + pos += 2; + valid_char = ScanNumericEscape(styler, pos, 2, false); + } else if (n == 'u') { + pos += 2; + valid_char = ScanNumericEscape(styler, pos, 4, false); + } else if (n == 'U') { + pos += 2; + valid_char = ScanNumericEscape(styler, pos, 8, false); + } else { + valid_char = false; + } + break; + case '\'': + valid_char = !first; + done = true; + break; + case '\t': + case '\n': + case '\r': + case '\0': + valid_char = false; + done = true; + break; + default: + if (!IsIdentifierContinue(c) && !first) { + done = true; + } else { + pos++; + } + break; + } + c = styler.SafeGetCharAt(pos, '\0'); + n = styler.SafeGetCharAt(pos + 1, '\0'); + + first = false; + } + if (styler.SafeGetCharAt(pos, '\0') == '\'') { + valid_lifetime = false; + } else { + valid_char = false; + } + if (valid_lifetime) { + styler.ColourTo(pos - 1, SCE_RUST_LIFETIME); + } else if (valid_char) { + pos++; + styler.ColourTo(pos - 1, SCE_RUST_CHARACTER); + } else { + styler.ColourTo(pos - 1, SCE_RUST_LEXERROR); + } +} + +enum CommentState { + UnknownComment, + DocComment, + NotDocComment +}; + +/* + * The rule for block-doc comments is as follows (use x for asterisk)... /xx and /x! start doc comments + * unless the entire comment is x's. + */ +static void ResumeBlockComment(Accessor &styler, int& pos, int max, CommentState state) { + int c = styler.SafeGetCharAt(pos, '\0'); + bool maybe_doc_comment = false; + bool any_non_asterisk = false; + if (c == '*' || c == '!') { + maybe_doc_comment = true; + } + for (;;) { + if (pos == styler.LineEnd(styler.GetLine(pos))) + styler.SetLineState(styler.GetLine(pos), 0); + if (c == '*') { + int n = styler.SafeGetCharAt(pos + 1, '\0'); + if (n == '/') { + pos += 2; + if (state == DocComment || (state == UnknownComment && maybe_doc_comment && any_non_asterisk)) + styler.ColourTo(pos - 1, SCE_RUST_COMMENTBLOCKDOC); + else + styler.ColourTo(pos - 1, SCE_RUST_COMMENTBLOCK); + break; + } + } else { + any_non_asterisk = true; + } + if (c == '\0' || pos >= max) { + if (state == DocComment || (state == UnknownComment && maybe_doc_comment)) + styler.ColourTo(pos - 1, SCE_RUST_COMMENTBLOCKDOC); + else + styler.ColourTo(pos - 1, SCE_RUST_COMMENTBLOCK); + break; + } + pos++; + c = styler.SafeGetCharAt(pos, '\0'); + } +} + +/* + * The rule for line-doc comments is as follows... /// and //! start doc comments + * unless the comment is composed entirely of /'s followed by whitespace. That is: + * // - comment + * /// - doc-comment + * //// - comment + * ////a - doc-comment + */ +static void ResumeLineComment(Accessor &styler, int& pos, int max, CommentState state) { + bool maybe_doc_comment = false; + int num_leading_slashes = 0; + int c = styler.SafeGetCharAt(pos, '\0'); + if (c == '/') { + num_leading_slashes = 1; + while (pos < max) { + pos++; + c = styler.SafeGetCharAt(pos, '\0'); + if (c == '/') { + num_leading_slashes++; + } else { + break; + } + } + } else if (c == '!') { + maybe_doc_comment = true; + } + + bool non_white_space = false; + while (pos < max && c != '\n' && c != '\0') { + if (!IsWhitespace(c)) + non_white_space = true; + if (pos == styler.LineEnd(styler.GetLine(pos))) + styler.SetLineState(styler.GetLine(pos), 0); + pos++; + c = styler.SafeGetCharAt(pos, '\0'); + } + + maybe_doc_comment |= num_leading_slashes == 1 || (num_leading_slashes > 1 && non_white_space); + + if (state == DocComment || (state == UnknownComment && maybe_doc_comment)) + styler.ColourTo(pos - 1, SCE_RUST_COMMENTLINEDOC); + else + styler.ColourTo(pos - 1, SCE_RUST_COMMENTLINE); +} + +static void ScanComments(Accessor &styler, int& pos, int max) { + pos++; + int c = styler.SafeGetCharAt(pos, '\0'); + pos++; + if (c == '/') + ResumeLineComment(styler, pos, max, UnknownComment); + else if (c == '*') + ResumeBlockComment(styler, pos, max, UnknownComment); +} + +static void ResumeString(Accessor &styler, int& pos, int max) { + int c = styler.SafeGetCharAt(pos, '\0'); + bool error = false; + while (c != '"' && !error) { + if (c == '\0' || pos >= max) { + error = true; + break; + } + if (pos == styler.LineEnd(styler.GetLine(pos))) + styler.SetLineState(styler.GetLine(pos), 0); + if (c == '\\') { + int n = styler.SafeGetCharAt(pos + 1, '\0'); + if (IsValidStringEscape(n)) { + pos += 2; + } else if (n == 'x') { + pos += 2; + error = !ScanNumericEscape(styler, pos, 2, true); + } else if (n == 'u') { + pos += 2; + error = !ScanNumericEscape(styler, pos, 4, true); + } else if (n == 'U') { + pos += 2; + error = !ScanNumericEscape(styler, pos, 8, true); + } else { + pos += 1; + error = true; + } + } else { + pos++; + } + c = styler.SafeGetCharAt(pos, '\0'); + } + if (!error) + pos++; + styler.ColourTo(pos - 1, SCE_RUST_STRING); +} + +static void ResumeRawString(Accessor &styler, int& pos, int max, int num_hashes) { + for (;;) { + int c = styler.SafeGetCharAt(pos, '\0'); + if (c == '"') { + pos++; + int trailing_num_hashes = 0; + while (styler.SafeGetCharAt(pos, '\0') == '#' && trailing_num_hashes < num_hashes) { + trailing_num_hashes++; + pos++; + } + if (trailing_num_hashes == num_hashes) { + styler.SetLineState(styler.GetLine(pos), 0); + styler.ColourTo(pos - 1, SCE_RUST_STRINGR); + break; + } + } else if (c == '\0' || pos >= max) { + styler.ColourTo(pos - 1, SCE_RUST_STRINGR); + break; + } + if (pos == styler.LineEnd(styler.GetLine(pos))) + styler.SetLineState(styler.GetLine(pos), num_hashes); + pos++; + } +} + +static void ScanRawString(Accessor &styler, int& pos, int max) { + pos++; + int num_hashes = 0; + while (styler.SafeGetCharAt(pos, '\0') == '#') { + num_hashes++; + pos++; + } + if (styler.SafeGetCharAt(pos, '\0') != '"') { + styler.ColourTo(pos - 1, SCE_RUST_LEXERROR); + } else { + pos++; + ResumeRawString(styler, pos, max, num_hashes); + } +} + +void SCI_METHOD LexerRust::Lex(unsigned int startPos, int length, int initStyle, IDocument *pAccess) { + PropSetSimple props; + Accessor styler(pAccess, &props); + int pos = startPos; + int max = pos + length; + + styler.StartAt(pos); + styler.StartSegment(pos); + + if (initStyle == SCE_RUST_COMMENTBLOCK || initStyle == SCE_RUST_COMMENTBLOCKDOC) { + ResumeBlockComment(styler, pos, max, initStyle == SCE_RUST_COMMENTBLOCKDOC ? DocComment : NotDocComment); + } else if (initStyle == SCE_RUST_COMMENTLINE || initStyle == SCE_RUST_COMMENTLINEDOC) { + ResumeLineComment(styler, pos, max, initStyle == SCE_RUST_COMMENTLINEDOC ? DocComment : NotDocComment); + } else if (initStyle == SCE_RUST_STRING) { + ResumeString(styler, pos, max); + } else if (initStyle == SCE_RUST_STRINGR) { + ResumeRawString(styler, pos, max, styler.GetLineState(styler.GetLine(pos) - 1)); + } + + while (pos < max) { + int c = styler.SafeGetCharAt(pos, '\0'); + int n = styler.SafeGetCharAt(pos + 1, '\0'); + int n2 = styler.SafeGetCharAt(pos + 2, '\0'); + + if (pos == 0 && c == '#' && n == '!') { + pos += 2; + ResumeLineComment(styler, pos, max, NotDocComment); + } else if (IsWhitespace(c)) { + ScanWhitespace(styler, pos, max); + } else if (c == '/' && (n == '/' || n == '*')) { + ScanComments(styler, pos, max); + } else if (c == 'r' && (n == '#' || n == '"')) { + ScanRawString(styler, pos, max); + } else if (IsIdentifierStart(c)) { + ScanIdentifier(styler, pos, keywords); + } else if (IsADigit(c)) { + ScanNumber(styler, pos); + } else if (IsThreeCharOperator(c, n, n2)) { + pos += 3; + styler.ColourTo(pos - 1, SCE_RUST_OPERATOR); + } else if (IsTwoCharOperator(c, n)) { + pos += 2; + styler.ColourTo(pos - 1, SCE_RUST_OPERATOR); + } else if (IsOneCharOperator(c)) { + pos++; + styler.ColourTo(pos - 1, SCE_RUST_OPERATOR); + } else if (c == '\'') { + ScanCharacterLiteralOrLifetime(styler, pos); + } else if (c == '"') { + pos++; + ResumeString(styler, pos, max); + } else { + pos++; + styler.ColourTo(pos - 1, SCE_RUST_LEXERROR); + } + } + styler.ColourTo(pos - 1, SCE_RUST_DEFAULT); + styler.Flush(); +} + +void SCI_METHOD LexerRust::Fold(unsigned int startPos, int length, int initStyle, IDocument *pAccess) { + + if (!options.fold) + return; + + LexAccessor styler(pAccess); + + unsigned int endPos = startPos + length; + int visibleChars = 0; + bool inLineComment = false; + int lineCurrent = styler.GetLine(startPos); + int levelCurrent = SC_FOLDLEVELBASE; + if (lineCurrent > 0) + levelCurrent = styler.LevelAt(lineCurrent-1) >> 16; + unsigned int lineStartNext = styler.LineStart(lineCurrent+1); + int levelMinCurrent = levelCurrent; + int levelNext = levelCurrent; + char chNext = styler[startPos]; + int styleNext = styler.StyleAt(startPos); + int style = initStyle; + const bool userDefinedFoldMarkers = !options.foldExplicitStart.empty() && !options.foldExplicitEnd.empty(); + for (unsigned int i = startPos; i < endPos; i++) { + char ch = chNext; + chNext = styler.SafeGetCharAt(i + 1); + int stylePrev = style; + style = styleNext; + styleNext = styler.StyleAt(i + 1); + bool atEOL = i == (lineStartNext-1); + if ((style == SCE_RUST_COMMENTLINE) || (style == SCE_RUST_COMMENTLINEDOC)) + inLineComment = true; + if (options.foldComment && options.foldCommentMultiline && IsStreamCommentStyle(style) && !inLineComment) { + if (!IsStreamCommentStyle(stylePrev)) { + levelNext++; + } else if (!IsStreamCommentStyle(styleNext) && !atEOL) { + // Comments don't end at end of line and the next character may be unstyled. + levelNext--; + } + } + if (options.foldComment && options.foldCommentExplicit && ((style == SCE_RUST_COMMENTLINE) || options.foldExplicitAnywhere)) { + if (userDefinedFoldMarkers) { + if (styler.Match(i, options.foldExplicitStart.c_str())) { + levelNext++; + } else if (styler.Match(i, options.foldExplicitEnd.c_str())) { + levelNext--; + } + } else { + if ((ch == '/') && (chNext == '/')) { + char chNext2 = styler.SafeGetCharAt(i + 2); + if (chNext2 == '{') { + levelNext++; + } else if (chNext2 == '}') { + levelNext--; + } + } + } + } + if (options.foldSyntaxBased && (style == SCE_RUST_OPERATOR)) { + if (ch == '{') { + // Measure the minimum before a '{' to allow + // folding on "} else {" + if (levelMinCurrent > levelNext) { + levelMinCurrent = levelNext; + } + levelNext++; + } else if (ch == '}') { + levelNext--; + } + } + if (!IsASpace(ch)) + visibleChars++; + if (atEOL || (i == endPos-1)) { + int levelUse = levelCurrent; + if (options.foldSyntaxBased && options.foldAtElse) { + levelUse = levelMinCurrent; + } + int lev = levelUse | levelNext << 16; + if (visibleChars == 0 && options.foldCompact) + lev |= SC_FOLDLEVELWHITEFLAG; + if (levelUse < levelNext) + lev |= SC_FOLDLEVELHEADERFLAG; + if (lev != styler.LevelAt(lineCurrent)) { + styler.SetLevel(lineCurrent, lev); + } + lineCurrent++; + lineStartNext = styler.LineStart(lineCurrent+1); + levelCurrent = levelNext; + levelMinCurrent = levelCurrent; + if (atEOL && (i == static_cast(styler.Length()-1))) { + // There is an empty line at end of file so give it same level and empty + styler.SetLevel(lineCurrent, (levelCurrent | levelCurrent << 16) | SC_FOLDLEVELWHITEFLAG); + } + visibleChars = 0; + inLineComment = false; + } + } +} + +LexerModule lmRust(SCLEX_RUST, LexerRust::LexerFactoryRust, "rust", rustWordLists); diff --git a/scintilla/makefile.win32 b/scintilla/makefile.win32 index 5cbed06a70..6daac9b63e 100644 --- a/scintilla/makefile.win32 +++ b/scintilla/makefile.win32 @@ -96,6 +96,7 @@ LexLua.o \ LexHaskell.o \ LexBasic.o \ LexR.o \ +LexRust.o \ LexYAML.o \ LexCmake.o \ LexNsis.o diff --git a/scintilla/src/Catalogue.cxx b/scintilla/src/Catalogue.cxx index 85116a5fb4..70ce3bc701 100644 --- a/scintilla/src/Catalogue.cxx +++ b/scintilla/src/Catalogue.cxx @@ -112,6 +112,7 @@ int Scintilla_LinkLexers() { LINK_LEXER(lmPython); LINK_LEXER(lmR); LINK_LEXER(lmRuby); + LINK_LEXER(lmRust); LINK_LEXER(lmSQL); LINK_LEXER(lmTCL); LINK_LEXER(lmTxt2tags); From 36537c4dcd7d3f78075289128c0e0a4a13544880 Mon Sep 17 00:00:00 2001 From: SiegeLord Date: Sun, 12 Jan 2014 13:57:03 -0500 Subject: [PATCH 05/12] Update Rust Scintilla lexer to Scintilla revision 4946:4d9978010c3a --- scintilla/lexers/LexRust.cxx | 177 +++++++++++++++++++---------------- 1 file changed, 95 insertions(+), 82 deletions(-) diff --git a/scintilla/lexers/LexRust.cxx b/scintilla/lexers/LexRust.cxx index 0d9aa1e5e9..bf59933afb 100644 --- a/scintilla/lexers/LexRust.cxx +++ b/scintilla/lexers/LexRust.cxx @@ -34,6 +34,9 @@ using namespace Scintilla; #endif +static const int NUM_RUST_KEYWORD_LISTS = 7; +static const int MAX_RUST_IDENT_CHARS = 1023; + static bool IsStreamCommentStyle(int style) { return style == SCE_RUST_COMMENTBLOCK || style == SCE_RUST_COMMENTBLOCKDOC; @@ -67,7 +70,7 @@ struct OptionsRust { } }; -static const char * const rustWordLists[] = { +static const char * const rustWordLists[NUM_RUST_KEYWORD_LISTS + 1] = { "Primary keywords and identifiers", "Built in types", "Other keywords", @@ -114,7 +117,7 @@ struct OptionSetRust : public OptionSet { }; class LexerRust : public ILexer { - WordList keywords[7]; + WordList keywords[NUM_RUST_KEYWORD_LISTS]; OptionsRust options; OptionSetRust osRust; public: @@ -159,7 +162,7 @@ int SCI_METHOD LexerRust::PropertySet(const char *key, const char *val) { int SCI_METHOD LexerRust::WordListSet(int n, const char *wl) { int firstModification = -1; - if (n < 7) { + if (n < NUM_RUST_KEYWORD_LISTS) { WordList *wordListN = &keywords[n]; WordList wlNew; wlNew.Set(wl); @@ -209,12 +212,12 @@ static void ScanIdentifier(Accessor& styler, int& pos, WordList *keywords) { pos++; styler.ColourTo(pos - 1, SCE_RUST_MACRO); } else { - char s[1024]; + char s[MAX_RUST_IDENT_CHARS + 1]; int len = pos - start; - len = len > 1024 ? 1024 : len; + len = len > MAX_RUST_IDENT_CHARS ? MAX_RUST_IDENT_CHARS : len; GrabString(s, styler, start, len); bool keyword = false; - for (int ii = 0; ii < 7; ii++) { + for (int ii = 0; ii < NUM_RUST_KEYWORD_LISTS; ii++) { if (keywords[ii].InList(s)) { styler.ColourTo(pos - 1, SCE_RUST_WORD + ii); keyword = true; @@ -237,41 +240,28 @@ static void ScanDigits(Accessor& styler, int& pos, int base) { } } -static bool ScanExponent(Accessor& styler, int& pos) { - int c = styler.SafeGetCharAt(pos, '\0'); - if (c == 'e' || c == 'E') { - pos++; - c = styler.SafeGetCharAt(pos, '\0'); - if (c == '-' || c == '+') - pos++; - int old_pos = pos; - ScanDigits(styler, pos, 10); - if (old_pos == pos) { - return false; - } - } - return true; -} - static void ScanNumber(Accessor& styler, int& pos) { int base = 10; int c = styler.SafeGetCharAt(pos, '\0'); int n = styler.SafeGetCharAt(pos + 1, '\0'); bool error = false; if (c == '0' && n == 'x') { - pos += 2; - base = 16; - } else if (c == '0' && n == 'b') { - pos += 2; - base = 2; - } - int old_pos = pos; - ScanDigits(styler, pos, base); - c = styler.SafeGetCharAt(pos, '\0'); - if (c == 'u' || c == 'i') { + pos += 2; + base = 16; + } else if (c == '0' && n == 'b') { + pos += 2; + base = 2; + } else if (c == '0' && n == 'o') { + pos += 2; + base = 8; + } + int old_pos = pos; + ScanDigits(styler, pos, base); + c = styler.SafeGetCharAt(pos, '\0'); + if (c == 'u' || c == 'i') { pos++; c = styler.SafeGetCharAt(pos, '\0'); - int n = styler.SafeGetCharAt(pos + 1, '\0'); + n = styler.SafeGetCharAt(pos + 1, '\0'); if (c == '8') { pos++; } else if (c == '1' && n == '6') { @@ -281,16 +271,34 @@ static void ScanNumber(Accessor& styler, int& pos) { } else if (c == '6' && n == '4') { pos += 2; } - } else if (c == '.') { - error = base != 10; - pos++; - ScanDigits(styler, pos, 10); - error |= !ScanExponent(styler, pos); + } else { + n = styler.SafeGetCharAt(pos + 1, '\0'); + if (c == '.' && !(IsIdentifierStart(n) || n == '.')) { + error |= base != 10; + pos++; + ScanDigits(styler, pos, 10); + } + + c = styler.SafeGetCharAt(pos, '\0'); + if (c == 'e' || c == 'E') { + error |= base != 10; + pos++; + c = styler.SafeGetCharAt(pos, '\0'); + if (c == '-' || c == '+') + pos++; + int old_pos = pos; + ScanDigits(styler, pos, 10); + if (old_pos == pos) { + error = true; + } + } + c = styler.SafeGetCharAt(pos, '\0'); if (c == 'f') { + error |= base != 10; pos++; c = styler.SafeGetCharAt(pos, '\0'); - int n = styler.SafeGetCharAt(pos + 1, '\0'); + n = styler.SafeGetCharAt(pos + 1, '\0'); if (c == '3' && n == '2') { pos += 2; } else if (c == '6' && n == '4') { @@ -438,83 +446,86 @@ enum CommentState { }; /* - * The rule for block-doc comments is as follows (use x for asterisk)... /xx and /x! start doc comments - * unless the entire comment is x's. + * The rule for block-doc comments is as follows: /xxN and /x! (where x is an asterisk, N is a non-asterisk) start doc comments. + * Otherwise it's a regular comment. */ -static void ResumeBlockComment(Accessor &styler, int& pos, int max, CommentState state) { +static void ResumeBlockComment(Accessor &styler, int& pos, int max, CommentState state, int level) { int c = styler.SafeGetCharAt(pos, '\0'); bool maybe_doc_comment = false; - bool any_non_asterisk = false; - if (c == '*' || c == '!') { + if (c == '*') { + int n = styler.SafeGetCharAt(pos + 1, '\0'); + if (n != '*' && n != '/') { + maybe_doc_comment = true; + } + } else if (c == '!') { maybe_doc_comment = true; } + for (;;) { + int n = styler.SafeGetCharAt(pos + 1, '\0'); if (pos == styler.LineEnd(styler.GetLine(pos))) - styler.SetLineState(styler.GetLine(pos), 0); + styler.SetLineState(styler.GetLine(pos), level); if (c == '*') { - int n = styler.SafeGetCharAt(pos + 1, '\0'); + pos++; if (n == '/') { - pos += 2; - if (state == DocComment || (state == UnknownComment && maybe_doc_comment && any_non_asterisk)) - styler.ColourTo(pos - 1, SCE_RUST_COMMENTBLOCKDOC); - else - styler.ColourTo(pos - 1, SCE_RUST_COMMENTBLOCK); - break; + pos++; + level--; + if (level == 0) { + styler.SetLineState(styler.GetLine(pos), 0); + if (state == DocComment || (state == UnknownComment && maybe_doc_comment)) + styler.ColourTo(pos - 1, SCE_RUST_COMMENTBLOCKDOC); + else + styler.ColourTo(pos - 1, SCE_RUST_COMMENTBLOCK); + break; + } + } + } else if (c == '/') { + pos++; + if (n == '*') { + pos++; + level++; } - } else { - any_non_asterisk = true; } - if (c == '\0' || pos >= max) { + else { + pos++; + } + if (pos >= max) { if (state == DocComment || (state == UnknownComment && maybe_doc_comment)) styler.ColourTo(pos - 1, SCE_RUST_COMMENTBLOCKDOC); else styler.ColourTo(pos - 1, SCE_RUST_COMMENTBLOCK); break; } - pos++; c = styler.SafeGetCharAt(pos, '\0'); } } /* - * The rule for line-doc comments is as follows... /// and //! start doc comments - * unless the comment is composed entirely of /'s followed by whitespace. That is: - * // - comment - * /// - doc-comment - * //// - comment - * ////a - doc-comment + * The rule for line-doc comments is as follows... ///N and //! (where N is a non slash) start doc comments. + * Otherwise it's a normal line comment. */ static void ResumeLineComment(Accessor &styler, int& pos, int max, CommentState state) { bool maybe_doc_comment = false; - int num_leading_slashes = 0; int c = styler.SafeGetCharAt(pos, '\0'); if (c == '/') { - num_leading_slashes = 1; - while (pos < max) { + if (pos < max) { pos++; c = styler.SafeGetCharAt(pos, '\0'); - if (c == '/') { - num_leading_slashes++; - } else { - break; + if (c != '/') { + maybe_doc_comment = true; } } } else if (c == '!') { maybe_doc_comment = true; } - bool non_white_space = false; - while (pos < max && c != '\n' && c != '\0') { - if (!IsWhitespace(c)) - non_white_space = true; + while (pos < max && c != '\n') { if (pos == styler.LineEnd(styler.GetLine(pos))) styler.SetLineState(styler.GetLine(pos), 0); pos++; c = styler.SafeGetCharAt(pos, '\0'); } - maybe_doc_comment |= num_leading_slashes == 1 || (num_leading_slashes > 1 && non_white_space); - if (state == DocComment || (state == UnknownComment && maybe_doc_comment)) styler.ColourTo(pos - 1, SCE_RUST_COMMENTLINEDOC); else @@ -528,14 +539,14 @@ static void ScanComments(Accessor &styler, int& pos, int max) { if (c == '/') ResumeLineComment(styler, pos, max, UnknownComment); else if (c == '*') - ResumeBlockComment(styler, pos, max, UnknownComment); + ResumeBlockComment(styler, pos, max, UnknownComment, 1); } static void ResumeString(Accessor &styler, int& pos, int max) { int c = styler.SafeGetCharAt(pos, '\0'); bool error = false; while (c != '"' && !error) { - if (c == '\0' || pos >= max) { + if (pos >= max) { error = true; break; } @@ -570,6 +581,9 @@ static void ResumeString(Accessor &styler, int& pos, int max) { static void ResumeRawString(Accessor &styler, int& pos, int max, int num_hashes) { for (;;) { + if (pos == styler.LineEnd(styler.GetLine(pos))) + styler.SetLineState(styler.GetLine(pos), num_hashes); + int c = styler.SafeGetCharAt(pos, '\0'); if (c == '"') { pos++; @@ -583,13 +597,12 @@ static void ResumeRawString(Accessor &styler, int& pos, int max, int num_hashes) styler.ColourTo(pos - 1, SCE_RUST_STRINGR); break; } - } else if (c == '\0' || pos >= max) { + } else if (pos >= max) { styler.ColourTo(pos - 1, SCE_RUST_STRINGR); break; + } else { + pos++; } - if (pos == styler.LineEnd(styler.GetLine(pos))) - styler.SetLineState(styler.GetLine(pos), num_hashes); - pos++; } } @@ -618,7 +631,7 @@ void SCI_METHOD LexerRust::Lex(unsigned int startPos, int length, int initStyle, styler.StartSegment(pos); if (initStyle == SCE_RUST_COMMENTBLOCK || initStyle == SCE_RUST_COMMENTBLOCKDOC) { - ResumeBlockComment(styler, pos, max, initStyle == SCE_RUST_COMMENTBLOCKDOC ? DocComment : NotDocComment); + ResumeBlockComment(styler, pos, max, initStyle == SCE_RUST_COMMENTBLOCKDOC ? DocComment : NotDocComment, styler.GetLineState(styler.GetLine(pos) - 1)); } else if (initStyle == SCE_RUST_COMMENTLINE || initStyle == SCE_RUST_COMMENTLINEDOC) { ResumeLineComment(styler, pos, max, initStyle == SCE_RUST_COMMENTLINEDOC ? DocComment : NotDocComment); } else if (initStyle == SCE_RUST_STRING) { From aa7e938164dc14cb1d866effb0c11b42ec11ff6e Mon Sep 17 00:00:00 2001 From: Colomban Wendling Date: Fri, 18 Oct 2013 15:06:09 +0200 Subject: [PATCH 06/12] Update our Scintilla patch for the addition of Rust lexer --- scintilla/scintilla_changes.patch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scintilla/scintilla_changes.patch b/scintilla/scintilla_changes.patch index fdc8d467c4..3b754f82f6 100644 --- a/scintilla/scintilla_changes.patch +++ b/scintilla/scintilla_changes.patch @@ -31,7 +31,7 @@ diff --git b/scintilla/src/Catalogue.cxx a/scintilla/src/Catalogue.cxx index e728f34..85116a5 100644 +++ scintilla/src/Catalogue.cxx --- scintilla/src/Catalogue.cxx -@@ -76,112 +76,47 @@ int Scintilla_LinkLexers() { +@@ -76,112 +76,48 @@ int Scintilla_LinkLexers() { //++Autogenerated -- run scripts/LexGen.py to regenerate //**\(\tLINK_LEXER(\*);\n\) @@ -121,7 +121,7 @@ index e728f34..85116a5 100644 LINK_LEXER(lmR); - LINK_LEXER(lmREBOL); LINK_LEXER(lmRuby); -- LINK_LEXER(lmRust); + LINK_LEXER(lmRust); - LINK_LEXER(lmScriptol); - LINK_LEXER(lmSmalltalk); - LINK_LEXER(lmSML); From a08557321296e9fa63dae7b25a2bf71a31a92b39 Mon Sep 17 00:00:00 2001 From: SiegeLord Date: Thu, 17 Oct 2013 20:22:48 -0400 Subject: [PATCH 07/12] Use :: as the context separator for Rust --- src/symbols.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/symbols.c b/src/symbols.c index 0d820d11e1..2caa0f2119 100644 --- a/src/symbols.c +++ b/src/symbols.c @@ -300,6 +300,7 @@ const gchar *symbols_get_context_separator(gint ft_id) case GEANY_FILETYPES_GLSL: /* for structs */ /*case GEANY_FILETYPES_RUBY:*/ /* not sure what to use atm*/ case GEANY_FILETYPES_PHP: + case GEANY_FILETYPES_RUST: return "::"; /* avoid confusion with other possible separators in group/section name */ From 3d7ade4e0232f4292b3d966e20f012afb4a2856e Mon Sep 17 00:00:00 2001 From: SiegeLord Date: Wed, 16 Oct 2013 17:19:29 -0400 Subject: [PATCH 08/12] Integrate the Rust lexer with the rest of Geany's functionality --- data/filetype_extensions.conf | 2 +- data/filetypes.Rust.conf | 61 --------------------------------- data/filetypes.rust | 31 ++++++++++++----- src/document.c | 1 + src/editor.c | 7 ++++ src/highlighting.c | 14 ++++++++ src/highlightingmappings.h | 35 +++++++++++++++++++ src/symbols.c | 16 +++++++++ tagmanager/ctags/makefile.win32 | 2 +- wscript | 1 + 10 files changed, 99 insertions(+), 71 deletions(-) delete mode 100644 data/filetypes.Rust.conf diff --git a/data/filetype_extensions.conf b/data/filetype_extensions.conf index 3965b46789..6ec67e1e4f 100644 --- a/data/filetype_extensions.conf +++ b/data/filetype_extensions.conf @@ -53,7 +53,7 @@ Python=*.py;*.pyw;SConstruct;SConscript;wscript; PowerShell=*.ps1;*.psm1; reStructuredText=*.rest;*.reST;*.rst; R=*.R;*.r; -Rust=*.rs +Rust=*.rs; Ruby=*.rb;*.rhtml;*.ruby;*.gemspec;Gemfile;rakefile;Rakefile; Scala=*.scala;*.scl; Sh=*.sh;configure;configure.in;configure.in.in;configure.ac;*.ksh;*.mksh;*.zsh;*.ash;*.bash;*.m4;PKGBUILD;*profile; diff --git a/data/filetypes.Rust.conf b/data/filetypes.Rust.conf deleted file mode 100644 index ceb37863d8..0000000000 --- a/data/filetypes.Rust.conf +++ /dev/null @@ -1,61 +0,0 @@ -# For complete documentation of this file, please see Geany's main documentation -[styling=C] - -[keywords] -# all items must be in one line -primary=as break const copy do drop else enum extern false fn for if impl let log loop match mod mut priv pub pure ref return self static struct super true trait type unsafe use while -secondary= -# 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] -# preprocessor properties - possibly useful for tweaking #[attribute] highlighting -#styling.within.preprocessor=1 -#lexer.cpp.track.preprocessor=0 -#preprocessor.symbol.$(file.patterns.cpp)=# -#preprocessor.start.$(file.patterns.cpp)=if ifdef ifndef -#preprocessor.middle.$(file.patterns.cpp)=else elif -#preprocessor.end.$(file.patterns.cpp)=endif - -[settings] -# default extension used when saving files -extension=rs -lexer_filetype=C - -# the following characters are these which a "word" can contains, see documentation -#wordchars=_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 - -# single comments, like # in this file -comment_single=// -# multiline comments -comment_open=/* -comment_close=*/ - -# set to false if a comment character/string should start at column 0 of a line, true uses any -# indentation of the line, e.g. setting to true causes the following on pressing CTRL+d - #command_example(); -# setting to false would generate this -# command_example(); -# This setting works only for single line comments -comment_use_indent=true - -# context action command (please see Geany's main documentation for details) -context_action_cmd= - -[indentation] -#width=4 -# 0 is spaces, 1 is tabs, 2 is tab & spaces -#type=1 - -[build-menu] -FT_00_LB=_Compile -FT_00_CM=rustc -c "%f" -FT_00_WD= -FT_01_LB=_Build -FT_01_CM=rustc "%f" -FT_01_WD= -EX_00_LB=_Run -EX_00_CM="./%e" -EX_00_WD= - - diff --git a/data/filetypes.rust b/data/filetypes.rust index a69cb23fbd..45a2b02864 100644 --- a/data/filetypes.rust +++ b/data/filetypes.rust @@ -1,24 +1,39 @@ # For complete documentation of this file, please see Geany's main documentation -[styling=C] +[styling] +# Edit these in the colorscheme .conf file instead +default=default +commentblock=comment +commentline=comment_line +commentblockdoc=comment_doc +commentlinedoc=comment_doc +number=number_1 +word=keyword_1 +word2=keyword_2 +word3=keyword_3 +word4=type +string=string_1 +stringraw=string_2 +character=character +operator=operator +identifier=identifier_1 +lifetime=parameter +macro=preprocessor +lexerror=error [keywords] # all items must be in one line -primary=fn type as break const copy do else enum extern fail for if impl let log loop match mod mut priv pub ref return static struct trait unsafe use while in continue alignof be offsetof pure sizeof typeof yield -secondary=bool int uint i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str Self self +primary=alignof as be box break const continue do else enum extern false fn for if impl in let __log_level loop match mod mut offsetof once priv proc pub pure ref return self sizeof static struct super trait true type typeof unsafe unsized use while yield +secondary=bool char f32 f64 i16 i32 i64 i8 int str u16 u32 u64 u8 uint +tertiary=Self [lexer_properties] styling.within.preprocessor=1 lexer.cpp.track.preprocessor=0 [settings] -lexer_filetype=C - # default extension used when saving files extension=rs -# the following characters are these which a "word" can contains, see documentation -#wordchars=_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 - # single comments, like # in this file comment_single=// # multiline comments diff --git a/src/document.c b/src/document.c index 37dbe97b1b..14b7643e22 100644 --- a/src/document.c +++ b/src/document.c @@ -2317,6 +2317,7 @@ void document_highlight_tags(GeanyDocument *doc) case GEANY_FILETYPES_JAVA: case GEANY_FILETYPES_OBJECTIVEC: case GEANY_FILETYPES_VALA: + case GEANY_FILETYPES_RUST: { /* index of the keyword set in the Scintilla lexer, for diff --git a/src/editor.c b/src/editor.c index 1d6676ee54..0db81c475e 100644 --- a/src/editor.c +++ b/src/editor.c @@ -1257,6 +1257,7 @@ static gboolean lexer_has_braces(ScintillaObject *sci) case SCLEX_PERL: case SCLEX_TCL: case SCLEX_R: + case SCLEX_RUST: return TRUE; default: return FALSE; @@ -2893,6 +2894,7 @@ static gint get_multiline_comment_style(GeanyEditor *editor, gint line_start) case SCLEX_CAML: style_comment = SCE_CAML_COMMENT; break; case SCLEX_D: style_comment = SCE_D_COMMENT; break; case SCLEX_PASCAL: style_comment = SCE_PAS_COMMENT; break; + case SCLEX_RUST: style_comment = SCE_RUST_COMMENTBLOCK; break; default: style_comment = SCE_C_COMMENT; } @@ -3415,6 +3417,10 @@ static gboolean in_block_comment(gint lexer, gint style) case SCLEX_CSS: return (style == SCE_CSS_COMMENT); + case SCLEX_RUST: + return (style == SCE_RUST_COMMENTBLOCK || + style == SCE_RUST_COMMENTBLOCKDOC); + default: return FALSE; } @@ -4951,6 +4957,7 @@ void editor_set_indentation_guides(GeanyEditor *editor) case SCLEX_FREEBASIC: case SCLEX_D: case SCLEX_OCTAVE: + case SCLEX_RUST: mode = SC_IV_LOOKBOTH; break; diff --git a/src/highlighting.c b/src/highlighting.c index d84b489336..073acef58c 100644 --- a/src/highlighting.c +++ b/src/highlighting.c @@ -1049,6 +1049,7 @@ void highlighting_init_styles(guint filetype_idx, GKeyFile *config, GKeyFile *co init_styleset_case(PYTHON); init_styleset_case(R); init_styleset_case(RUBY); + init_styleset_case(RUST); init_styleset_case(SH); init_styleset_case(SQL); init_styleset_case(TCL); @@ -1131,6 +1132,7 @@ void highlighting_set_styles(ScintillaObject *sci, GeanyFiletype *ft) styleset_case(PYTHON); styleset_case(R); styleset_case(RUBY); + styleset_case(RUST); styleset_case(SH); styleset_case(SQL); styleset_case(TCL); @@ -1543,6 +1545,12 @@ gboolean highlighting_is_string_style(gint lexer, gint style) case SCLEX_ABAQUS: return (style == SCE_ABAQUS_STRING); + + case SCLEX_RUST: + return (style == SCE_RUST_CHARACTER || + style == SCE_RUST_STRING || + style == SCE_RUST_STRINGR || + style == SCE_RUST_LEXERROR); } return FALSE; } @@ -1692,6 +1700,12 @@ gboolean highlighting_is_comment_style(gint lexer, gint style) return (style == SCE_ASM_COMMENT || style == SCE_ASM_COMMENTBLOCK || style == SCE_ASM_COMMENTDIRECTIVE); + + case SCLEX_RUST: + return (style == SCE_RUST_COMMENTBLOCK || + style == SCE_RUST_COMMENTLINE || + style == SCE_RUST_COMMENTBLOCKDOC || + style == SCE_RUST_COMMENTLINEDOC); } return FALSE; } diff --git a/src/highlightingmappings.h b/src/highlightingmappings.h index 1eba579a51..8c34d5303e 100644 --- a/src/highlightingmappings.h +++ b/src/highlightingmappings.h @@ -1290,6 +1290,41 @@ static const HLKeyword highlighting_keywords_RUBY[] = }; #define highlighting_properties_RUBY EMPTY_PROPERTIES +/* Rust */ +#define highlighting_lexer_RUST SCLEX_RUST +static const HLStyle highlighting_styles_RUST[] = +{ + { SCE_RUST_DEFAULT, "default", FALSE }, + { SCE_RUST_COMMENTBLOCK, "commentblock", FALSE }, + { SCE_RUST_COMMENTLINE, "commentline", FALSE }, + { SCE_RUST_COMMENTBLOCKDOC, "commentblockdoc", FALSE }, + { SCE_RUST_COMMENTLINEDOC, "commentlinedoc", FALSE }, + { SCE_RUST_NUMBER, "number", FALSE }, + { SCE_RUST_WORD, "word", FALSE }, + { SCE_RUST_WORD2, "word2", FALSE }, + { SCE_RUST_WORD3, "word3", FALSE }, + { SCE_RUST_WORD4, "word4", FALSE }, + { SCE_RUST_WORD5, "word5", FALSE }, + { SCE_RUST_WORD6, "word6", FALSE }, + { SCE_RUST_WORD7, "word7", FALSE }, + { SCE_RUST_STRING, "string", FALSE }, + { SCE_RUST_STRINGR, "stringraw", FALSE }, + { SCE_RUST_CHARACTER, "character", FALSE }, + { SCE_RUST_OPERATOR, "operator", FALSE }, + { SCE_RUST_IDENTIFIER, "identifier", FALSE }, + { SCE_RUST_LIFETIME, "lifetime", FALSE }, + { SCE_RUST_MACRO, "macro", FALSE }, + { SCE_RUST_LEXERROR, "lexerror", FALSE } +}; +static const HLKeyword highlighting_keywords_RUST[] = +{ + { 0, "primary", FALSE }, + /* SCI_SETKEYWORDS = 1 - secondary + global tags file types */ + { 1, "secondary", TRUE }, + { 2, "tertiary", FALSE }, + /* SCI_SETKEYWORDS = 3 is for current session types - see editor_lexer_get_type_keyword_idx() */ +}; +#define highlighting_properties_RUST EMPTY_PROPERTIES /* SH */ #define highlighting_lexer_SH SCLEX_BASH diff --git a/src/symbols.c b/src/symbols.c index 2caa0f2119..f03626fd1d 100644 --- a/src/symbols.c +++ b/src/symbols.c @@ -776,6 +776,22 @@ static void add_top_level_items(GeanyDocument *doc) NULL); break; } + case GEANY_FILETYPES_RUST: + { + tag_list_add_groups(tag_store, + &(tv_iters.tag_namespace), _("Modules"), "classviewer-namespace", + &(tv_iters.tag_struct), _("Structures"), "classviewer-struct", + &(tv_iters.tag_interface), _("Traits"), "classviewer-class", + &(tv_iters.tag_class), _("Implementations"), "classviewer-class", + &(tv_iters.tag_function), _("Functions"), "classviewer-method", + &(tv_iters.tag_type), _("Typedefs / Enums"), "classviewer-struct", + &(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, + NULL); + break; + } case GEANY_FILETYPES_PERL: { tag_list_add_groups(tag_store, diff --git a/tagmanager/ctags/makefile.win32 b/tagmanager/ctags/makefile.win32 index f1a11a9496..598620945c 100644 --- a/tagmanager/ctags/makefile.win32 +++ b/tagmanager/ctags/makefile.win32 @@ -48,7 +48,7 @@ $(COMPLIB): abaqus.o abc.o args.o c.o cobol.o fortran.o make.o conf.o pascal.o p 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 asm.o sql.o txt2tags.o css.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 $(AR) rc $@ $^ $(RANLIB) $@ diff --git a/wscript b/wscript index 1fac2ea829..5075f2cd51 100644 --- a/wscript +++ b/wscript @@ -105,6 +105,7 @@ ctags_sources = set([ 'tagmanager/ctags/read.c', 'tagmanager/ctags/rest.c', 'tagmanager/ctags/ruby.c', + 'tagmanager/ctags/rust.c', 'tagmanager/ctags/sh.c', 'tagmanager/ctags/sort.c', 'tagmanager/ctags/sql.c', From 828df0ab5fc6a2a25a3b1a524a34c442dc01921f Mon Sep 17 00:00:00 2001 From: SiegeLord Date: Wed, 16 Oct 2013 18:27:10 -0400 Subject: [PATCH 09/12] Add shebang filetype detection for Rust --- src/filetypes.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/filetypes.c b/src/filetypes.c index b201623a07..0048b345f1 100644 --- a/src/filetypes.c +++ b/src/filetypes.c @@ -946,7 +946,8 @@ static GeanyFiletype *find_shebang(const gchar *utf8_filename, const gchar *line { "ash", GEANY_FILETYPES_SH }, { "dmd", GEANY_FILETYPES_D }, { "wish", GEANY_FILETYPES_TCL }, - { "node", GEANY_FILETYPES_JS } + { "node", GEANY_FILETYPES_JS }, + { "rust", GEANY_FILETYPES_RUST } }; gchar *tmp = g_path_get_basename(line + 2); gchar *basename_interpreter = tmp; From 09cb24c7c718fe587d1f3f97feff811c05a8ab72 Mon Sep 17 00:00:00 2001 From: dobkeratops Date: Thu, 17 Oct 2013 20:08:57 -0400 Subject: [PATCH 10/12] Add Rust ctag tests --- tests/ctags/test_input.rs | 90 +++++++++++++++++++++++++++++++++ tests/ctags/test_input.rs.tags | 33 ++++++++++++ tests/ctags/test_input2.rs | 43 ++++++++++++++++ tests/ctags/test_input2.rs.tags | 19 +++++++ 4 files changed, 185 insertions(+) create mode 100644 tests/ctags/test_input.rs create mode 100644 tests/ctags/test_input.rs.tags create mode 100644 tests/ctags/test_input2.rs create mode 100644 tests/ctags/test_input2.rs.tags diff --git a/tests/ctags/test_input.rs b/tests/ctags/test_input.rs new file mode 100644 index 0000000000..0aca65d6d0 --- /dev/null +++ b/tests/ctags/test_input.rs @@ -0,0 +1,90 @@ +#[feature(globs)]; +use std::*; +use test_input2::*; +mod test_input2; + +fn yada(a:int,c:Foo,b:test_input2::fruit::SomeStruct)->~str { a.to_str() } +fn main() { + use test_input2::fruit::*; + io::println(foo_bar_test_func(SomeStruct{red_value:1,green_value:2,blue_value:3},(4,5)).to_str()); + let a=Foo{foo_field_1:2}; + a.my_method(1); + let c=a_cat(3); + let d=Foo{foo_field_1:a.foo_field_1+2}; a.test(); + println(a.foo_field_1.to_str()); +} +struct Bar(int); +struct Baz(int); + +struct Foo{foo_field_1:int} +struct Foo2 { + x:int, + y:int +} + +impl Foo { + fn my_method(&self,_:int){ print("my_method of foo");} +} + +enum Animal { + a_anteater(int), + a_bear(int), + a_cat(int), + a_dog(int), +} + +trait Testable +{ fn test(&self); + fn test1(&self); + fn test2(&self); +} +trait DoZ { + fn do_z(&self); +} + +impl Testable for Foo { + fn test(&self) { + println(self.foo_field_1.to_str()); + } + fn test1(&self) + { + println(self.foo_field_1.to_str()); + } fn test2(&self) + { + println(self.foo_field_1.to_str()); + } + +} +impl DoZ for Foo { + fn do_z(&self) { + println(self.foo_field_1.to_str()); + } +} + +trait SuperTraitTest:Testable+DoZ { +} + +fn gfunc(x:&X) { + let a1=a_anteater(1); + let a2=a_bear(1); + let a3=a_cat(1); + let a4=a_dog(1); + x.test(); + x.do_z(); +} +struct TraitedStructTest { + x:X +} +fn some2(a:Animal) { + match a { + a_cat(x)=> println("cat"), + _ => println("not a cat") + } + +} + + + + + + diff --git a/tests/ctags/test_input.rs.tags b/tests/ctags/test_input.rs.tags new file mode 100644 index 0000000000..a0bdcefcfd --- /dev/null +++ b/tests/ctags/test_input.rs.tags @@ -0,0 +1,33 @@ +# format=tagmanager +AnimalÌ2Ö0 +BarÌ2048Ö0 +BazÌ2048Ö0 +DoZÌ32Ö0 +FooÌ1Ö0 +FooÌ2048Ö0 +Foo2Ì2048Ö0 +SuperTraitTestÌ32Ö0 +TestableÌ32Ö0 +TraitedStructTestÌ2048Ö0 +a_anteaterÌ4ÎAnimalÖ0 +a_bearÌ4ÎAnimalÖ0 +a_catÌ4ÎAnimalÖ0 +a_dogÌ4ÎAnimalÖ0 +do_zÌ128Í(&self)ÎDoZÖ0 +do_zÌ128Í(&self)ÎFooÖ0 +foo_field_1Ì8ÎFooÖ0 +gfuncÌ16Í(x:&X)Ö0 +mainÌ16Í()Ö0 +my_methodÌ128Í(&self,_:int)ÎFooÖ0 +some2Ì16Í(a:Animal)Ö0 +testÌ128Í(&self)ÎFooÖ0 +testÌ128Í(&self)ÎTestableÖ0 +test1Ì128Í(&self)ÎFooÖ0 +test1Ì128Í(&self)ÎTestableÖ0 +test2Ì128Í(&self)ÎFooÖ0 +test2Ì128Í(&self)ÎTestableÖ0 +test_input2Ì256Ö0 +xÌ8ÎFoo2Ö0 +xÌ8ÎTraitedStructTestÖ0 +yÌ8ÎFoo2Ö0 +yadaÌ16Í(a:int,c:Foo,b:test_input2::fruit::SomeStruct)->~strÖ0 diff --git a/tests/ctags/test_input2.rs b/tests/ctags/test_input2.rs new file mode 100644 index 0000000000..8e343cc304 --- /dev/null +++ b/tests/ctags/test_input2.rs @@ -0,0 +1,43 @@ +pub fn foo_bar_test_func(apples:fruit::SomeStruct,(oranges,lemon):(int,int))->int{ + let some_var_name=2*oranges; + let a=SomeLongStructName{v:0}; + println("a");println("b"); println("c"); + veg::another_function(apples.red_value,oranges,lemon); + some_var_name-apples.red_value+lemon+a.v +} + + +pub mod fruit { + pub struct SomeStruct{ + red_value:int,green_value:int,blue_value:int + } +} +fn free_func() { +} + +impl SomeLongStructName { + fn fooo() { + } + fn baaz() { + } +} + + +pub struct SomeLongStructName {v:int} + +mod veg{ + pub fn another_function(a:int,b:int,c:int)->int { + a+b+c + } +} + + + +mod mineral { + fn granite() { + } + fn limestone() { + } + fn chalk() { + } +} diff --git a/tests/ctags/test_input2.rs.tags b/tests/ctags/test_input2.rs.tags new file mode 100644 index 0000000000..cbcddf0ebd --- /dev/null +++ b/tests/ctags/test_input2.rs.tags @@ -0,0 +1,19 @@ +# format=tagmanager +SomeLongStructNameÌ1Ö0 +SomeLongStructNameÌ2048Ö0 +SomeStructÌ2048ÎfruitÖ0 +another_functionÌ16Í(a:int,b:int,c:int)->intÎvegÖ0 +baazÌ128Í()ÎSomeLongStructNameÖ0 +blue_valueÌ8Îfruit::SomeStructÖ0 +chalkÌ16Í()ÎmineralÖ0 +foo_bar_test_funcÌ16Í(apples:fruit::SomeStruct,(oranges,lemon):(int,int))->intÖ0 +foooÌ128Í()ÎSomeLongStructNameÖ0 +free_funcÌ16Í()Ö0 +fruitÌ256Ö0 +graniteÌ16Í()ÎmineralÖ0 +green_valueÌ8Îfruit::SomeStructÖ0 +limestoneÌ16Í()ÎmineralÖ0 +mineralÌ256Ö0 +red_valueÌ8Îfruit::SomeStructÖ0 +vÌ8ÎSomeLongStructNameÖ0 +vegÌ256Ö0 From 370666865bea7ccb73689990231af87446cfe19a Mon Sep 17 00:00:00 2001 From: Colomban Wendling Date: Thu, 17 Oct 2013 20:13:33 -0400 Subject: [PATCH 11/12] Add Rust tests to the makefile --- tests/ctags/Makefile.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/ctags/Makefile.am b/tests/ctags/Makefile.am index 826dd8ad0b..35c5151f2d 100644 --- a/tests/ctags/Makefile.am +++ b/tests/ctags/Makefile.am @@ -229,6 +229,8 @@ test_sources = \ tabindent.py \ test.py \ test.vhd \ + test_input.rs \ + test_input2.rs \ traffic_signal.v \ traits.php \ union.f \ From 416c3daecc649b63c96503f5c43de37b19220b11 Mon Sep 17 00:00:00 2001 From: SiegeLord Date: Fri, 18 Oct 2013 17:33:33 -0400 Subject: [PATCH 12/12] Clean up Rust ctag tests and make them more comprehensive --- tests/ctags/test_input.rs | 72 ++++++++++++++++++++++++++++------ tests/ctags/test_input.rs.tags | 11 +++++- tests/ctags/test_input2.rs | 5 +-- 3 files changed, 71 insertions(+), 17 deletions(-) diff --git a/tests/ctags/test_input.rs b/tests/ctags/test_input.rs index 0aca65d6d0..f99d2cfed3 100644 --- a/tests/ctags/test_input.rs +++ b/tests/ctags/test_input.rs @@ -1,9 +1,36 @@ +#! fn ignored_in_comment() {} #[feature(globs)]; +#[feature(macro_rules)]; use std::*; use test_input2::*; mod test_input2; -fn yada(a:int,c:Foo,b:test_input2::fruit::SomeStruct)->~str { a.to_str() } +/* + * fn ignored_in_comment() {} + */ + +// fn ignored_in_comment() {} + +/* /* + * */ + fn ignored_in_nested_comment() {} + */ + +static size: uint = 1; + +struct S1 { + only_field: [int, ..size] +} + +macro_rules! test_macro +{ + () => {1} +} + +fn yada(a:int,c:Foo,b:test_input2::fruit::SomeStruct) -> ~str { + a.to_str() +} + fn main() { use test_input2::fruit::*; io::println(foo_bar_test_func(SomeStruct{red_value:1,green_value:2,blue_value:3},(4,5)).to_str()); @@ -12,11 +39,25 @@ fn main() { let c=a_cat(3); let d=Foo{foo_field_1:a.foo_field_1+2}; a.test(); println(a.foo_field_1.to_str()); + ignore! + ( + fn ignored_inside_macro() {} + ) + + let _ = "fn ignored_in_string() {} + "; + + let _ = r##"fn ignored_in_raw_string() {}""##; + + fn nested() {} } + struct Bar(int); + struct Baz(int); struct Foo{foo_field_1:int} + struct Foo2 { x:int, y:int @@ -38,6 +79,7 @@ trait Testable fn test1(&self); fn test2(&self); } + trait DoZ { fn do_z(&self); } @@ -46,15 +88,16 @@ impl Testable for Foo { fn test(&self) { println(self.foo_field_1.to_str()); } - fn test1(&self) - { - println(self.foo_field_1.to_str()); - } fn test2(&self) - { + + fn test1(&self) { println(self.foo_field_1.to_str()); } + fn test2(&self) { + println(self.foo_field_1.to_str()); + } } + impl DoZ for Foo { fn do_z(&self) { println(self.foo_field_1.to_str()); @@ -72,9 +115,20 @@ fn gfunc(x:&X) { x.test(); x.do_z(); } + struct TraitedStructTest { x:X } + +trait ParametrizedTrait { + fn test(&self); +} + +impl ParametrizedTrait for TraitedStructTest { + fn test(&self) { + } +} + fn some2(a:Animal) { match a { a_cat(x)=> println("cat"), @@ -82,9 +136,3 @@ fn some2(a:Animal) { } } - - - - - - diff --git a/tests/ctags/test_input.rs.tags b/tests/ctags/test_input.rs.tags index a0bdcefcfd..2c6c0f15c8 100644 --- a/tests/ctags/test_input.rs.tags +++ b/tests/ctags/test_input.rs.tags @@ -6,8 +6,11 @@ DoZ FooÌ1Ö0 FooÌ2048Ö0 Foo2Ì2048Ö0 +ParametrizedTraitÌ32Ö0 +S1Ì2048Ö0 SuperTraitTestÌ32Ö0 TestableÌ32Ö0 +TraitedStructTestÌ1Ö0 TraitedStructTestÌ2048Ö0 a_anteaterÌ4ÎAnimalÖ0 a_bearÌ4ÎAnimalÖ0 @@ -19,15 +22,21 @@ foo_field_1 gfuncÌ16Í(x:&X)Ö0 mainÌ16Í()Ö0 my_methodÌ128Í(&self,_:int)ÎFooÖ0 +nestedÌ16Í()ÎmainÖ0 +only_fieldÌ8ÎS1Ö0 +sizeÌ16384Ö0 some2Ì16Í(a:Animal)Ö0 testÌ128Í(&self)ÎFooÖ0 +testÌ128Í(&self)ÎParametrizedTraitÖ0 testÌ128Í(&self)ÎTestableÖ0 +testÌ128Í(&self)ÎTraitedStructTestÖ0 test1Ì128Í(&self)ÎFooÖ0 test1Ì128Í(&self)ÎTestableÖ0 test2Ì128Í(&self)ÎFooÖ0 test2Ì128Í(&self)ÎTestableÖ0 test_input2Ì256Ö0 +test_macroÌ65536Ö0 xÌ8ÎFoo2Ö0 xÌ8ÎTraitedStructTestÖ0 yÌ8ÎFoo2Ö0 -yadaÌ16Í(a:int,c:Foo,b:test_input2::fruit::SomeStruct)->~strÖ0 +yadaÌ16Í(a:int,c:Foo,b:test_input2::fruit::SomeStruct) -> ~strÖ0 diff --git a/tests/ctags/test_input2.rs b/tests/ctags/test_input2.rs index 8e343cc304..dbd0d344ce 100644 --- a/tests/ctags/test_input2.rs +++ b/tests/ctags/test_input2.rs @@ -6,12 +6,12 @@ pub fn foo_bar_test_func(apples:fruit::SomeStruct,(oranges,lemon):(int,int))->in some_var_name-apples.red_value+lemon+a.v } - pub mod fruit { pub struct SomeStruct{ red_value:int,green_value:int,blue_value:int } } + fn free_func() { } @@ -22,7 +22,6 @@ impl SomeLongStructName { } } - pub struct SomeLongStructName {v:int} mod veg{ @@ -31,8 +30,6 @@ mod veg{ } } - - mod mineral { fn granite() { }