public TokenRegion(SnapshotPoint start, SnapshotPoint end, DafnyTokenKind kind) { Start = start; End = end; Kind = kind; }
public DafnyTokenTag(DafnyTokenKind kind, string fixedHoverText, Microsoft.Dafny.IVariable variable = null) { this.Kind = kind; this.FixedHoverText = fixedHoverText; this.Variable = variable; }
private static SnapshotPoint Scan(string txt, SnapshotPoint start, List <TokenRegion> newRegions, ITextSnapshot newSnapshot) { int longCommentDepth = 0; SnapshotPoint commentStart = new SnapshotPoint(); SnapshotPoint commentEndAsWeKnowIt = new SnapshotPoint(); // used only when longCommentDepth != 0 int N = txt.Length; bool done = false; while (!done) { N = txt.Length; // length of the current buffer int cur = 0; // offset into the current buffer if (longCommentDepth != 0) { ScanForEndOfComment(txt, ref longCommentDepth, ref cur); if (longCommentDepth == 0) { // we just finished parsing a long comment newRegions.Add(new TokenRegion(commentStart, new SnapshotPoint(newSnapshot, start + cur), DafnyTokenKind.Comment)); } else { // we're still parsing the long comment Contract.Assert(cur == txt.Length); commentEndAsWeKnowIt = new SnapshotPoint(newSnapshot, start + cur); goto OUTER_CONTINUE; } } // repeatedly get the remaining tokens from this buffer int end; // offset into the current buffer for (; ; cur = end) { // advance to the first character of a keyword or token DafnyTokenKind ty = DafnyTokenKind.Keyword; for (; ; cur++) { if (N <= cur) { // we've looked at everything in this buffer goto OUTER_CONTINUE; } char ch = txt[cur]; if ('a' <= ch && ch <= 'z') { break; } if ('A' <= ch && ch <= 'Z') { break; } if ('0' <= ch && ch <= '9') { ty = DafnyTokenKind.Number; break; } if (ch == '_' || ch == '?' || ch == '\\') { break; // parts of identifiers } if (ch == '\'') { ty = DafnyTokenKind.Char; break; } // part character literal or identifier if (ch == '"') { ty = DafnyTokenKind.String; break; } if (ch == '/') { ty = DafnyTokenKind.Comment; break; } } // advance to the end of the token end = cur + 1; // offset into the current buffer // first investigate if this is really a character literal if (ty == DafnyTokenKind.Char) { ty = DafnyTokenKind.Keyword; // we've seen a starting single-quote already if (cur + 3 <= N && txt[cur + 2] == '\'') { // Look for a simple character literal, like 'a' char cx = txt[cur + 1]; if (cx != '\'' && cx != '\\' && cx != '\n' && cx != '\r') { if (cur + 3 == N) { ty = DafnyTokenKind.Char; end = cur + 3; } else { // check if the next character is an identifier character, because then what we've seen was // really just part of that identifier cx = txt[cur + 3]; if ('a' <= cx && cx <= 'z') { } else if ('A' <= cx && cx <= 'Z') { } else if ('0' <= cx && cx <= '9') { } else if (cx == '\'' || cx == '_' || cx == '?' || cx == '\\') { } else { ty = DafnyTokenKind.Char; end = cur + 3; } } } } else if (cur + 4 <= N && txt[cur + 1] == '\\' && txt[cur + 3] == '\'') { // Look for an escaped character literal, like '\n' (note, a \ cannot be part of an identifier) char cx = txt[cur + 2]; if (cx == '\'' || cx == '\"' || cx == '\\' || cx == '0' || cx == 'n' || cx == 'r' || cx == 't') { ty = DafnyTokenKind.Char; end = cur + 4; } } else if (cur + 8 <= N && txt[cur + 1] == '\\' && txt[cur + 2] == 'u' && txt[cur + 7] == '\'') { // Look for a unicode character literal, like '\u40fE' (note, a \ cannot be part of an identifier) var numberOfHexDigits = 0; for (int i = 3; i < 7; i++) { char cx = txt[cur + i]; if (('0' <= cx && cx <= '9') || ('a' <= cx && cx <= 'f') || ('A' <= cx && cx <= 'F')) { numberOfHexDigits++; } } if (numberOfHexDigits == 4) { ty = DafnyTokenKind.Char; end = cur + 8; } } } if (ty == DafnyTokenKind.Number) { // scan the rest of this number for (; end < N; end++) { char ch = txt[end]; if ('0' <= ch && ch <= '9') { } else { break; } } } else if (ty == DafnyTokenKind.Char) { // we already did the work above } else if (ty == DafnyTokenKind.String) { // scan the rest of this string, but not past the end-of-buffer for (; end < N; end++) { char ch = txt[end]; if (ch == '"') { end++; break; } else if (ch == '\\') { // escape sequence end++; if (end == N) { break; } ch = txt[end]; if (ch == 'u') { end += 4; if (N <= end) { end = N; break; } } } } } else if (ty == DafnyTokenKind.Comment) { if (end == N) { continue; // this was not the start of a comment; it was just a single "/" and we don't care to color it } char ch = txt[end]; if (ch == '/') { // a short comment, to the end of the line. end = newSnapshot.GetLineFromPosition(start + end).End.Position - start; } else if (ch == '*') { // a long comment; find the matching "*/" end++; commentStart = new SnapshotPoint(newSnapshot, start + cur); Contract.Assert(longCommentDepth == 0); longCommentDepth = 1; ScanForEndOfComment(txt, ref longCommentDepth, ref end); if (longCommentDepth == 0) { // we finished scanning a long comment, and "end" is set to right after it newRegions.Add(new TokenRegion(commentStart, new SnapshotPoint(newSnapshot, start + end), DafnyTokenKind.Comment)); } else { commentEndAsWeKnowIt = new SnapshotPoint(newSnapshot, start + end); } continue; } else { // not a comment; it was just a single "/" and we don't care to color it continue; } } else { int trailingDigits = 0; for (; end < N; end++) { char ch = txt[end]; if ('a' <= ch && ch <= 'z') { trailingDigits = 0; } else if ('A' <= ch && ch <= 'Z') { trailingDigits = 0; } else if ('0' <= ch && ch <= '9') { trailingDigits++; } else if (ch == '\'' || ch == '_' || ch == '?' || ch == '\\') { trailingDigits = 0; } else { break; } } // we have a keyword or an identifier string s = txt.Substring(cur, end - cur); if (0 < trailingDigits && s.Length == 5 + trailingDigits && s.StartsWith("array") && s[5] != '0' && (trailingDigits != 1 || s[5] != '1')) { // this is a keyword for a built-in type (array2, array3, ...) ty = DafnyTokenKind.BuiltInType; } else if (0 < trailingDigits && s.Length == 2 + trailingDigits && s.StartsWith("bv") && (s[2] != '0' || trailingDigits == 1)) { // this is a keyword for a built-in type (bv0, bv1, ...) ty = DafnyTokenKind.BuiltInType; } else { switch (s) { #region keywords case "abstract": case "allocated": case "as": case "assert": case "assume": case "break": case "by": case "calc": case "case": case "class": case "const": case "trait": case "extends": case "codatatype": case "colemma": case "constructor": case "copredicate": case "datatype": case "else": case "exists": case "export": case "false": case "forall": case "fresh": case "function": case "ghost": case "if": case "import": case "in": case "include": case "inductive": case "iterator": case "label": case "lemma": case "match": case "method": case "modify": case "module": case "new": case "newtype": case "null": case "old": case "opened": case "predicate": case "print": case "protected": case "refines": case "return": case "returns": case "static": case "then": case "this": case "true": case "twostate": case "type": case "unchanged": case "var": case "where": case "while": case "yield": case "yields": #endregion break; #region keywords in specification clauses case "decreases": case "ensures": case "invariant": case "modifies": case "provides": case "reads": case "requires": case "reveals": case "witness": // "yields" plays a dual role #endregion ty = DafnyTokenKind.SpecificationClause; break; #region keywords for built-in types case "array": case "bool": case "char": case "imap": case "int": case "iset": case "map": case "multiset": case "nat": case "object": case "ORDINAL": case "real": case "seq": case "set": case "string": #endregion ty = DafnyTokenKind.BuiltInType; break; default: continue; // it was an identifier, so we don't color it } } } newRegions.Add(new TokenRegion(new SnapshotPoint(newSnapshot, start + cur), new SnapshotPoint(newSnapshot, start + end), ty)); } OUTER_CONTINUE: done = true; if (longCommentDepth != 0) { // we need to look into the next line ITextSnapshotLine currLine = newSnapshot.GetLineFromPosition(start + N); if ((currLine.LineNumber + 1) < newSnapshot.LineCount) { ITextSnapshotLine nextLine = newSnapshot.GetLineFromLineNumber(currLine.LineNumber + 1); txt = nextLine.GetText(); start = nextLine.Start; // we are done scanning the current buffer, but not the whole file yet. // we need to continue to find the enclosing "*/", or until the end of the file. done = false; } else { // This was a malformed comment, running to the end of the buffer. Above, we let "commentEndAsWeKnowIt" be the end of the // last line, so we can use it here. newRegions.Add(new TokenRegion(commentStart, commentEndAsWeKnowIt, DafnyTokenKind.Comment)); } } } return(new SnapshotPoint(newSnapshot, start + N)); }
public DafnyTokenTag(DafnyTokenKind kind) { this.Kind = kind; }
private static List <TokenRegion> Rescan(ITextSnapshot newSnapshot) { List <TokenRegion> newRegions = new List <TokenRegion>(); int longCommentDepth = 0; SnapshotPoint commentStart = new SnapshotPoint(); // used only when longCommentDepth != 0 SnapshotPoint commentEndAsWeKnowIt = new SnapshotPoint(); // used only when longCommentDepth != 0 foreach (ITextSnapshotLine line in newSnapshot.Lines) { string txt = line.GetText(); // the current line (without linebreak characters) int N = txt.Length; // length of the current line int cur = 0; // offset into the current line if (longCommentDepth != 0) { ScanForEndOfComment(txt, ref longCommentDepth, ref cur); if (longCommentDepth == 0) { // we just finished parsing a long comment newRegions.Add(new TokenRegion(commentStart, new SnapshotPoint(newSnapshot, line.Start + cur), DafnyTokenKind.Comment)); } else { // we're still parsing the long comment Contract.Assert(cur == txt.Length); commentEndAsWeKnowIt = new SnapshotPoint(newSnapshot, line.Start + cur); goto OUTER_CONTINUE; } } // repeatedly get the remaining tokens from this line int end; // offset into the current line for (; ; cur = end) { // advance to the first character of a keyword or token DafnyTokenKind ty = DafnyTokenKind.Keyword; for (; ; cur++) { if (N <= cur) { // we've looked at everything in this line goto OUTER_CONTINUE; } char ch = txt[cur]; if ('a' <= ch && ch <= 'z') { break; } if ('A' <= ch && ch <= 'Z') { break; } if ('0' <= ch && ch <= '9') { ty = DafnyTokenKind.Number; break; } if (ch == '\'' || ch == '_' || ch == '?' || ch == '\\') { break; // parts of identifiers } if (ch == '"') { ty = DafnyTokenKind.String; break; } if (ch == '/') { ty = DafnyTokenKind.Comment; break; } } // advance to the end of the token end = cur + 1; // offset into the current line if (ty == DafnyTokenKind.Number) { // scan the rest of this number for (; end < N; end++) { char ch = txt[end]; if ('0' <= ch && ch <= '9') { } else { break; } } } else if (ty == DafnyTokenKind.String) { // scan the rest of this string, but not past the end-of-line for (; end < N; end++) { char ch = txt[end]; if (ch == '"') { end++; break; } else if (ch == '\\') { // escape sequence end++; if (end == N) { break; } ch = txt[end]; if (ch == 'u') { end += 4; if (N <= end) { end = N; break; } } } } } else if (ty == DafnyTokenKind.Comment) { if (end == N) { continue; // this was not the start of a comment; it was just a single "/" and we don't care to color it } char ch = txt[end]; if (ch == '/') { // a short comment end = N; } else if (ch == '*') { // a long comment; find the matching "*/" end++; commentStart = new SnapshotPoint(newSnapshot, line.Start + cur); Contract.Assert(longCommentDepth == 0); longCommentDepth = 1; ScanForEndOfComment(txt, ref longCommentDepth, ref end); if (longCommentDepth == 0) { // we finished scanning a long comment, and "end" is set to right after it newRegions.Add(new TokenRegion(commentStart, new SnapshotPoint(newSnapshot, line.Start + end), DafnyTokenKind.Comment)); } else { commentEndAsWeKnowIt = new SnapshotPoint(newSnapshot, line.Start + end); } continue; } else { // not a comment; it was just a single "/" and we don't care to color it continue; } } else { int trailingDigits = 0; for (; end < N; end++) { char ch = txt[end]; if ('a' <= ch && ch <= 'z') { trailingDigits = 0; } else if ('A' <= ch && ch <= 'Z') { trailingDigits = 0; } else if ('0' <= ch && ch <= '9') { trailingDigits++; } else if (ch == '\'' || ch == '_' || ch == '?' || ch == '\\') { trailingDigits = 0; } else { break; } } // we have a keyword or an identifier string s = txt.Substring(cur, end - cur); if (0 < trailingDigits && s.Length == 5 + trailingDigits && s.StartsWith("array") && s[5] != '0' && (trailingDigits != 1 || s[5] != '1')) { // this is a keyword (array2, array3, ...) } else { switch (s) { #region keywords case "abstract": case "array": case "as": case "assert": case "assume": case "bool": case "break": case "calc": case "case": case "char": case "class": case "trait": case "extends": case "codatatype": case "colemma": case "constructor": case "copredicate": case "datatype": case "decreases": case "default": case "else": case "ensures": case "exists": case "false": case "forall": case "free": case "fresh": case "function": case "ghost": case "if": case "imap": case "import": case "in": case "include": case "inductive": case "int": case "invariant": case "iterator": case "label": case "lemma": case "map": case "match": case "method": case "modifies": case "modify": case "module": case "multiset": case "nat": case "new": case "newtype": case "null": case "object": case "old": case "opened": case "predicate": case "print": case "protected": case "reads": case "real": case "refines": case "requires": case "return": case "returns": case "seq": case "set": case "static": case "string": case "then": case "this": case "true": case "type": case "var": case "where": case "while": case "yield": case "yields": #endregion break; default: continue; // it was an identifier, so we don't color it } } } newRegions.Add(new TokenRegion(new SnapshotPoint(newSnapshot, line.Start + cur), new SnapshotPoint(newSnapshot, line.Start + end), ty)); } OUTER_CONTINUE :; } if (longCommentDepth != 0) { // This was a malformed comment, running to the end of the buffer. Above, we let "commentEndAsWeKnowIt" be the end of the // last line, so we can use it here. newRegions.Add(new TokenRegion(commentStart, commentEndAsWeKnowIt, DafnyTokenKind.Comment)); } return(newRegions); }