private async Task <ImmutableArray <RazorMappedSpanResult> > MapSpansAsync(
            IEnumerable <TextSpan> spans,
            SourceText sourceTextGenerated,
            SourceText sourceTextRazor,
            CancellationToken cancellationToken)
        {
            if (spans == null)
            {
                throw new ArgumentNullException(nameof(spans));
            }

            var projectedRanges = spans.Select(span => span.AsRange(sourceTextGenerated)).ToArray();

            var mappedResult = await _lspDocumentMappingProvider.MapToDocumentRangesAsync(
                RazorLanguageKind.CSharp,
                _documentSnapshot.Uri,
                projectedRanges,
                cancellationToken).ConfigureAwait(false);

            cancellationToken.ThrowIfCancellationRequested();

            var mappedSpanResults = GetMappedSpanResults(_documentSnapshot, sourceTextRazor, mappedResult);

            return(mappedSpanResults);
        }
        private async Task <ImmutableArray <RazorMappedSpanResult> > MapSpansAsync(
            Document document,
            IEnumerable <TextSpan> spans,
            SourceText sourceTextGenerated,
            SourceText sourceTextRazor,
            CancellationToken cancellationToken)
        {
            if (spans == null)
            {
                throw new ArgumentNullException(nameof(spans));
            }

            var projectedRanges = spans.Select(span => span.AsLSPRange(sourceTextGenerated)).ToArray();

            var mappedResult = await _lspDocumentMappingProvider.MapToDocumentRangesAsync(
                RazorLanguageKind.CSharp,
                _documentSnapshot.Uri,
                projectedRanges,
                cancellationToken).ConfigureAwait(false);

            cancellationToken.ThrowIfCancellationRequested();

            var results = ImmutableArray.CreateBuilder <RazorMappedSpanResult>();

            if (mappedResult is null)
            {
                return(results.ToImmutable());
            }

            foreach (var mappedRange in mappedResult.Ranges)
            {
                var mappedSpan       = mappedRange.AsTextSpan(sourceTextRazor);
                var linePositionSpan = sourceTextRazor.Lines.GetLinePositionSpan(mappedSpan);
                var filePath         = _documentSnapshot.Uri.LocalPath;
                results.Add(new RazorMappedSpanResult(filePath, linePositionSpan, mappedSpan));
            }

            return(results.ToImmutable());
        }
        public override async Task <Range> TryResolveBreakpointRangeAsync(ITextBuffer textBuffer, int lineIndex, int characterIndex, CancellationToken cancellationToken)
        {
            if (textBuffer is null)
            {
                throw new ArgumentNullException(nameof(textBuffer));
            }

            if (!_fileUriProvider.TryGet(textBuffer, out var documentUri))
            {
                // Not an addressable Razor document. Do not allow a breakpoint here. In practice this shouldn't happen, just being defensive.
                return(null);
            }

            if (!_documentManager.TryGetDocument(documentUri, out var documentSnapshot))
            {
                // No associated Razor document. Do not allow a breakpoint here. In practice this shouldn't happen, just being defensive.
                return(null);
            }

            var lspPosition      = new Position(lineIndex, characterIndex);
            var projectionResult = await _projectionProvider.GetProjectionAsync(documentSnapshot, lspPosition, cancellationToken).ConfigureAwait(false);

            if (projectionResult == null)
            {
                // Can't map the position, invalid breakpoint location.
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            if (projectionResult.LanguageKind != RazorLanguageKind.CSharp)
            {
                // We only allow breakpoints in C#
                return(null);
            }

            if (!documentSnapshot.TryGetVirtualDocument <CSharpVirtualDocumentSnapshot>(out var virtualDocument))
            {
                Debug.Fail($"Some how there's no C# document associated with the host Razor document {documentUri.OriginalString} when validating breakpoint locations.");
                return(null);
            }

            var sourceText = virtualDocument.Snapshot.AsText();
            var syntaxTree = CSharpSyntaxTree.ParseText(sourceText, isGeneratedCode: true, cancellationToken: cancellationToken);

            if (!_csharpBreakpointResolver.TryGetBreakpointSpan(syntaxTree, projectionResult.PositionIndex, cancellationToken, out var csharpBreakpointSpan))
            {
                return(null);
            }

            virtualDocument.Snapshot.GetLineAndCharacter(csharpBreakpointSpan.Start, out var startLineIndex, out var startCharacterIndex);
            virtualDocument.Snapshot.GetLineAndCharacter(csharpBreakpointSpan.End, out var endLineIndex, out var endCharacterIndex);

            var projectedRange = new[]
            {
                new Range()
                {
                    Start = new Position(startLineIndex, startCharacterIndex),
                    End   = new Position(endLineIndex, endCharacterIndex),
                },
            };
            var hostDocumentMapping = await _documentMappingProvider.MapToDocumentRangesAsync(RazorLanguageKind.CSharp, documentUri, projectedRange, cancellationToken).ConfigureAwait(false);

            if (hostDocumentMapping == null)
            {
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            var hostDocumentRange = hostDocumentMapping.Ranges.FirstOrDefault();

            return(hostDocumentRange);
        }
Esempio n. 4
0
        public override async Task <Range> TryResolveBreakpointRangeAsync(ITextBuffer textBuffer, int lineIndex, int characterIndex, CancellationToken cancellationToken)
        {
            if (textBuffer is null)
            {
                throw new ArgumentNullException(nameof(textBuffer));
            }

            if (!_fileUriProvider.TryGet(textBuffer, out var documentUri))
            {
                // Not an addressable Razor document. Do not allow a breakpoint here. In practice this shouldn't happen, just being defensive.
                return(null);
            }

            if (!_documentManager.TryGetDocument(documentUri, out var documentSnapshot))
            {
                // No associated Razor document. Do not allow a breakpoint here. In practice this shouldn't happen, just being defensive.
                return(null);
            }

            if (!documentSnapshot.TryGetVirtualDocument <CSharpVirtualDocumentSnapshot>(out var virtualDocument))
            {
                Debug.Fail($"Some how there's no C# document associated with the host Razor document {documentUri.OriginalString} when validating breakpoint locations.");
                return(null);
            }

            if (virtualDocument.HostDocumentSyncVersion != documentSnapshot.Version)
            {
                // C# document isn't up-to-date with the Razor document. Because VS' debugging tech is synchronous on the UI thread we have to bail. Ideally we'd wait
                // for the C# document to become "updated"; however, that'd require the UI thread to see that the C# buffer is updated. Because this call path blocks
                // the UI thread the C# document will never update until this path has exited. This means as a user types around the point of interest data may get stale
                // but will re-adjust later.
                return(null);
            }

            var cacheKey = new CacheKey(documentSnapshot.Uri, documentSnapshot.Version, lineIndex, characterIndex);

            if (_cache.TryGetValue(cacheKey, out var cachedRange))
            {
                // We've seen this request before, no need to go async.
                return(cachedRange);
            }

            var lspPosition      = new Position(lineIndex, characterIndex);
            var projectionResult = await _projectionProvider.GetProjectionAsync(documentSnapshot, lspPosition, cancellationToken).ConfigureAwait(false);

            if (projectionResult == null)
            {
                // Can't map the position, invalid breakpoint location.
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            if (projectionResult.LanguageKind != RazorLanguageKind.CSharp)
            {
                // We only allow breakpoints in C#
                return(null);
            }

            var syntaxTree = await virtualDocument.GetCSharpSyntaxTreeAsync(_workspace, cancellationToken).ConfigureAwait(false);

            if (!RazorBreakpointSpans.TryGetBreakpointSpan(syntaxTree, projectionResult.PositionIndex, cancellationToken, out var csharpBreakpointSpan))
            {
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            virtualDocument.Snapshot.GetLineAndCharacter(csharpBreakpointSpan.Start, out var startLineIndex, out var startCharacterIndex);
            virtualDocument.Snapshot.GetLineAndCharacter(csharpBreakpointSpan.End, out var endLineIndex, out var endCharacterIndex);

            var projectedRange = new[]
            {
                new Range()
                {
                    Start = new Position(startLineIndex, startCharacterIndex),
                    End   = new Position(endLineIndex, endCharacterIndex),
                },
            };
            var hostDocumentMapping = await _documentMappingProvider.MapToDocumentRangesAsync(RazorLanguageKind.CSharp, documentUri, projectedRange, cancellationToken).ConfigureAwait(false);

            if (hostDocumentMapping == null)
            {
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            var hostDocumentRange = hostDocumentMapping.Ranges.FirstOrDefault();

            // Cache range so if we're asked again for this document/line/character we don't have to go async.
            _cache.Set(cacheKey, hostDocumentRange);

            return(hostDocumentRange);
        }
        public override async Task <Range> TryResolveBreakpointRangeAsync(ITextBuffer textBuffer, int lineIndex, int characterIndex, CancellationToken cancellationToken)
        {
            if (textBuffer is null)
            {
                throw new ArgumentNullException(nameof(textBuffer));
            }

            if (!_fileUriProvider.TryGet(textBuffer, out var documentUri))
            {
                // Not an addressable Razor document. Do not allow a breakpoint here. In practice this shouldn't happen, just being defensive.
                return(null);
            }

            if (!_documentManager.TryGetDocument(documentUri, out var documentSnapshot))
            {
                // No associated Razor document. Do not allow a breakpoint here. In practice this shouldn't happen, just being defensive.
                return(null);
            }

            var cacheKey = new CacheKey(documentSnapshot.Uri, documentSnapshot.Version, lineIndex, characterIndex);

            if (_cache.TryGetValue(cacheKey, out var cachedRange))
            {
                // We've seen this request before, no need to go async.
                return(cachedRange);
            }

            var lspPosition      = new Position(lineIndex, characterIndex);
            var projectionResult = await _projectionProvider.GetProjectionAsync(documentSnapshot, lspPosition, cancellationToken).ConfigureAwait(false);

            if (projectionResult == null)
            {
                // Can't map the position, invalid breakpoint location.
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            if (projectionResult.LanguageKind != RazorLanguageKind.CSharp)
            {
                // We only allow breakpoints in C#
                return(null);
            }

            if (!documentSnapshot.TryGetVirtualDocument <CSharpVirtualDocumentSnapshot>(out var virtualDocument))
            {
                Debug.Fail($"Some how there's no C# document associated with the host Razor document {documentUri.OriginalString} when validating breakpoint locations.");
                return(null);
            }

            _workspaceAccessor.TryGetWorkspace(textBuffer, out var workspace);

            var syntaxTree = await virtualDocument.GetCSharpSyntaxTreeAsync(workspace, cancellationToken).ConfigureAwait(false);

            if (!RazorBreakpointSpans.TryGetBreakpointSpan(syntaxTree, projectionResult.PositionIndex, cancellationToken, out var csharpBreakpointSpan))
            {
                return(null);
            }

            virtualDocument.Snapshot.GetLineAndCharacter(csharpBreakpointSpan.Start, out var startLineIndex, out var startCharacterIndex);
            virtualDocument.Snapshot.GetLineAndCharacter(csharpBreakpointSpan.End, out var endLineIndex, out var endCharacterIndex);

            var projectedRange = new[]
            {
                new Range()
                {
                    Start = new Position(startLineIndex, startCharacterIndex),
                    End   = new Position(endLineIndex, endCharacterIndex),
                },
            };
            var hostDocumentMapping = await _documentMappingProvider.MapToDocumentRangesAsync(RazorLanguageKind.CSharp, documentUri, projectedRange, cancellationToken).ConfigureAwait(false);

            if (hostDocumentMapping == null)
            {
                return(null);
            }

            cancellationToken.ThrowIfCancellationRequested();

            var hostDocumentRange = hostDocumentMapping.Ranges.FirstOrDefault();

            // Cache range so if we're asked again for this document/line/character we don't have to go async.
            _cache.Set(cacheKey, hostDocumentRange);

            return(hostDocumentRange);
        }