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); }
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); }