[geany/geany] e54956: Merge branch 'SiegeLord/rust'

Colomban Wendling git-noreply at xxxxx
Tue Jan 14 20:09:10 UTC 2014


Branch:      refs/heads/master
Author:      Colomban Wendling <ban at herbesfolles.org>
Committer:   Colomban Wendling <ban at herbesfolles.org>
Date:        Tue, 14 Jan 2014 20:09:10 UTC
Commit:      e5495627924c5505425d1699fdc7380ae0465670
             https://github.com/geany/geany/commit/e5495627924c5505425d1699fdc7380ae0465670

Log Message:
-----------
Merge branch 'SiegeLord/rust'

Closes PR#181.


Modified Paths:
--------------
    data/filetype_extensions.conf
    data/filetypes.Rust.conf
    data/filetypes.rust
    scintilla/Makefile.am
    scintilla/lexers/LexRust.cxx
    scintilla/makefile.win32
    scintilla/scintilla_changes.patch
    scintilla/src/Catalogue.cxx
    src/document.c
    src/editor.c
    src/filetypes.c
    src/filetypes.h
    src/highlighting.c
    src/highlightingmappings.h
    src/symbols.c
    tagmanager/ctags/Makefile.am
    tagmanager/ctags/makefile.win32
    tagmanager/ctags/parsers.h
    tagmanager/ctags/rust.c
    tests/ctags/Makefile.am
    tests/ctags/test_input.rs
    tests/ctags/test_input.rs.tags
    tests/ctags/test_input2.rs
    tests/ctags/test_input2.rs.tags
    wscript

Modified: data/filetype_extensions.conf
2 files changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -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;


Modified: data/filetypes.Rust.conf
61 files changed, 0 insertions(+), 61 deletions(-)
===================================================================
@@ -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=
-
-


Modified: data/filetypes.rust
66 files changed, 66 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,66 @@
+# For complete documentation of this file, please see Geany's main documentation
+[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=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]
+# default extension used when saving files
+extension=rs
+
+# 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"
+


Modified: scintilla/Makefile.am
1 files changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -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 \


Modified: scintilla/lexers/LexRust.cxx
781 files changed, 781 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,781 @@
+/** @file LexRust.cxx
+ ** Lexer for Rust.
+ **
+ ** Copyright (c) 2013 by SiegeLord <slabode at aim.com>
+ ** Converted to lexer object and added further folding features/properties by "Udo Lechner" <dlchnr(at)gmx(dot)net>
+ **/
+// Copyright 1998-2005 by Neil Hodgson <neilh at scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include <string>
+#include <map>
+
+#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 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;
+}
+
+// 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[NUM_RUST_KEYWORD_LISTS + 1] = {
+			"Primary keywords and identifiers",
+			"Built in types",
+			"Other keywords",
+			"Keywords 4",
+			"Keywords 5",
+			"Keywords 6",
+			"Keywords 7",
+			0,
+		};
+
+struct OptionSetRust : public OptionSet<OptionsRust> {
+	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[NUM_RUST_KEYWORD_LISTS];
+	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 < NUM_RUST_KEYWORD_LISTS) {
+		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[MAX_RUST_IDENT_CHARS + 1];
+		int len = pos - start;
+		len = len > MAX_RUST_IDENT_CHARS ? MAX_RUST_IDENT_CHARS : len;
+		GrabString(s, styler, start, len);
+		bool keyword = false;
+		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;
+				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 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;
+	} 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');
+		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 {
+		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');
+			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: /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, int level) {
+	int c = styler.SafeGetCharAt(pos, '\0');
+	bool maybe_doc_comment = false;
+	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), level);
+		if (c == '*') {
+			pos++;
+			if (n == '/') {
+				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 {
+			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;
+		}
+		c = styler.SafeGetCharAt(pos, '\0');
+	}
+}
+
+/*
+ * 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 c = styler.SafeGetCharAt(pos, '\0');
+	if (c == '/') {
+		if (pos < max) {
+			pos++;
+			c = styler.SafeGetCharAt(pos, '\0');
+			if (c != '/') {
+				maybe_doc_comment = true;
+			}
+		}
+	} else if (c == '!') {
+		maybe_doc_comment = 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');
+	}
+
+	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, 1);
+}
+
+static void ResumeString(Accessor &styler, int& pos, int max) {
+	int c = styler.SafeGetCharAt(pos, '\0');
+	bool error = false;
+	while (c != '"' && !error) {
+		if (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 (;;) {
+		if (pos == styler.LineEnd(styler.GetLine(pos)))
+			styler.SetLineState(styler.GetLine(pos), num_hashes);
+
+		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 (pos >= max) {
+			styler.ColourTo(pos - 1, SCE_RUST_STRINGR);
+			break;
+		} else {		
+			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, 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) {
+		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<unsigned int>(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);


Modified: scintilla/makefile.win32
1 files changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -96,6 +96,7 @@ LexLua.o \
 LexHaskell.o \
 LexBasic.o \
 LexR.o \
+LexRust.o \
 LexYAML.o \
 LexCmake.o \
 LexNsis.o


Modified: scintilla/scintilla_changes.patch
4 files changed, 2 insertions(+), 2 deletions(-)
===================================================================
@@ -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);


Modified: scintilla/src/Catalogue.cxx
1 files changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -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);


Modified: src/document.c
1 files changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -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


Modified: src/editor.c
7 files changed, 7 insertions(+), 0 deletions(-)
===================================================================
@@ -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;
 


Modified: src/filetypes.c
12 files changed, 11 insertions(+), 1 deletions(-)
===================================================================
@@ -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;
+
 }
 
 
@@ -937,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;


Modified: src/filetypes.h
1 files changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -94,6 +94,7 @@
 	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 */
 }


Modified: src/highlighting.c
14 files changed, 14 insertions(+), 0 deletions(-)
===================================================================
@@ -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;
 }


Modified: src/highlightingmappings.h
35 files changed, 35 insertions(+), 0 deletions(-)
===================================================================
@@ -1290,6 +1290,41 @@
 };
 #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


Modified: src/symbols.c
17 files changed, 17 insertions(+), 0 deletions(-)
===================================================================
@@ -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 */
@@ -775,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,


Modified: tagmanager/ctags/Makefile.am
1 files changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -43,6 +43,7 @@ parsers = \
 	r.c \
 	rest.c \
 	ruby.c \
+	rust.c \
 	sh.c \
 	sql.c \
 	tcl.c \


Modified: tagmanager/ctags/makefile.win32
2 files changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -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) $@


Modified: tagmanager/ctags/parsers.h
4 files changed, 3 insertions(+), 1 deletions(-)
===================================================================
@@ -59,7 +59,8 @@
     CobolParser, \
     ObjcParser, \
     AsciidocParser, \
-    AbaqusParser
+    AbaqusParser, \
+    RustParser
 /*
 langType of each parser
  0	CParser
@@ -107,6 +108,7 @@
 42	ObjcParser
 43  AsciidocParser
 44	AbaqusParser
+45  Rust
 */
 #endif	/* _PARSERS_H */
 


Modified: tagmanager/ctags/rust.c
909 files changed, 909 insertions(+), 0 deletions(-)
===================================================================
@@ -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 <string.h>
+
+#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 (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 (lexer->cur_c != EOF && lexer->cur_c != '\n')
+			advanceChar(lexer);
+	}
+	else if (lexer->next_c == '*')
+	{
+		int level = 1;
+		advanceNChar(lexer, 2);
+		while (lexer->cur_c != EOF && 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(lexer->cur_c != EOF && 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 (lexer->cur_c != EOF && 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 (lexer->cur_c == '#')
+	{
+		num_initial_hashes++;
+		advanceChar(lexer);
+	}
+	if (lexer->cur_c != '"')
+		return;
+	advanceChar(lexer);
+	while (lexer->cur_c != EOF)
+	{
+		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 (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 (lexer->cur_c != EOF)
+	{
+		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 (lexer->cur_c != EOF)
+	{
+		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" <ident>[<type_bounds>] "(" [<args>] ")" ["->" <ret_type>] "{" [<body>] "}"*/
+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" <ident> "{" [<body>] "}"
+ * "mod" <ident> ";"*/
+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" <ident> [<type_bounds>] "{" [<body>] "}"
+ */
+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 <T:T<T>, ...> */
+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" [<type_bounds>] <qualified_ident>[<type_bounds>] ["for" <qualified_ident>[<type_bounds>]] "{" [<body>] "}"
+ */
+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"] <ident>
+ */
+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" <ident>
+ */
+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" <ident>[<type_bounds>] "{" [<ident>,]+ "}"
+ * "struct/enum" <ident>[<type_bounds>] ";"
+ * */
+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" "!" <ident> <macro_body>
+ */
+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;
+}


Modified: tests/ctags/Makefile.am
2 files changed, 2 insertions(+), 0 deletions(-)
===================================================================
@@ -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							\


Modified: tests/ctags/test_input.rs
138 files changed, 138 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,138 @@
+#! fn ignored_in_comment() {}
+#[feature(globs)];
+#[feature(macro_rules)];
+use std::*;
+use test_input2::*;
+mod test_input2;
+
+/*
+ * 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());
+	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());
+	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
+}
+
+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:Testable+DoZ>(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:X
+}
+
+trait ParametrizedTrait<T> {
+	fn test(&self);
+}
+
+impl<T: Clone> ParametrizedTrait<T> for TraitedStructTest<T> {
+	fn test(&self) {
+	}
+}
+
+fn some2(a:Animal) {
+	match a {
+		a_cat(x)=> println("cat"),
+		_ => println("not a cat")
+	}
+
+}


Modified: tests/ctags/test_input.rs.tags
42 files changed, 42 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,42 @@
+# format=tagmanager
+AnimalÌ2Ö0
+BarÌ2048Ö0
+BazÌ2048Ö0
+DoZÌ32Ö0
+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
+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:Testable+DoZ>(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


Modified: tests/ctags/test_input2.rs
40 files changed, 40 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,40 @@
+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() {
+	}
+}


Modified: tests/ctags/test_input2.rs.tags
19 files changed, 19 insertions(+), 0 deletions(-)
===================================================================
@@ -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


Modified: wscript
1 files changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -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',



--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).


More information about the Commits mailing list