/// <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); }
/// <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); }
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); }
/// <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); }
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]; }
/// <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)); }
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)); }
/// <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)); }
/// <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)); }
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)); }
public IList <T> ItemsInRange(int start) { return(this.collection.ItemsInRange(TextRange.FromBounds(start, start))); }
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))) ); } }
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) { var compositeToken = token as ICompositeToken <TTokenClass>; if (compositeToken != null) { foreach (var internalToken in compositeToken.TokenList) { AddClassificationFromToken(classifications, textSnapshot, internalToken); } } else { AddClassificationFromToken(classifications, textSnapshot, token); } } return(classifications); }
/// <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)); }
public CharacterStream(ITextProvider textProvider) : this(textProvider, TextRange.FromBounds(0, textProvider.Length)) { }
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) { int start, oldLength, newLength; TextUtility.CombineChanges(e, out start, out oldLength, out newLength); var changeStart = Int32.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 < Int32.MaxValue) { this.CurrentRegions.TextBufferVersion = this.TextBuffer.CurrentSnapshot.Version.VersionNumber; } } if (changeStart < Int32.MaxValue) { if (this.RegionsChanged != null) { this.RegionsChanged(this, new OutlineRegionsChangedEventArgs(this.CurrentRegions, TextRange.FromBounds(changeStart, changeEnd))); } } } }
public int IndexOf(string text, int startPosition, bool ignoreCase) { return(IndexOf(text, TextRange.FromBounds(startPosition, this.Length), ignoreCase)); }