private void DoUpdate() { // It would be good to do all of this work on a background thread but we can't: // _classifier.GetClassificationSpans() should only be called on the UI thread because some classifiers assume they are called from the UI thread. // Raising the TagsChanged event from the taggers needs to happen on the UI thread (because some consumers might assume it is being raised on the UI thread). // // Updating the snapshot for the factory and calling the sink can happen on any thread but those operations are so fast that there is no point. if (!_isDisposed) { if (_buffer.Equals(_textview.TextBuffer)) { ErrorSnapShot oldErrors = this._factory.CurrentSnapshot; ErrorSnapShot newErrors = new ErrorSnapShot(this._filePath, oldErrors.VersionNumber + 1); List <ErrorInformation> newSpanErrors; // Go through the existing errors. If they are on the line we are currently parsing then // copy them to oldLineErrors, otherwise they go to the new errors. newSpanErrors = this.GetErrorInformation(_buffer.CurrentSnapshot.GetText()); if (!newSpanErrors.Equals(_spanErrors)) { _spanErrors.Clear(); _spanErrors.AddRange(newSpanErrors); foreach (ErrorInformation spanError in _spanErrors) { if (spanError.Length >= 0) { SnapshotSpan newSpan = new SnapshotSpan(_buffer.CurrentSnapshot, spanError.StartIndex, spanError.Length); ErrorSpan oldError = oldErrors.Errors.Find((e) => e.Span == newSpan); if (oldError != null) { // There was a error at the same span as the old one so we should be able to just reuse it. oldError.NextIndex = newErrors.Errors.Count; newErrors.Errors.Add(ErrorSpan.Clone(oldError)); // Don't clone the old error yet } else { newErrors.Errors.Add(new ErrorSpan(newSpan, spanError.ErrorMessage, spanError.ErrorCode)); } } } this.UpdateErrors(newErrors); } else { foreach (var error in oldErrors.Errors) { error.NextIndex = -1; } } } } _isUpdating = false; }
/// <summary> /// Quickly parses the entire text of the file and returns a span for each multi-line comment /// (because these cannot be determined from a snapshot without any context). /// </summary> /// <param name="snapshot">The text snapshot</param> /// <returns>A sequence of Spans (possibly empty) with one span for each multi-line comment in the file.</returns> private IEnumerable <Span> GetMultiLineComments(ITextSnapshot snapshot) { // use cache IList <Span> multiLineComments; _commentCache.TryGetValue(snapshot, out multiLineComments); if (multiLineComments != null) { return(multiLineComments); } multiLineComments = new List <Span>(); string text = snapshot.GetText(); int commentStart; int commentEnd = 0 - MultiLineCommentDelimiter.Length; do { commentStart = text.IndexOf( MultiLineCommentDelimiter, commentEnd + MultiLineCommentDelimiter.Length, StringComparison.Ordinal); if (commentStart < 0) { continue; } commentEnd = text.IndexOf( MultiLineCommentDelimiter, commentStart + MultiLineCommentDelimiter.Length, StringComparison.Ordinal); if (commentEnd < 0) { commentEnd = text.Length; // if there's no end then run to end of file } else { commentEnd += MultiLineCommentDelimiter.Length; } multiLineComments.Add(Span.FromBounds(commentStart, commentEnd)); Debug.Assert( commentStart < commentEnd, "Comment must have some width and the start and end must be the correct way around"); } while (commentStart > -1 && commentEnd > -1 && commentEnd < text.Length); // Remove the cached version if there is one if (_commentCache.Count > 0) { //// ReSharper disable RedundantAssignment ITextBuffer buffer = snapshot.TextBuffer; //// ReSharper restore RedundantAssignment Debug.Assert(_commentCache.Count == 1, "There should be one item in the cache"); Debug.Assert(buffer.Equals(_commentCache.Single().Key.TextBuffer), "The key should be the same"); _commentCache.Clear(); } // add to cache so we don't have to calculate it again for this snapshot _commentCache[snapshot] = multiLineComments; return(multiLineComments); }