Example #1
0
        IEnumerable <FindResult> FindAllSingleLineForward(ITextSnapshotLine startLine, ITextSnapshotLine endLine, SnapshotSpan searchRange, string searchPattern, FindOptions options, StringComparison stringComparison, string?replacePattern)
        {
            Debug.Assert((options & FindOptions.SearchReverse) == 0);
            int  endLineNumber = endLine.LineNumber;
            var  snapshot      = searchRange.Snapshot;
            bool onlyWords     = (options & FindOptions.WholeWord) != 0;
            var  regex         = (options & FindOptions.UseRegularExpressions) != 0 ? GetRegex(searchPattern, options) : null;

            for (int lineNumber = startLine.LineNumber; lineNumber <= endLineNumber; lineNumber++)
            {
                var line  = snapshot.GetLineFromLineNumber(lineNumber);
                var range = searchRange.Intersection(line.ExtentIncludingLineBreak);
                if (range is null || range.Value.Length == 0)
                {
                    continue;
                }
                var text  = range.Value.GetText();
                int index = 0;
                if (!(regex is null))
                {
                    foreach (var res in GetRegexResults(regex, range.Value.Start, text, index, searchPattern, options, replacePattern))
                    {
                        yield return(res);
                    }
                }
Example #2
0
        void Update(SnapshotSpan span, ref HashSet <ITextViewLine> checkedLines)
        {
            Debug.Assert(span.Snapshot == wpfTextViewHost.TextView.TextSnapshot);
            var intersection = span.Intersection(wpfTextViewHost.TextView.TextViewLines.FormattedSpan);

            if (intersection == null)
            {
                return;
            }
            var point = intersection.Value.Start;

            while (point <= intersection.Value.End)
            {
                var line = wpfTextViewHost.TextView.TextViewLines.GetTextViewLineContainingBufferPosition(point);
                if (line == null)
                {
                    break;
                }
                if (checkedLines == null)
                {
                    checkedLines = new HashSet <ITextViewLine>();
                }
                if (!checkedLines.Contains(line))
                {
                    checkedLines.Add(line);
                    Update(line);
                }
                if (line.IsLastDocumentLine())
                {
                    break;
                }
                point = line.GetPointAfterLineBreak();
            }
        }
Example #3
0
        private static ClassificationSpan GetClassificationSpan(SnapshotSpan span, TokenInfo token, int lineNumber, IClassificationType classification)
        {
            var tokenSpan    = SnapshotSpanToSpan(span.Snapshot, token, lineNumber);
            var intersection = span.Intersection(tokenSpan);

            if (intersection != null && intersection.Value.Length > 0 ||
                (span.Length == 0 && tokenSpan.Contains(span.Start)))   // handle zero-length spans which Intersect and Overlap won't return true on ever.
            {
                return(new ClassificationSpan(new SnapshotSpan(span.Snapshot, tokenSpan), classification));
            }
            return(null);
        }
Example #4
0
        private ClassificationSpan ClassifyToken(SnapshotSpan span, TokenInfo token, int lineNumber)
        {
            IClassificationType classification = null;

            if (token.Category == TokenCategory.Operator)
            {
                if (token.Trigger == TokenTriggers.MemberSelect)
                {
                    classification = Genero4glClassifierProvider.DotClassification;
                }
            }
            else if (token.Category == TokenCategory.Grouping)
            {
                if ((token.Trigger & TokenTriggers.MatchBraces) != 0)
                {
                    classification = Genero4glClassifierProvider.GroupingClassification;
                }
            }
            else if (token.Category == TokenCategory.Delimiter)
            {
                if (token.Trigger == TokenTriggers.ParameterNext)
                {
                    classification = Genero4glClassifierProvider.CommaClassification;
                }
            }

            if (classification == null)
            {
                CategoryMap.TryGetValue(token.Category, out classification);
            }

            if (classification != null)
            {
                var tokenSpan    = SnapshotSpanToSpan(span.Snapshot, token, lineNumber);
                var intersection = span.Intersection(tokenSpan);

                if (intersection != null && intersection.Value.Length > 0 ||
                    (span.Length == 0 && tokenSpan.Contains(span.Start)))
                { // handle zero-length spans which Intersect and Overlap won't return true on ever.
                    try
                    {
                        return(new ClassificationSpan(new SnapshotSpan(span.Snapshot, tokenSpan), classification));
                    }
                    catch (Exception ex)
                    {
                        // TODO: log and try to get more info...
                    }
                }
            }

            return(null);
        }
Example #5
0
        private static IEnumerable <Tuple <SnapshotSpan, string> > FindInLine(int lineNumber, SnapshotSpan searchRange, FindOptions options, string searchPattern, string replacePattern)
        {
            bool reverse                 = (options & FindOptions.SearchReverse) == FindOptions.SearchReverse;
            ITextSnapshotLine line       = searchRange.Snapshot.GetLineFromLineNumber(lineNumber);
            SnapshotSpan?     searchSpan = searchRange.Intersection(line.ExtentIncludingLineBreak);

            if (searchSpan.HasValue)
            {
                foreach (var result in FindInString(searchSpan.Value.Start, reverse ? searchSpan.Value.End - searchSpan.Value.Start : 0, searchSpan.Value.GetText(), options, searchPattern, replacePattern))
                {
                    yield return(result);
                }
            }
        }
Example #6
0
        private ClassificationSpan ClassifyToken(SnapshotSpan span, TokenInfo token, int lineNumber)
        {
            IClassificationType classification = null;

            if (token.Category == TokenCategory.Operator)
            {
                if (token.Trigger == TokenTriggers.MemberSelect)
                {
                    classification = _provider.DotClassification;
                }
            }
            else if (token.Category == TokenCategory.Grouping)
            {
                if (token.Trigger == (TokenTriggers.MatchBraces | TokenTriggers.ParameterStart))
                {
                    classification = _provider.OpenGroupingClassification;
                }
                else if (token.Trigger == (TokenTriggers.MatchBraces | TokenTriggers.ParameterEnd))
                {
                    classification = _provider.CloseGroupingClassification;
                }
            }
            else if (token.Category == TokenCategory.Delimiter)
            {
                if (token.Trigger == TokenTriggers.ParameterNext)
                {
                    classification = _provider.CommaClassification;
                }
            }

            if (classification == null)
            {
                CategoryMap.TryGetValue(token.Category, out classification);
            }

            if (classification != null)
            {
                var line         = span.Snapshot.GetLineFromLineNumber(lineNumber);
                var index        = line.Start.Position + token.SourceSpan.Start.Column - 1;
                var tokenSpan    = new Span(index, token.SourceSpan.Length);
                var intersection = span.Intersection(tokenSpan);
                if (intersection != null && intersection.Value.Length > 0)
                {
                    return(new ClassificationSpan(new SnapshotSpan(span.Snapshot, intersection.Value), classification));
                }
            }

            return(null);
        }
        public IList <ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
        {
            var results = new List <ClassificationSpan>();

            var artifacts = artifactsGetter();

            // In case the span starts in the middle
            // of an HTML code block, we must always
            // start from the previous artifact.
            int thisArtifact = artifacts.GetLastItemBeforeOrAtPosition(span.Start);

            // For each artifact, classify the artifact and
            // the plain text after it.  If the span starts
            // before the first artifact, the 1st iteration
            // will classify the leading text only.
            while (thisArtifact < artifacts.Count)
            {
                // If the span starts before the first artifact, thisArtifact will be negative.
                if (thisArtifact >= 0)
                {
                    results.AddRange(ClassifyArtifacts(span.Snapshot, artifacts, ref thisArtifact));
                }

                int plainStart = thisArtifact < 0 ? 0 : artifacts[thisArtifact].End;
                thisArtifact++;
                int plainEnd = thisArtifact == artifacts.Count ? span.Snapshot.Length : artifacts[thisArtifact].Start;

                // If artifacts become inconsistent, don't choke
                if (plainEnd <= plainStart)
                {
                    continue;
                }

                // Chop off any part before or after the span being classified
                var plainSpan = span.Intersection(Span.FromBounds(plainStart, plainEnd));

                // If there was no intersection, we've passed the target span and can stop.
                if (plainSpan == null)
                {
                    break;
                }

                results.AddRange(ClassifyPlainSpan(plainSpan.Value));
            }

            return(results);
        }
Example #8
0
		bool Intersects(SnapshotSpan fullSpan, ITextViewLine line, Rect rect) {
			var span = fullSpan.Intersection(line.ExtentIncludingLineBreak);
			if (span == null || span.Value.Length == 0)
				return false;
			var start = line.GetExtendedCharacterBounds(span.Value.Start);
			var end = line.GetExtendedCharacterBounds(span.Value.End - 1);
			double left = Math.Min(start.Left, end.Left) - wpfTextView.ViewportLeft;
			double top = Math.Min(start.Top, end.Top) - wpfTextView.ViewportTop;
			double right = Math.Max(start.Right, end.Right) - wpfTextView.ViewportLeft;
			double bottom = Math.Max(start.Bottom, end.Bottom) - wpfTextView.ViewportTop;
			bool b = left <= right && top <= bottom;
			Debug.Assert(b);
			if (!b)
				return false;
			var r = new Rect(left, top, right - left, bottom - top);
			return r.IntersectsWith(rect);
		}
Example #9
0
        private ClassificationSpan ClassifyToken(SnapshotSpan span, TrackingTokenInfo token)
        {
            IClassificationType classification = null;

            if (token.Category == TokenCategory.Operator)
            {
                if (token.Trigger == TokenTriggers.MemberSelect)
                {
                    classification = _provider.DotClassification;
                }
            }
            else if (token.Category == TokenCategory.Grouping)
            {
                if ((token.Trigger & TokenTriggers.MatchBraces) != 0)
                {
                    classification = _provider.GroupingClassification;
                }
            }
            else if (token.Category == TokenCategory.Delimiter)
            {
                if (token.Trigger == TokenTriggers.ParameterNext)
                {
                    classification = _provider.CommaClassification;
                }
            }

            if (classification == null)
            {
                _provider.CategoryMap.TryGetValue(token.Category, out classification);
            }

            if (classification != null)
            {
                var tokenSpan    = token.ToSnapshotSpan(span.Snapshot);
                var intersection = span.Intersection(tokenSpan);

                if (intersection != null && intersection.Value.Length > 0 ||
                    (span.Length == 0 && tokenSpan.Contains(span.Start)))   // handle zero-length spans which Intersect and Overlap won't return true on ever.
                {
                    return(new ClassificationSpan(new SnapshotSpan(span.Snapshot, tokenSpan), classification));
                }
            }

            return(null);
        }
Example #10
0
        IEnumerable <FindResult> FindAllSingleLineReverse(ITextSnapshotLine startLine, ITextSnapshotLine endLine, SnapshotSpan searchRange, string searchPattern, FindOptions options, StringComparison stringComparison, string replacePattern)
        {
            Debug.Assert((options & FindOptions.SearchReverse) != 0);
            int  startLineNumber = startLine.LineNumber;
            var  snapshot        = searchRange.Snapshot;
            bool onlyWords       = (options & FindOptions.WholeWord) != 0;
            var  regex           = (options & FindOptions.UseRegularExpressions) != 0 ? GetRegex(searchPattern, options) : null;

            for (int lineNumber = endLine.LineNumber; lineNumber >= startLineNumber; lineNumber--)
            {
                var line  = snapshot.GetLineFromLineNumber(lineNumber);
                var range = searchRange.Intersection(line.ExtentIncludingLineBreak);
                if (range == null || range.Value.Length == 0)
                {
                    continue;
                }
                var text  = range.Value.GetText();
                int index = text.Length;
                if (regex != null)
                {
                    foreach (var res in GetRegexResults(regex, range.Value.Start, text, index, searchPattern, options, replacePattern))
                    {
                        yield return(res);
                    }
                }
                else
                {
                    while (index > 0)
                    {
                        index = text.LastIndexOf(searchPattern, index - 1, index, stringComparison);
                        if (index < 0)
                        {
                            break;
                        }
                        if (!onlyWords || UnicodeUtilities.IsWord(line, range.Value.Start.Position - line.Start.Position + index, searchPattern.Length))
                        {
                            yield return(new FindResult(range.Value.Start.Position + index, searchPattern.Length, replacePattern));
                        }
                        index += searchPattern.Length - 1;
                    }
                }
            }
        }
Example #11
0
		void Update(SnapshotSpan span, ref HashSet<ITextViewLine> checkedLines) {
			Debug.Assert(span.Snapshot == wpfTextViewHost.TextView.TextSnapshot);
			var intersection = span.Intersection(wpfTextViewHost.TextView.TextViewLines.FormattedSpan);
			if (intersection == null)
				return;
			var point = intersection.Value.Start;
			while (point <= intersection.Value.End) {
				var line = wpfTextViewHost.TextView.TextViewLines.GetTextViewLineContainingBufferPosition(point);
				if (line == null)
					break;
				if (checkedLines == null)
					checkedLines = new HashSet<ITextViewLine>();
				if (!checkedLines.Contains(line)) {
					checkedLines.Add(line);
					Update(line);
				}
				if (line.IsLastDocumentLine())
					break;
				point = line.GetPointAfterLineBreak();
			}
		}
Example #12
0
		bool Intersects(SnapshotSpan fullSpan, ITextViewLine line, Rect rect) {
			var span = fullSpan.Intersection(line.ExtentIncludingLineBreak);
			if (span == null || span.Value.Length == 0)
				return false;
			var start = line.GetExtendedCharacterBounds(span.Value.Start);
			var end = line.GetExtendedCharacterBounds(span.Value.End - 1);
			double left = Math.Min(start.Left, end.Left) - wpfTextView.ViewportLeft;
			double top = Math.Min(start.Top, end.Top) - wpfTextView.ViewportTop;
			double right = Math.Max(start.Right, end.Right) - wpfTextView.ViewportLeft;
			double bottom = Math.Max(start.Bottom, end.Bottom) - wpfTextView.ViewportTop;
			bool b = left <= right && top <= bottom;
			Debug.Assert(b);
			if (!b)
				return false;
			var r = new Rect(left, top, right - left, bottom - top);
			return r.IntersectsWith(rect);
		}
Example #13
0
		IEnumerable<FindResult> FindAllSingleLineReverse(ITextSnapshotLine startLine, ITextSnapshotLine endLine, SnapshotSpan searchRange, string searchPattern, FindOptions options, StringComparison stringComparison, string replacePattern) {
			Debug.Assert((options & FindOptions.SearchReverse) != 0);
			int startLineNumber = startLine.LineNumber;
			var snapshot = searchRange.Snapshot;
			bool onlyWords = (options & FindOptions.WholeWord) != 0;
			var regex = (options & FindOptions.UseRegularExpressions) != 0 ? GetRegex(searchPattern, options) : null;
			for (int lineNumber = endLine.LineNumber; lineNumber >= startLineNumber; lineNumber--) {
				var line = snapshot.GetLineFromLineNumber(lineNumber);
				var range = searchRange.Intersection(line.ExtentIncludingLineBreak);
				if (range == null || range.Value.Length == 0)
					continue;
				var text = range.Value.GetText();
				int index = text.Length;
				if (regex != null) {
					foreach (var res in GetRegexResults(regex, range.Value.Start, text, index, searchPattern, options, replacePattern))
						yield return res;
				}
				else {
					while (index > 0) {
						index = text.LastIndexOf(searchPattern, index - 1, index, stringComparison);
						if (index < 0)
							break;
						if (!onlyWords || UnicodeUtilities.IsWord(line, range.Value.Start.Position - line.Start.Position + index, searchPattern.Length))
							yield return new FindResult(range.Value.Start.Position + index, searchPattern.Length, replacePattern);
						index += searchPattern.Length - 1;
					}
				}
			}
		}
Example #14
0
        private static ClassificationSpan GetClassificationSpan(SnapshotSpan span, TokenInfo token, int lineNumber, IClassificationType classification) {
            var tokenSpan = SnapshotSpanToSpan(span.Snapshot, token, lineNumber);
            var intersection = span.Intersection(tokenSpan);

            if (intersection != null && intersection.Value.Length > 0 ||
                (span.Length == 0 && tokenSpan.Contains(span.Start))) { // handle zero-length spans which Intersect and Overlap won't return true on ever.
                return new ClassificationSpan(new SnapshotSpan(span.Snapshot, tokenSpan), classification);
            }
            return null;
        }
Example #15
0
        /// <summary>
        /// Scans the given SnapshotSpan for potential matches for this classification.
        /// </summary>
        /// <param name="span">The span currently being classified</param>
        /// <returns>A list of ClassificationSpans that represent spans identified to be of this classification</returns>
        public IList <ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
        {
            List <ClassificationSpan> classifications = new List <ClassificationSpan>();

            try
            {
                using (ReaderTextProvider textProvider = new ReaderTextProvider(new TextSnapshotToTextReader(span.Snapshot)))
                {
                    // We parse the whole buffer for statements, not just the current line,
                    // because we need to know if we're on a continuation line.
                    // TODO: Cache the "beginning of logical line" points so that we
                    // don't have to re-parse everything each time?
                    IEnumerable <Statement <StatementType, ParserTokenType> > statements = null;

                    switch (this.language)
                    {
                    case ParserLanguage.Rtype:
                        statements = RtypeStatementParser.ParseStatements(new Position(0, 0, 0), textProvider);
                        break;

                    case ParserLanguage.Xml:
                        statements = XmlStatementParser.ParseStatements(new Position(0, 0, 0), textProvider);
                        break;
                    }

                    int spanStart = span.Start.Position;
                    int spanEnd   = span.End.Position;

                    foreach (var statement in statements)
                    {
                        if (statement.Range.End.Offset <= spanStart)
                        {
                            continue;
                        }

                        if (statement.Range.Start.Offset >= spanEnd)
                        {
                            break;
                        }

                        // for a comment statement, we don't have to look at the
                        // individual tokens...
                        if (statement.StatementType == StatementType.Comment)
                        {
                            var classification = this.commentType;

                            // Ensure the returned span doesn't extend past the request!
                            var classifiedSpan = span.Intersection(span.Snapshot.CreateSpanFromSwix(statement.Range));

                            if (classifiedSpan.HasValue)
                            {
                                classifications.Add(new ClassificationSpan(classifiedSpan.Value, classification));
                            }
                        }
                        else
                        {
                            foreach (var token in statement.AllTokens)
                            {
                                if (token.Range.End.Offset <= span.Start.Position)
                                {
                                    continue;
                                }

                                if (token.Range.Start.Offset >= span.End.Position)
                                {
                                    break;
                                }

                                IClassificationType classification = null;

                                switch (token.TokenType)
                                {
                                case ParserTokenType.Unknown:
                                    classification = this.delimiterType;
                                    break;

                                case ParserTokenType.Whitespace:
                                    classification = this.whitespaceType;
                                    break;

                                case ParserTokenType.Comment:
                                    classification = this.commentType;
                                    break;

                                case ParserTokenType.UseKeyword:
                                    classification = this.keywordType;
                                    break;

                                case ParserTokenType.Object:
                                    classification = this.objectType;
                                    break;

                                case ParserTokenType.PropertyName:
                                    classification = this.propertyType;
                                    break;

                                case ParserTokenType.Equals:
                                    classification = this.assignmentType;
                                    break;

                                case ParserTokenType.PropertyValue:
                                case ParserTokenType.DoubleQuote:
                                case ParserTokenType.SingleQuote:
                                    classification = this.valueType;
                                    break;

                                case ParserTokenType.NamespacePrefix:
                                    classification = this.namespacePrefixType;
                                    break;

                                case ParserTokenType.NamespacePrefixDeclaration:
                                    // TODO
                                    break;

                                case ParserTokenType.NamespaceDeclaration:
                                    // TODO
                                    break;

                                case ParserTokenType.AttachedPropertyObject:
                                    classification = this.attachableObjectType;
                                    break;

                                case ParserTokenType.LeftAngle:
                                case ParserTokenType.RightAngle:
                                case ParserTokenType.Colon:
                                case ParserTokenType.Slash:
                                case ParserTokenType.Period:
                                    classification = this.delimiterType;
                                    break;

                                default:
                                    break;
                                }

                                if (classification != null)
                                {
                                    // Ensure the returned span doesn't extend past the request!
                                    var classifiedSpan = span.Intersection(span.Snapshot.CreateSpanFromSwix(token.Range));

                                    if (classifiedSpan.HasValue)
                                    {
                                        classifications.Add(new ClassificationSpan(classifiedSpan.Value, classification));
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception)
            {
            }

            return(classifications);
        }
        public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
        {
            var results = new List<ClassificationSpan>();

            var artifacts = artifactsGetter();

            // In case the span starts in the middle
            // of an HTML code block, we must always
            // start from the previous artifact.
            int thisArtifact = artifacts.GetLastItemBeforeOrAtPosition(span.Start);

            // For each artifact, classify the artifact and
            // the plain text after it.  If the span starts
            // before the first artifact, the 1st iteration
            // will classify the leading text only.
            while (thisArtifact < artifacts.Count)
            {
                // If the span starts before the first artifact, thisArtifact will be negative.
                if (thisArtifact >= 0)
                    results.AddRange(ClassifyArtifacts(span.Snapshot, artifacts, ref thisArtifact));

                int plainStart = thisArtifact < 0 ? 0 : artifacts[thisArtifact].End;
                thisArtifact++;
                int plainEnd = thisArtifact == artifacts.Count ? span.Snapshot.Length : artifacts[thisArtifact].Start;

                // If artifacts become inconsistent, don't choke
                if (plainEnd <= plainStart)
                    continue;

                // Chop off any part before or after the span being classified
                var plainSpan = span.Intersection(Span.FromBounds(plainStart, plainEnd));

                // If there was no intersection, we've passed the target span and can stop.
                if (plainSpan == null) break;

                results.AddRange(ClassifyPlainSpan(plainSpan.Value));
            }

            return results;
        }
Example #17
0
        /// <summary>
        /// Adds classification spans to the given collection.
        /// Scans a contiguous sub-<paramref name="span"/> of a larger code span which starts at <paramref name="codeStartLine"/>.
        /// </summary>
        private void AddClassifications(JSScanner JSScanner, List <ClassificationSpan> classifications, SnapshotSpan span)
        {
            Debug.Assert(span.Length > 0);

            var snapshot  = span.Snapshot;
            int firstLine = snapshot.GetLineNumberFromPosition(span.Start);
            int lastLine  = snapshot.GetLineNumberFromPosition(span.End - 1);

            Contract.Assert(firstLine >= 0);

            _tokenCache.EnsureCapacity(snapshot.LineCount);

            // find the closest line preceding firstLine for which we know categorizer state, stop at the codeStartLine:
            LineTokenization lineTokenization;
            int    currentLine = _tokenCache.IndexOfPreviousTokenization(firstLine, 0, out lineTokenization) + 1;
            object state       = lineTokenization.State;

            // track the previous 2 tokens to adjust our classifications of keywords
            // when they shouldn't be displayed as keywords...
            TokenInfoWithLine?prevToken = null, prevPrevToken = null;

            // initialize the previous tokens so we can handle things like:
            //      foo.
            //          get()
            // even if we're called on the line for get()
            int prevLine = currentLine - 1;

            while (prevLine >= 0 && prevToken == null)
            {
                LineTokenization prevLineTokenization = GetPreviousTokenization(JSScanner, snapshot, firstLine, prevLine);
                for (int i = prevLineTokenization.Tokens.Length - 1; i >= 0 && prevToken == null; i--)
                {
                    var tempToken = prevLineTokenization.Tokens[i];
                    if (IsValidPreviousToken(ref tempToken))
                    {
                        prevToken     = prevPrevToken;
                        prevPrevToken = new TokenInfoWithLine()
                        {
                            TokenInfo = tempToken, Line = prevLine
                        };
                    }
                }
                prevLine--;
            }

            while (currentLine <= lastLine)
            {
                if (!_tokenCache.TryGetTokenization(currentLine, out lineTokenization))
                {
                    lineTokenization         = TokenizeLine(JSScanner, snapshot, state, currentLine);
                    _tokenCache[currentLine] = lineTokenization;
                }

                state = lineTokenization.State;

                for (int i = 0; i < lineTokenization.Tokens.Length; i++)
                {
                    var token = lineTokenization.Tokens[i];

                    if (token.Category == TokenCategory.IncompleteMultiLineStringLiteral || token.Category == TokenCategory.Comment)
                    {
                        IClassificationType type;
                        switch (token.Category)
                        {
                        case TokenCategory.IncompleteMultiLineStringLiteral:
                            type = _provider.StringLiteral;
                            break;

                        case TokenCategory.Comment:
                            type = _provider.Comment;
                            break;

                        default:
                            type = null;
                            break;
                        }

                        Debug.Assert(type != null, "We should have a defined ClassificationType for every token.");

                        // we need to walk backwards to find the start of this multi-line string...
                        TokenInfo startToken = token;
                        int       validPrevLine;
                        int       length = startToken.SourceSpan.Length;
                        if (i == 0)
                        {
                            length += GetLeadingMultiLineTokens(JSScanner, snapshot, token.Category, firstLine, currentLine, out validPrevLine, ref startToken);
                        }
                        else
                        {
                            validPrevLine = currentLine;
                        }

                        if (i == lineTokenization.Tokens.Length - 1)
                        {
                            length += GetTrailingMultiLineTokens(JSScanner, snapshot, token.Category, currentLine, state);
                        }

                        var tokenSpan    = new Span(SnapshotSpanToSpan(snapshot, startToken, validPrevLine).Start, length);
                        var intersection = span.Intersection(tokenSpan);

                        if ((intersection != null && intersection.Value.Length > 0) ||
                            (span.Length == 0 && tokenSpan.Contains(span.Start)) // handle zero-length spans
                            )
                        {
                            classifications.Add(new ClassificationSpan(new SnapshotSpan(snapshot, tokenSpan), type));
                        }
                    }
                    else
                    {
                        ClassificationSpan classification = null;
                        if (token.Category == TokenCategory.Keyword)
                        {
                            // check and see if we're not really a keyword...
                            if (IsKeywordInIdentifierContext(snapshot, prevToken, prevPrevToken, new TokenInfoWithLine()
                            {
                                TokenInfo = token, Line = currentLine
                            }))
                            {
                                classification = GetClassificationSpan(
                                    span,
                                    token,
                                    currentLine,
                                    CategoryMap[TokenCategory.Identifier]
                                    );
                            }
                        }
                        if (classification == null)
                        {
                            classification = ClassifyToken(span, token, currentLine);
                        }

                        if (classification != null)
                        {
                            classifications.Add(classification);
                        }
                    }

                    if (IsValidPreviousToken(ref token))
                    {
                        prevPrevToken = prevToken;
                        prevToken     = new TokenInfoWithLine()
                        {
                            TokenInfo = token, Line = currentLine
                        };
                    }
                }

                currentLine++;
            }
        }
Example #18
0
        private ClassificationSpan ClassifyToken(SnapshotSpan span, TokenInfo token, int lineNumber)
        {
            IClassificationType classification = null;

            if (token.Category == TokenCategory.Operator) {
                if (token.Trigger == TokenTriggers.MemberSelect) {
                    classification = _provider.DotClassification;
                }
            } else if (token.Category == TokenCategory.Grouping) {
                if (token.Trigger == (TokenTriggers.MatchBraces | TokenTriggers.ParameterStart)) {
                    classification = _provider.OpenGroupingClassification;
                } else if (token.Trigger == (TokenTriggers.MatchBraces | TokenTriggers.ParameterEnd)) {
                    classification = _provider.CloseGroupingClassification;
                }
            } else if (token.Category == TokenCategory.Delimiter) {
                if (token.Trigger == TokenTriggers.ParameterNext) {
                    classification = _provider.CommaClassification;
                }
            }

            if (classification == null) {
                CategoryMap.TryGetValue(token.Category, out classification);
            }

            if (classification != null) {
                var line = span.Snapshot.GetLineFromLineNumber(lineNumber);
                var index = line.Start.Position + token.SourceSpan.Start.Column - 1;
                var tokenSpan = new Span(index, token.SourceSpan.Length);
                var intersection = span.Intersection(tokenSpan);
                if (intersection != null && intersection.Value.Length > 0) {
                    return new ClassificationSpan(new SnapshotSpan(span.Snapshot, tokenSpan), classification);
                }
            }

            return null;
        }
Example #19
0
        /// <summary>
        /// Adds classification spans to the given collection.
        /// Scans a contiguous sub-<paramref name="span"/> of a larger code span which starts at <paramref name="codeStartLine"/>.
        /// </summary>
        private void AddClassifications(JSScanner JSScanner, List<ClassificationSpan> classifications, SnapshotSpan span) {
            Debug.Assert(span.Length > 0);

            var snapshot = span.Snapshot;
            int firstLine = snapshot.GetLineNumberFromPosition(span.Start);
            int lastLine = snapshot.GetLineNumberFromPosition(span.End - 1);

            Contract.Assert(firstLine >= 0);

            _tokenCache.EnsureCapacity(snapshot.LineCount);

            // find the closest line preceding firstLine for which we know categorizer state, stop at the codeStartLine:
            LineTokenization lineTokenization;
            int currentLine = _tokenCache.IndexOfPreviousTokenization(firstLine, 0, out lineTokenization) + 1;
            object state = lineTokenization.State;

            // track the previous 2 tokens to adjust our classifications of keywords
            // when they shouldn't be displayed as keywords...
            TokenInfoWithLine? prevToken = null, prevPrevToken = null;

            // initialize the previous tokens so we can handle things like:
            //      foo.
            //          get()
            // even if we're called on the line for get()
            int prevLine = currentLine - 1;
            while (prevLine >= 0 && prevToken == null) {
                LineTokenization prevLineTokenization = GetPreviousTokenization(JSScanner, snapshot, firstLine, prevLine);
                for (int i = prevLineTokenization.Tokens.Length - 1; i >= 0 && prevToken == null; i--) {
                    var tempToken = prevLineTokenization.Tokens[i];
                    if (IsValidPreviousToken(ref tempToken)) {
                        prevToken = prevPrevToken;
                        prevPrevToken = new TokenInfoWithLine() { TokenInfo = tempToken, Line = prevLine };
                    }
                }
                prevLine--;
            }

            while (currentLine <= lastLine) {
                if (!_tokenCache.TryGetTokenization(currentLine, out lineTokenization)) {
                    lineTokenization = TokenizeLine(JSScanner, snapshot, state, currentLine);
                    _tokenCache[currentLine] = lineTokenization;
                }

                state = lineTokenization.State;

                for (int i = 0; i < lineTokenization.Tokens.Length; i++) {
                    var token = lineTokenization.Tokens[i];

                    if (token.Category == TokenCategory.IncompleteMultiLineStringLiteral || token.Category == TokenCategory.Comment) {
                        IClassificationType type;
                        switch (token.Category) {
                            case TokenCategory.IncompleteMultiLineStringLiteral:
                                type = _provider.StringLiteral;
                                break;
                            case TokenCategory.Comment:
                                type = _provider.Comment;
                                break;
                            default:
                                type = null;
                                break;
                        }

                        Debug.Assert(type != null, "We should have a defined ClassificationType for every token.");

                        // we need to walk backwards to find the start of this multi-line string...
                        TokenInfo startToken = token;
                        int validPrevLine;
                        int length = startToken.SourceSpan.Length;
                        if (i == 0) {
                            length += GetLeadingMultiLineTokens(JSScanner, snapshot, token.Category, firstLine, currentLine, out validPrevLine, ref startToken);
                        } else {
                            validPrevLine = currentLine;
                        }

                        if (i == lineTokenization.Tokens.Length - 1) {
                            length += GetTrailingMultiLineTokens(JSScanner, snapshot, token.Category, currentLine, state);
                        }

                        var tokenSpan = new Span(SnapshotSpanToSpan(snapshot, startToken, validPrevLine).Start, length);
                        var intersection = span.Intersection(tokenSpan);

                        if ((intersection != null && intersection.Value.Length > 0) ||
                            (span.Length == 0 && tokenSpan.Contains(span.Start)) // handle zero-length spans
                        ) {
                            classifications.Add(new ClassificationSpan(new SnapshotSpan(snapshot, tokenSpan), type));
                        }
                    } else {
                        ClassificationSpan classification = null;
                        if (token.Category == TokenCategory.Keyword) {
                            // check and see if we're not really a keyword...
                            if (IsKeywordInIdentifierContext(snapshot, prevToken, prevPrevToken, new TokenInfoWithLine() { TokenInfo = token, Line = currentLine })) {
                                classification = GetClassificationSpan(
                                    span,
                                    token,
                                    currentLine,
                                    CategoryMap[TokenCategory.Identifier]
                                );
                            }
                        }
                        if (classification == null) {
                            classification = ClassifyToken(span, token, currentLine);
                        }

                        if (classification != null) {
                            classifications.Add(classification);
                        }
                    }

                    if (IsValidPreviousToken(ref token)) {
                        prevPrevToken = prevToken;
                        prevToken = new TokenInfoWithLine() { TokenInfo = token, Line = currentLine };
                    }
                }

                currentLine++;
            }
        }
Example #20
0
        private ClassificationSpan ClassifyToken(SnapshotSpan span, TokenInfo token, int lineNumber)
        {
            IClassificationType classification = null;

            if (token.Category == TokenCategory.Operator) {
                if (token.Trigger == TokenTriggers.MemberSelect) {
                    classification = _provider.DotClassification;
                }
            } else if (token.Category == TokenCategory.Grouping) {
                if ((token.Trigger & TokenTriggers.MatchBraces) != 0) {
                    classification = _provider.GroupingClassification;
                }
            } else if (token.Category == TokenCategory.Delimiter) {
                if (token.Trigger == TokenTriggers.ParameterNext) {
                    classification = _provider.CommaClassification;
                }
            }

            if (classification == null) {
                CategoryMap.TryGetValue(token.Category, out classification);
            }

            if (classification != null) {
                var tokenSpan = SnapshotSpanToSpan(span.Snapshot, token, lineNumber);
                var intersection = span.Intersection(tokenSpan);

                if (intersection != null && intersection.Value.Length > 0 ||
                    (span.Length == 0 && tokenSpan.Contains(span.Start))) { // handle zero-length spans which Intersect and Overlap won't return true on ever.
                    return new ClassificationSpan(new SnapshotSpan(span.Snapshot, tokenSpan), classification);
                }
            }

            return null;
        }
        /// <summary>
        /// Scans the given SnapshotSpan for potential matches for this classification.
        /// </summary>
        /// <param name="span">The span currently being classified</param>
        /// <returns>A list of ClassificationSpans that represent spans identified to be of this classification</returns>
        public IList <ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
        {
            List <ClassificationSpan> classifications = new List <ClassificationSpan>();

            // Extend the span to full lines, and tokenize/classify...
            var firstLine = span.Start.GetContainingLine();
            var lastLine  = (span.End - 1).GetContainingLine();

            // Remove any errors in the span?
            ////this.RemoveTagSpans(s => span.Contains(s.Span.GetSpan(span.Snapshot)));
            ////var toRemove = this.errorListProvider.Tasks.OfType<Task>().Where(
            ////    t => firstLine.LineNumber <= t.Line && t.Line <= lastLine.LineNumber).ToList();
            ////foreach (var task in toRemove)
            ////{
            ////    this.errorListProvider.Tasks.Remove(task);
            ////}

            SnapshotTextProvider textProvider = new SnapshotTextProvider(span.Snapshot, span);

            var statements = StatementParser.ParseStatements(
                new Position(firstLine.Start.Position, firstLine.LineNumber, 0),
                textProvider);

            foreach (var statement in statements)
            {
                // If the statement ends before the requested span, ignore it completely.
                if (statement.AllTokens.Last().Range.End.Offset <= span.Start.Position)
                {
                    continue;
                }

                foreach (var token in statement.AllTokens)
                {
                    // Skip any tokens entirely before or after the span...
                    if (token.Range.End.Offset <= span.Start.Position)
                    {
                        continue;
                    }
                    else if (token.Range.Start.Offset >= span.End.Position)
                    {
                        break;
                    }

                    IClassificationType classification = null;

                    switch (token.TokenType)
                    {
                    case ParserTokenType.Unknown:
                        break;

                    case ParserTokenType.Whitespace:
                        classification = this.whitespaceType;
                        break;

                    case ParserTokenType.Comment:
                        classification = this.commentType;
                        break;

                    case ParserTokenType.TypeKeyword:
                        classification = this.keywordType;
                        break;

                    case ParserTokenType.MessageTypeDefinition:
                        classification = this.messageTypeDefinitionType;
                        break;

                    case ParserTokenType.MessageTypeRange:
                        classification = this.messageTypeRangeType;
                        break;

                    case ParserTokenType.MessageType:
                        classification = this.messageTypeType;
                        break;

                    case ParserTokenType.MessageName:
                        classification = this.messageNameType;
                        break;

                    case ParserTokenType.LeftBrace:
                        classification = this.replacementDelimiterStart;
                        break;

                    case ParserTokenType.RightBrace:
                        classification = this.replacementDelimiterEnd;
                        break;

                    case ParserTokenType.LeftBracket:
                    case ParserTokenType.RightBracket:
                    case ParserTokenType.Comma:
                    case ParserTokenType.Colon:
                        classification = this.replacementDelimiterType;
                        break;

                    case ParserTokenType.ReplacementName:
                        classification = this.replacementNameType;
                        break;

                    case ParserTokenType.ReplacementType:
                        classification = this.replacementTypeType;
                        break;

                    case ParserTokenType.ReplacementPosition:
                        classification = this.replacementPositionType;
                        break;

                    case ParserTokenType.ReplacementAlignment:
                        classification = this.replacementAlignmentType;
                        break;

                    case ParserTokenType.ReplacementFormat:
                        classification = this.replacementFormatType;
                        break;

                    case ParserTokenType.Value:
                        classification = this.stringType;
                        break;

                    case ParserTokenType.Escape:
                        classification = this.escapeType;
                        break;

                    default:
                        break;
                    }

                    if (classification != null)
                    {
                        SnapshotSpan?intersect = span.Intersection(token.Range.AsSpan());

                        if (intersect.HasValue)
                        {
                            classifications.Add(new ClassificationSpan(intersect.Value, classification));
                        }
                    }

                    // If there were any errors, pass those up...
                    ////if (token.Errors != null)
                    ////{
                    ////    foreach (Error error in token.Errors)
                    ////    {
                    ////        ErrorTag tag = new ErrorTag("syntax error", error.Message);
                    ////        this.CreateTagSpan(span.Snapshot.CreateTrackingSpan(token), tag);

                    ////        ErrorTask task = new ErrorTask();
                    ////        task.Document = this.filename;
                    ////        task.Line = error.Range.Start.Line;
                    ////        task.Column = error.Range.Start.Column;
                    ////        task.Text = error.Message;

                    ////        // inline?
                    ////        task.Category = TaskCategory.CodeSense;
                    ////        task.ErrorCategory = TaskErrorCategory.Error;

                    ////        // Why do we have to do this ourselves?
                    ////        if (!string.IsNullOrWhiteSpace(task.Document))
                    ////        {
                    ////            IVsUIHierarchy hierarchy;
                    ////            uint item;
                    ////            IVsWindowFrame frame;

                    ////            if (VsShellUtilities.IsDocumentOpen(
                    ////                this.provider.ServiceProvider,
                    ////                task.Document,
                    ////                VSConstants.LOGVIEWID.Any_guid,
                    ////                out hierarchy,
                    ////                out item,
                    ////                out frame))
                    ////            {
                    ////                task.HierarchyItem = hierarchy;
                    ////            }

                    ////            task.Navigate += Task_Navigate;
                    ////        }

                    ////        this.errorListProvider.Tasks.Add(task);
                    ////    }
                    ////}
                }

                // If the statement ends at or after the requested span, we're done!
                if (statement.AllTokens.Last().Range.End.Offset >= span.End.Position)
                {
                    break;
                }
            }

            return(classifications);
        }