示例#1
0
        /// <summary>
        /// Handles single or double quoted strings
        /// </summary>
        protected virtual ITextRange HandleString(bool addToken = true)
        {
            var start = this._cs.Position;
            var quote = this._cs.CurrentChar;

            // since the escape char is exactly the string openning char we say we start in escaped mode
            // it will get reset by the first char regardless what it is, but it will keep the '' case honest
            this._cs.MoveToNextChar();

            while (!this._cs.IsEndOfStream() && !this._cs.IsAtNewLine())
            {
                if (this._cs.CurrentChar == '\\' && this._cs.NextChar == quote)
                {
                    this._cs.Advance(2);
                }

                if (this._cs.CurrentChar == quote)
                {
                    this._cs.MoveToNextChar();
                    break;
                }

                this._cs.MoveToNextChar();
            }

            var range = TextRange.FromBounds(start, this._cs.Position);

            if (range.Length > 0)
            {
                this.Tokens.Add(GetStringToken(start, range.Length));
            }

            return(range);
        }
示例#2
0
        /// <summary>
        /// Calculates range that is an intersection of the supplied ranges.
        /// </summary>
        /// <returns>Intersection or empty range if ranges don't intersect</returns>
        public static ITextRange Intersection(ITextRange range1, int rangeStart, int rangeLength)
        {
            var start = Math.Max(range1.Start, rangeStart);
            var end   = Math.Min(range1.End, rangeStart + rangeLength);

            return(start <= end?TextRange.FromBounds(start, end) : TextRange.EmptyRange);
        }
示例#3
0
        /// <summary>
        /// Calculates range that includes both supplied ranges.
        /// </summary>
        public static ITextRange Union(ITextRange range1, ITextRange range2)
        {
            var start = Math.Min(range1.Start, range2.Start);
            var end   = Math.Max(range1.End, range2.End);

            return(start <= end?TextRange.FromBounds(start, end) : TextRange.EmptyRange);
        }
示例#4
0
        /// <summary>
        /// Calculates range that is an intersection of the supplied ranges.
        /// </summary>
        /// <returns>Intersection or empty range if ranges don't intersect</returns>
        public static ITextRange Intersection(ITextRange range1, ITextRange range2)
        {
            int start = Math.Max(range1.Start, range2.Start);
            int end   = Math.Min(range1.End, range2.End);

            return(start <= end?TextRange.FromBounds(start, end) : TextRange.EmptyRange);
        }
示例#5
0
        protected override void RemoveSensitiveTokens(int position, TextRangeCollection <JadeToken> tokens)
        {
            if (tokens.Count > 0)
            {
                var line  = this.TextBuffer.CurrentSnapshot.GetLineFromPosition(position);
                var index = tokens.GetFirstItemAfterPosition(line.Start);
                if (index >= 0)
                {
                    for (var i = index; i >= 0; i--)
                    {
                        if (IsAnchorToken(tokens[i].TokenType))
                        {
                            line = this.TextBuffer.CurrentSnapshot.GetLineFromPosition(tokens[i].Start);
                            break;
                        }
                    }
                }

                int start = line.Start;
                var end   = tokens[tokens.Count - 1].End;

                if (start < end)
                {
                    tokens.RemoveInRange(TextRange.FromBounds(start, end), true);
                }
            }

            base.RemoveSensitiveTokens(position, tokens);
        }
示例#6
0
        private ITextRange GetAttributeValue()
        {
            int start = _cs.Position;

            while (!_cs.IsEndOfStream() && !_cs.IsWhiteSpace() && _cs.CurrentChar != ')')
            {
                _cs.MoveToNextChar();
            }

            return(TextRange.FromBounds(start, _cs.Position));
        }
示例#7
0
        public CharacterStream(ITextProvider textProvider, ITextRange range)
        {
            _text = textProvider;

            int end = Math.Min(_text.Length, range.End);

            _range = TextRange.FromBounds(range.Start, end);

            Position     = _range.Start;
            _currentChar = _text[_range.Start];
        }
示例#8
0
        private ITextRange GetAttributeValue()
        {
            var start = this._cs.Position;

            while (!this._cs.IsEndOfStream() && !this._cs.IsWhiteSpace() && this._cs.CurrentChar != ')')
            {
                this._cs.MoveToNextChar();
            }

            return(TextRange.FromBounds(start, this._cs.Position));
        }
        public CharacterStream(ITextProvider textProvider, ITextRange range)
        {
            this._text = textProvider;

            var end = Math.Min(this._text.Length, range.End);

            this._range = TextRange.FromBounds(range.Start, end);

            this.Position     = this._range.Start;
            this._currentChar = this._text[this._range.Start];
        }
示例#10
0
        /// <summary>
        /// Collects 'identifier' sequence. Identifier consists of ANSI characters and decimal digits.
        /// </summary>
        /// <returns>Identifier range</returns>
        protected virtual ITextRange ParseIdentifier()
        {
            var start = this._cs.Position;

            while (!this._cs.IsEndOfStream() && !this._cs.IsWhiteSpace() &&
                   (this._cs.IsAnsiLetter() || this._cs.IsDecimal() || this._cs.CurrentChar == '_'))
            {
                this._cs.MoveToNextChar();
            }

            return(TextRange.FromBounds(start, this._cs.Position));
        }
示例#11
0
        private ITextRange GetAttribute()
        {
            var start = this._cs.Position;

            while (!this._cs.IsEndOfStream() && !this._cs.IsWhiteSpace() &&
                   (this._cs.IsAnsiLetter() || this._cs.IsDecimal() ||
                    this._cs.CurrentChar == '_' || this._cs.CurrentChar == '-' ||
                    this._cs.CurrentChar == ':') || this._cs.CurrentChar == '.')
            {
                this._cs.MoveToNextChar();
            }

            return(TextRange.FromBounds(start, this._cs.Position));
        }
示例#12
0
        protected override ITextRange HandleString(bool addToken = true)
        {
            int  start = _cs.Position;
            char quote = _cs.CurrentChar;

            // since the escape char is exactly the string openning char we say we start in escaped mode
            // it will get reset by the first char regardless what it is, but it will keep the '' case honest
            _cs.MoveToNextChar();

            while (!_cs.IsEndOfStream() && !_cs.IsAtNewLine())
            {
                if (_cs.CurrentChar == '\\' && _cs.NextChar == quote)
                {
                    _cs.Advance(2);
                }

                if (_cs.CurrentChar == quote)
                {
                    _cs.MoveToNextChar();
                    break;
                }

                if (_cs.CurrentChar == '<' && (_cs.NextChar == '/' || Char.IsLetter(_cs.NextChar)))
                {
                    if (_cs.Position > start)
                    {
                        Tokens.Add(GetStringToken(start, _cs.Position - start));
                    }

                    OnHtml();

                    start = _cs.Position;
                }
                else
                {
                    _cs.MoveToNextChar();
                }
            }

            var range = TextRange.FromBounds(start, _cs.Position);

            if (range.Length > 0)
            {
                Tokens.Add(GetStringToken(start, range.Length));
            }

            return(range);
        }
示例#13
0
        /// <summary>
        /// Collects all characters up to the next whitespace always
        /// including the current character
        /// </summary>
        /// <returns>Sequence range</returns>
        protected ITextRange GetNonWSSequence(string terminators)
        {
            var start = this._cs.Position;

            this._cs.MoveToNextChar();

            while (!this._cs.IsEndOfStream() && !this._cs.IsWhiteSpace())
            {
                if (terminators.IndexOf(this._cs.CurrentChar) != -1)
                {
                    return(TextRange.FromBounds(start, this._cs.Position));
                }

                this._cs.MoveToNextChar();
            }

            return(TextRange.FromBounds(start, this._cs.Position));
        }
示例#14
0
        /// <summary>
        /// Collects all characters up to the next whitespace
        /// </summary>
        /// <param name="terminator">Terminator character</param>
        /// <param name="inclusive">True if sequence includes the terminator,
        /// false if advance should stop at the terminator character</param>
        /// <returns>Sequence range</returns>
        protected virtual ITextRange GetNonWSSequence(char terminator, bool inclusive)
        {
            var start = this._cs.Position;

            while (!this._cs.IsEndOfStream() && !this._cs.IsWhiteSpace())
            {
                if (this._cs.CurrentChar == terminator && terminator != '\0')
                {
                    if (inclusive)
                    {
                        this._cs.MoveToNextChar();
                    }

                    break;
                }

                this._cs.MoveToNextChar();
            }

            return(TextRange.FromBounds(start, this._cs.Position));
        }
示例#15
0
        protected ITextRange ParseTagName()
        {
            var start = this._cs.Position;
            var count = 0;

            while (!this._cs.IsEndOfStream() && !this._cs.IsWhiteSpace() &&
                   (this._cs.IsAnsiLetter() || this._cs.IsDecimal() || this._cs.CurrentChar == '_' || (count > 0 && this._cs.CurrentChar == '-')))
            {
                if (this._cs.CurrentChar == ':')
                {
                    if (this._cs.NextChar != '_' && (this._cs.NextChar < 'A' || this._cs.NextChar > 'z'))
                    {
                        break; // allow tags with namespaces
                    }
                }

                this._cs.MoveToNextChar();
                count++;
            }

            return(TextRange.FromBounds(start, this._cs.Position));
        }
示例#16
0
        /// <summary>
        /// Compares two collections and calculates 'changed' range. In case this collection
        /// or comparand are empty, uses lowerBound and upperBound values as range
        /// delimiters. Typically lowerBound is 0 and upperBound is lentgh of the file.
        /// </summary>
        /// <param name="otherCollection">Collection to compare to</param>
        public virtual ITextRange RangeDifference(IEnumerable <ITextRange> otherCollection, int lowerBound, int upperBound)
        {
            if (otherCollection == null)
            {
                return(TextRange.FromBounds(lowerBound, upperBound));
            }

            var other = new TextRangeCollection <ITextRange>(otherCollection);

            if (this.Count == 0 && other.Count == 0)
            {
                return(TextRange.EmptyRange);
            }

            if (this.Count == 0)
            {
                return(TextRange.FromBounds(lowerBound, upperBound));
            }

            if (other.Count == 0)
            {
                return(TextRange.FromBounds(lowerBound, upperBound));
            }

            var minCount = Math.Min(this.Count, other.Count);
            var start = 0;
            var end = 0;
            int i, j;

            for (i = 0; i < minCount; i++)
            {
                start = Math.Min(this[i].Start, other[i].Start);

                if (this[i].Start != other[i].Start || this[i].Length != other[i].Length)
                {
                    break;
                }
            }

            if (i == minCount)
            {
                if (this.Count == other.Count)
                {
                    return(TextRange.EmptyRange);
                }

                if (this.Count > other.Count)
                {
                    return(TextRange.FromBounds(Math.Min(upperBound, other[minCount - 1].Start), upperBound));
                }
                else
                {
                    return(TextRange.FromBounds(Math.Min(this[minCount - 1].Start, upperBound), upperBound));
                }
            }

            for (i = this.Count - 1, j = other.Count - 1; i >= 0 && j >= 0; i--, j--)
            {
                end = Math.Max(this[i].End, other[j].End);

                if (this[i].Start != other[j].Start || this[i].Length != other[j].Length)
                {
                    break;
                }
            }

            if (start < end)
            {
                return(TextRange.FromBounds(start, end));
            }

            return(TextRange.FromBounds(lowerBound, upperBound));
        }
示例#17
0
 public int IndexOf(string text, int startPosition, bool ignoreCase)
 {
     return(IndexOf(text, TextRange.FromBounds(startPosition, this.Length), ignoreCase));
 }
 public IList <T> ItemsInRange(int start)
 {
     return(this._collection.ItemsInRange(TextRange.FromBounds(start, start)));
 }
示例#19
0
        protected virtual void OnTextBufferChanged(object sender, TextContentChangedEventArgs e)
        {
            // In order to provide nicer experience when user presser and holds
            // ENTER or DELETE or just types really fast, we are going to track
            // regions optimistically and report changes without going through
            // async or idle processing. Idle/async is still going to hit later.

            if (e.Changes.Count > 0)
            {
                TextUtility.CombineChanges(e, out var start, out var oldLength, out var newLength);

                var changeStart = int.MaxValue;
                var changeEnd   = 0;

                lock (this._regionsLock)
                {
                    // Remove affected regions and shift the remaining ones. Outlining
                    // regions are not sorted and can overlap. Hence linear search.

                    for (var i = 0; i < this.CurrentRegions.Count; i++)
                    {
                        var region = this.CurrentRegions[i];

                        if (region.End <= start)
                        {
                            continue;
                        }

                        if (region.Contains(start) && region.Contains(start + oldLength))
                        {
                            region.Expand(0, newLength - oldLength);
                        }
                        else if (region.Start >= start + oldLength)
                        {
                            region.Shift(newLength - oldLength);
                        }
                        else
                        {
                            this.CurrentRegions.RemoveAt(i);
                            i--;
                        }

                        changeStart = Math.Min(changeStart, region.Start);
                        changeEnd   = Math.Max(changeEnd, region.End);
                    }

                    if (changeStart < int.MaxValue)
                    {
                        this.CurrentRegions.TextBufferVersion = this.TextBuffer.CurrentSnapshot.Version.VersionNumber;
                    }
                }

                if (changeStart < int.MaxValue)
                {
                    if (this.RegionsChanged != null)
                    {
                        this.RegionsChanged(this, new OutlineRegionsChangedEventArgs(this.CurrentRegions, TextRange.FromBounds(changeStart, changeEnd)));
                    }
                }
            }
        }
示例#20
0
        public virtual IList <ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
        {
            var classifications = new List <ClassificationSpan>();
            var textSnapshot    = this.TextBuffer.CurrentSnapshot;

            if (span.Length <= 2)
            {
                var ws = textSnapshot.GetText(span);
                if (string.IsNullOrWhiteSpace(ws))
                {
                    return(classifications);
                }
            }

            // Token collection at this point contains valid tokens at least to a point
            // of the most recent change. We can reuse existing tokens but may also need
            // to tokenize to get tokens for the recently changed range.
            if (span.End > this._lastValidPosition)
            {
                // Span is beyond the last position we know about. We need to tokenize new area.
                // tokenize from end of the last good token. If last token intersected last change
                // it would have been removed from the collection by now.

                var tokenizeFrom   = this.Tokens.Count > 0 ? this.Tokens[this.Tokens.Count - 1].End : new SnapshotPoint(textSnapshot, 0);
                var tokenizeAnchor = GetAnchorPosition(tokenizeFrom);

                if (tokenizeAnchor < tokenizeFrom)
                {
                    this.Tokens.RemoveInRange(TextRange.FromBounds(tokenizeAnchor, span.End));
                    RemoveSensitiveTokens(tokenizeAnchor, this.Tokens);

                    tokenizeFrom = tokenizeAnchor;
                    VerifyTokensSorted();
                }

                var newTokens = this.Tokenizer.Tokenize(new TextProvider(this.TextBuffer.CurrentSnapshot), tokenizeFrom, span.End - tokenizeFrom);
                if (newTokens.Count > 0)
                {
                    this.Tokens.Add(newTokens);
                    this._lastValidPosition = newTokens[newTokens.Count - 1].End;
                }
            }

            var tokensInSpan = this.Tokens.ItemsInRange(TextRange.FromBounds(span.Start, span.End));

            foreach (var token in tokensInSpan)
            {
                if (token is ICompositeToken <TTokenClass> compositeToken)
                {
                    foreach (var internalToken in compositeToken.TokenList)
                    {
                        AddClassificationFromToken(classifications, textSnapshot, internalToken);
                    }
                }
                else
                {
                    AddClassificationFromToken(classifications, textSnapshot, token);
                }
            }

            return(classifications);
        }
示例#21
0
        protected virtual void OnTextChanged(int start, int oldLength, int newLength)
        {
            // Invalidate items starting from start of the change and onward

            // Expand range to take into accound token that might be just touching
            // changed area. For example, in PHP / is punctuation token and adding *
            // to it should remove / so tokenizer can recreate comment token.
            // However / is technically outside of the changed area and hence may end up
            // lingering on.

            var initialIndex = -1;
            var changeStart  = start;

            var touchingTokens = this.Tokens.GetItemsContainingInclusiveEnd(start);

            if (touchingTokens != null && touchingTokens.Count > 0)
            {
                initialIndex = touchingTokens.Min();
                start        = this.Tokens[initialIndex].Start;
            }

            // nothing is touching but we still might have tokens right after us
            if (initialIndex < 0)
            {
                initialIndex = this.Tokens.GetFirstItemAfterPosition(start);
            }

            if (initialIndex == 0)
            {
                start = this.Tokens[0].Start;
            }
            else
            {
                while (initialIndex > 0)
                {
                    if (this.Tokens[initialIndex - 1].End == start)
                    {
                        start = this.Tokens[initialIndex - 1].Start;
                        initialIndex--;
                    }
                    else
                    {
                        break;
                    }
                }
            }

            this._lastValidPosition = Math.Min(this._lastValidPosition, start);
            if (this.Tokens.Count > 0)
            {
                this.Tokens.RemoveInRange(TextRange.FromBounds(this._lastValidPosition, this.Tokens[this.Tokens.Count - 1].End), true);
            }

            // In line-based tokenizers like SaSS or Jade we need to start at the beginning
            // of the line i.e. at 'anchor' position that is canculated depending on particular
            // language syntax.

            this._lastValidPosition = GetAnchorPosition(this._lastValidPosition);

            RemoveSensitiveTokens(this._lastValidPosition, this.Tokens);
            VerifyTokensSorted();

            this._lastValidPosition = this.Tokens.Count > 0 ? Math.Min(this._lastValidPosition, this.Tokens[this.Tokens.Count - 1].End) : 0;

            if (ClassificationChanged != null)
            {
                var snapshot = this.TextBuffer.CurrentSnapshot;

                ClassificationChanged(this, new ClassificationChangedEventArgs(
                                          new SnapshotSpan(snapshot,
                                                           Span.FromBounds(this._lastValidPosition, snapshot.Length)))
                                      );
            }
        }
示例#22
0
 public CharacterStream(ITextProvider textProvider)
     : this(textProvider, TextRange.FromBounds(0, textProvider.Length))
 {
 }