public IEnumerable <ITagSpan <IOutliningRegionTag> > GetTags(NormalizedSnapshotSpanCollection spans) { if (!spans.Any()) { yield break; } var artifacts = artifactsGetter(); // Start from the first artifact after the start // position. Since code blocks will always have // an end-boundary artifact, this will work even // if we start in the middle of a code block. int i = artifacts.GetFirstItemAfterOrAtPosition(spans[0].Start); if (i < 0) { yield break; } CodeBlockInfo lastBlock = null; for (; i < artifacts.Count; i++) { var cba = artifacts[i] as ICodeBlockArtifact; // Skip artifacts that aren't in code blocks, and // those in code blocks we have already returned. if (cba == null || cba.BlockInfo == lastBlock) { continue; } lastBlock = cba.BlockInfo; // Skip single-line indented code blocks, but not single-line fenced code blocks if (lastBlock.CodeLines.Count == 1 && lastBlock.OuterEnd.End == lastBlock.CodeLines[0].End) { continue; } yield return(new TagSpan <IOutliningRegionTag>( new SnapshotSpan(spans[0].Snapshot, Span.FromBounds(lastBlock.OuterStart.Start, lastBlock.OuterEnd.End)), tagCreator( Caption(lastBlock), lastBlock.CodeLines .Select(a => a.InnerRange.ToSnapshotSpan(spans[0].Snapshot)) .ToList() // Force eager evaluation; this query is only enumerated when a tooltip is shown, so we need to grab the snapshot ) )); if (lastBlock.OuterStart.Start > spans.Last().End) { break; // If we have completely passed the requested range, stop. } } }
/// <inheritdoc/> public IEnumerable <ITagSpan <TTag> >?GetTags(NormalizedSnapshotSpanCollection spans) { if (spans[0].IsEmpty) { return(null); } bool isFullParse = spans.First().Start == 0 && spans.Last().End == spans[0].Snapshot.Length; return(GetTags(spans, isFullParse)); }
internal static NormalizedSnapshotSpanCollection CoalesceSpans(NormalizedSnapshotSpanCollection normalizedSpans) { var snapshot = normalizedSpans.First().Snapshot; // Coalesce the spans if there are a lot of them. if (normalizedSpans.Count > CoalesceDifferenceCount) { // Spans are normalized. So to find the whole span we just go from the // start of the first span to the end of the last span. normalizedSpans = new NormalizedSnapshotSpanCollection(snapshot.GetSpanFromBounds( normalizedSpans.First().Start, normalizedSpans.Last().End)); } return(normalizedSpans); }
private async Task <CommentSelectionResult> ToggleBlockCommentsAsync(Document document, CommentSelectionInfo commentInfo, ITextStructureNavigator navigator, NormalizedSnapshotSpanCollection selectedSpans, CancellationToken cancellationToken) { var firstLineAroundSelection = selectedSpans.First().Start.GetContainingLine().Start; var lastLineAroundSelection = selectedSpans.Last().End.GetContainingLine().End; var linesContainingSelection = TextSpan.FromBounds(firstLineAroundSelection, lastLineAroundSelection); var blockCommentedSpans = await GetBlockCommentsInDocumentAsync( document, selectedSpans.First().Snapshot, linesContainingSelection, commentInfo, cancellationToken).ConfigureAwait(false); var blockCommentSelections = selectedSpans.SelectAsArray(span => new BlockCommentSelectionHelper(blockCommentedSpans, span)); var returnOperation = Operation.Uncomment; var textChanges = ArrayBuilder <TextChange> .GetInstance(); var trackingSpans = ArrayBuilder <CommentTrackingSpan> .GetInstance(); // Try to uncomment until an already uncommented span is found. foreach (var blockCommentSelection in blockCommentSelections) { // If any selection does not have comments to remove, then the operation should be comment. if (!TryUncommentBlockComment(blockCommentedSpans, blockCommentSelection, textChanges, trackingSpans, commentInfo)) { returnOperation = Operation.Comment; break; } } if (returnOperation == Operation.Comment) { textChanges.Clear(); trackingSpans.Clear(); foreach (var blockCommentSelection in blockCommentSelections) { BlockCommentSpan(blockCommentSelection, navigator, textChanges, trackingSpans, commentInfo); } } return(new CommentSelectionResult(textChanges.ToArrayAndFree(), trackingSpans.ToArrayAndFree(), returnOperation)); }
public IEnumerable <TagSpan <IGLSLTag> > GetOverlapping(NormalizedSnapshotSpanCollection spans, ITextSnapshot textSnapshot) { var fullSpan = new SnapshotSpan(spans.First().Start, spans.Last().End); foreach (var tagSpan in _tagSpans) { var translatedSpan = tagSpan.Span.TranslateTo(textSnapshot, SpanTrackingMode.EdgeExclusive); if (translatedSpan.IntersectsWith(fullSpan)) { // Now check more incrementally foreach (var span in spans) { if (translatedSpan.IntersectsWith(span)) { yield return(new TagSpan <IGLSLTag>(translatedSpan, tagSpan.Tag)); break; } } } } }
public IEnumerable <ITagSpan <IClassificationTag> > GetAllTags(NormalizedSnapshotSpanCollection spans, CancellationToken cancellationToken) { this.AssertIsForeground(); if (spans.Count == 0) { return(Array.Empty <ITagSpan <IClassificationTag> >()); } var firstSpan = spans.First(); var snapshot = firstSpan.Snapshot; Debug.Assert(snapshot.TextBuffer == _subjectBuffer); var document = snapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document == null) { return(Array.Empty <ITagSpan <IClassificationTag> >()); } var classificationService = document.GetLanguageService <IClassificationService>(); if (classificationService == null) { return(Array.Empty <ITagSpan <IClassificationTag> >()); } // We want to classify from the start of the first requested span to the end of the // last requested span. var spanToTag = new SnapshotSpan(snapshot, Span.FromBounds(spans.First().Start, spans.Last().End)); GetCachedInfo(out var cachedTaggedSpan, out var cachedTags); // We don't need to actually classify if what we're being asked for is a subspan // of the last classification we performed. var canReuseCache = cachedTaggedSpan?.Snapshot == snapshot && cachedTaggedSpan.Value.Contains(spanToTag); if (!canReuseCache) { // Our cache is not there, or is out of date. We need to compute the up to date results. var context = new TaggerContext <IClassificationTag>(document, snapshot); var options = _globalOptions.GetClassificationOptions(document.Project.Language); ThreadingContext.JoinableTaskFactory.Run(async() => { var snapshotSpan = new DocumentSnapshotSpan(document, spanToTag); // When copying/pasting, ensure we have classifications fully computed for the requested spans // for both semantic classifications and embedded lang classifications. await ProduceTagsAsync(context, snapshotSpan, classificationService, options, ClassificationType.Semantic, cancellationToken).ConfigureAwait(false); await ProduceTagsAsync(context, snapshotSpan, classificationService, options, ClassificationType.EmbeddedLanguage, cancellationToken).ConfigureAwait(false); }); cachedTaggedSpan = spanToTag; cachedTags = new TagSpanIntervalTree <IClassificationTag>(snapshot.TextBuffer, SpanTrackingMode.EdgeExclusive, context.tagSpans); lock (_gate) { _cachedTaggedSpan = cachedTaggedSpan; _cachedTags = cachedTags; } } return(cachedTags == null ? Array.Empty <ITagSpan <IClassificationTag> >() : cachedTags.GetIntersectingTagSpans(spans)); }
public IEnumerable <ITagSpan <IClassificationTag> > GetAllTags(NormalizedSnapshotSpanCollection spans, CancellationToken cancellationToken) { this.AssertIsForeground(); if (spans.Count == 0) { return(Array.Empty <ITagSpan <IClassificationTag> >()); } var firstSpan = spans.First(); var snapshot = firstSpan.Snapshot; Debug.Assert(snapshot.TextBuffer == _subjectBuffer); // We want to classify from the start of the first requested span to the end of the // last requested span. var spanToTag = new SnapshotSpan(snapshot, Span.FromBounds(spans.First().Start, spans.Last().End)); // We don't need to actually classify if what we're being asked for is a subspan // of the last classification we performed. var cachedTaggedSpan = this.CachedTaggedSpan; var canReuseCache = cachedTaggedSpan?.Snapshot == snapshot && cachedTaggedSpan.Value.Contains(spanToTag); if (!canReuseCache) { // Our cache is not there, or is out of date. We need to compute the up to date // results. var document = snapshot.GetOpenDocumentInCurrentContextWithChanges(); if (document == null) { return(Array.Empty <ITagSpan <IClassificationTag> >()); } var context = new TaggerContext <IClassificationTag>(document, snapshot, cancellationToken: cancellationToken); var task = ProduceTagsAsync( context, new DocumentSnapshotSpan(document, spanToTag), _owner._typeMap); task.Wait(cancellationToken); CachedTaggedSpan = spanToTag; CachedTags = new TagSpanIntervalTree <IClassificationTag>(snapshot.TextBuffer, SpanTrackingMode.EdgeExclusive, context.tagSpans); } if (this.CachedTags == null) { return(Array.Empty <ITagSpan <IClassificationTag> >()); } return(this.CachedTags.GetIntersectingTagSpans(spans)); }
internal static NormalizedSnapshotSpanCollection CoalesceSpans(NormalizedSnapshotSpanCollection normalizedSpans) { var snapshot = normalizedSpans.First().Snapshot; // Coalesce the spans if there are a lot of them. if (normalizedSpans.Count > CoalesceDifferenceCount) { // Spans are normalized. So to find the whole span we just go from the // start of the first span to the end of the last span. normalizedSpans = new NormalizedSnapshotSpanCollection(snapshot.GetSpanFromBounds( normalizedSpans.First().Start, normalizedSpans.Last().End)); } return normalizedSpans; }