зеркало из https://github.com/dotnet/razor.git
Fix setting breakpoints with self versioned documents (#10811)
Fixes an issue found in app building. Thanks @phil-allen-msft! Also fixes https://github.com/dotnet/razor/issues/9161 To make reviewing easier: * First commit is entirely mechanical cleanup, renames, etc. and can be skipped. * Second commit is the fix. * Third commit is tests. * Fourth commit is updating more tests because these days when you ask VS to build things it doesn't build all of it and I need to get into the habit of doing a command line build before pushing Because of the type and file renames, looking at the PR as a whole is inadvised.
This commit is contained in:
Коммит
e1f6fbaad7
|
@ -19,30 +19,17 @@ using Microsoft.VisualStudio.LanguageServer.Protocol;
|
|||
namespace Microsoft.AspNetCore.Razor.LanguageServer.Debugging;
|
||||
|
||||
[RazorLanguageServerEndpoint(LanguageServerConstants.RazorBreakpointSpanEndpoint)]
|
||||
internal class RazorBreakpointSpanEndpoint : IRazorDocumentlessRequestHandler<RazorBreakpointSpanParams, RazorBreakpointSpanResponse?>, ITextDocumentIdentifierHandler<RazorBreakpointSpanParams, Uri>
|
||||
internal class RazorBreakpointSpanEndpoint(
|
||||
IDocumentMappingService documentMappingService,
|
||||
ILoggerFactory loggerFactory) : IRazorDocumentlessRequestHandler<RazorBreakpointSpanParams, RazorBreakpointSpanResponse?>, ITextDocumentIdentifierHandler<RazorBreakpointSpanParams, Uri>
|
||||
{
|
||||
private readonly IDocumentMappingService _documentMappingService;
|
||||
private readonly ILogger _logger;
|
||||
private readonly IDocumentMappingService _documentMappingService = documentMappingService;
|
||||
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<RazorBreakpointSpanEndpoint>();
|
||||
|
||||
public bool MutatesSolutionState => false;
|
||||
|
||||
public RazorBreakpointSpanEndpoint(
|
||||
IDocumentMappingService documentMappingService,
|
||||
ILoggerFactory loggerFactory)
|
||||
{
|
||||
if (loggerFactory is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(loggerFactory));
|
||||
}
|
||||
|
||||
_documentMappingService = documentMappingService ?? throw new ArgumentNullException(nameof(documentMappingService));
|
||||
_logger = loggerFactory.GetOrCreateLogger<RazorBreakpointSpanEndpoint>();
|
||||
}
|
||||
|
||||
public Uri GetTextDocumentIdentifier(RazorBreakpointSpanParams request)
|
||||
{
|
||||
return request.Uri;
|
||||
}
|
||||
=> request.Uri;
|
||||
|
||||
public async Task<RazorBreakpointSpanResponse?> HandleRequestAsync(RazorBreakpointSpanParams request, RazorRequestContext requestContext, CancellationToken cancellationToken)
|
||||
{
|
||||
|
@ -52,6 +39,12 @@ internal class RazorBreakpointSpanEndpoint : IRazorDocumentlessRequestHandler<Ra
|
|||
return null;
|
||||
}
|
||||
|
||||
if (documentContext.Snapshot.Version != request.HostDocumentSyncVersion)
|
||||
{
|
||||
// Whether we are being asked about an old version of the C# document, or somehow a future one, we can't rely on the result.
|
||||
return null;
|
||||
}
|
||||
|
||||
var codeDocument = await documentContext.GetCodeDocumentAsync(cancellationToken).ConfigureAwait(false);
|
||||
var sourceText = await documentContext.GetSourceTextAsync(cancellationToken).ConfigureAwait(false);
|
||||
var hostDocumentIndex = sourceText.GetPosition(request.Position);
|
||||
|
|
|
@ -20,35 +20,17 @@ using Microsoft.VisualStudio.LanguageServer.Protocol;
|
|||
namespace Microsoft.AspNetCore.Razor.LanguageServer.Debugging;
|
||||
|
||||
[RazorLanguageServerEndpoint(LanguageServerConstants.RazorProximityExpressionsEndpoint)]
|
||||
internal class RazorProximityExpressionsEndpoint : IRazorDocumentlessRequestHandler<RazorProximityExpressionsParams, RazorProximityExpressionsResponse?>, ITextDocumentIdentifierHandler<RazorProximityExpressionsParams, Uri>
|
||||
internal class RazorProximityExpressionsEndpoint(
|
||||
IDocumentMappingService documentMappingService,
|
||||
ILoggerFactory loggerFactory) : IRazorDocumentlessRequestHandler<RazorProximityExpressionsParams, RazorProximityExpressionsResponse?>, ITextDocumentIdentifierHandler<RazorProximityExpressionsParams, Uri>
|
||||
{
|
||||
private readonly IDocumentMappingService _documentMappingService;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public RazorProximityExpressionsEndpoint(
|
||||
IDocumentMappingService documentMappingService,
|
||||
ILoggerFactory loggerFactory)
|
||||
{
|
||||
if (documentMappingService is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentMappingService));
|
||||
}
|
||||
|
||||
if (loggerFactory is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(loggerFactory));
|
||||
}
|
||||
|
||||
_documentMappingService = documentMappingService;
|
||||
_logger = loggerFactory.GetOrCreateLogger<RazorBreakpointSpanEndpoint>();
|
||||
}
|
||||
private readonly IDocumentMappingService _documentMappingService = documentMappingService;
|
||||
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<RazorBreakpointSpanEndpoint>();
|
||||
|
||||
public bool MutatesSolutionState => false;
|
||||
|
||||
public Uri GetTextDocumentIdentifier(RazorProximityExpressionsParams request)
|
||||
{
|
||||
return request.Uri;
|
||||
}
|
||||
=> request.Uri;
|
||||
|
||||
public async Task<RazorProximityExpressionsResponse?> HandleRequestAsync(RazorProximityExpressionsParams request, RazorRequestContext requestContext, CancellationToken cancellationToken)
|
||||
{
|
||||
|
@ -58,6 +40,12 @@ internal class RazorProximityExpressionsEndpoint : IRazorDocumentlessRequestHand
|
|||
return null;
|
||||
}
|
||||
|
||||
if (documentContext.Snapshot.Version != request.HostDocumentSyncVersion)
|
||||
{
|
||||
// Whether we are being asked about an old version of the C# document, or somehow a future one, we can't rely on the result.
|
||||
return null;
|
||||
}
|
||||
|
||||
var codeDocument = await documentContext.GetCodeDocumentAsync(cancellationToken).ConfigureAwait(false);
|
||||
var sourceText = await documentContext.GetSourceTextAsync(cancellationToken).ConfigureAwait(false);
|
||||
var hostDocumentIndex = sourceText.GetPosition(request.Position);
|
||||
|
|
|
@ -14,4 +14,7 @@ internal class RazorBreakpointSpanParams
|
|||
|
||||
[JsonPropertyName("position")]
|
||||
public required Position Position { get; init; }
|
||||
|
||||
[JsonPropertyName("hostDocumentSyncVersion")]
|
||||
public required long HostDocumentSyncVersion { get; init; }
|
||||
}
|
||||
|
|
|
@ -14,4 +14,7 @@ internal class RazorProximityExpressionsParams
|
|||
|
||||
[JsonPropertyName("position")]
|
||||
public required Position Position { get; init; }
|
||||
|
||||
[JsonPropertyName("hostDocumentSyncVersion")]
|
||||
public required long HostDocumentSyncVersion { get; init; }
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ using Range = Microsoft.VisualStudio.LanguageServer.Protocol.Range;
|
|||
|
||||
namespace Microsoft.VisualStudio.Razor.Debugging;
|
||||
|
||||
internal abstract class RazorBreakpointResolver
|
||||
internal interface IRazorBreakpointResolver
|
||||
{
|
||||
public abstract Task<Range?> TryResolveBreakpointRangeAsync(ITextBuffer textBuffer, int lineIndex, int characterIndex, CancellationToken cancellationToken);
|
||||
Task<Range?> TryResolveBreakpointRangeAsync(ITextBuffer textBuffer, int lineIndex, int characterIndex, CancellationToken cancellationToken);
|
||||
}
|
|
@ -8,7 +8,7 @@ using Microsoft.VisualStudio.Text;
|
|||
|
||||
namespace Microsoft.VisualStudio.Razor.Debugging;
|
||||
|
||||
internal abstract class RazorProximityExpressionResolver
|
||||
internal interface IRazorProximityExpressionResolver
|
||||
{
|
||||
public abstract Task<IReadOnlyList<string>?> TryResolveProximityExpressionsAsync(ITextBuffer textBuffer, int lineIndex, int characterIndex, CancellationToken cancellationToken);
|
||||
Task<IReadOnlyList<string>?> TryResolveProximityExpressionsAsync(ITextBuffer textBuffer, int lineIndex, int characterIndex, CancellationToken cancellationToken);
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.Razor.Logging;
|
||||
using Microsoft.CodeAnalysis.Razor.Protocol;
|
||||
using Microsoft.CodeAnalysis.Razor.Protocol.Debugging;
|
||||
using Microsoft.VisualStudio.LanguageServer.ContainedLanguage;
|
||||
using Microsoft.VisualStudio.LanguageServer.Protocol;
|
||||
using Range = Microsoft.VisualStudio.LanguageServer.Protocol.Range;
|
||||
|
||||
namespace Microsoft.VisualStudio.Razor.LanguageClient.Debugging;
|
||||
|
||||
[Export(typeof(LSPBreakpointSpanProvider))]
|
||||
internal class DefaultLSPBreakpointSpanProvider : LSPBreakpointSpanProvider
|
||||
{
|
||||
private readonly LSPRequestInvoker _requestInvoker;
|
||||
private readonly ILogger _logger;
|
||||
|
||||
[ImportingConstructor]
|
||||
public DefaultLSPBreakpointSpanProvider(
|
||||
LSPRequestInvoker requestInvoker,
|
||||
ILoggerFactory loggerFactory)
|
||||
{
|
||||
_requestInvoker = requestInvoker;
|
||||
_logger = loggerFactory.GetOrCreateLogger<DefaultLSPBreakpointSpanProvider>();
|
||||
}
|
||||
|
||||
public async override Task<Range?> GetBreakpointSpanAsync(LSPDocumentSnapshot documentSnapshot, Position position, CancellationToken cancellationToken)
|
||||
{
|
||||
if (documentSnapshot is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentSnapshot));
|
||||
}
|
||||
|
||||
if (position is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(position));
|
||||
}
|
||||
|
||||
var languageQueryParams = new RazorBreakpointSpanParams()
|
||||
{
|
||||
Position = position,
|
||||
Uri = documentSnapshot.Uri
|
||||
};
|
||||
|
||||
var response = await _requestInvoker.ReinvokeRequestOnServerAsync<RazorBreakpointSpanParams, RazorBreakpointSpanResponse>(
|
||||
documentSnapshot.Snapshot.TextBuffer,
|
||||
LanguageServerConstants.RazorBreakpointSpanEndpoint,
|
||||
RazorLSPConstants.RazorLanguageServerName,
|
||||
languageQueryParams,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var languageResponse = response?.Response;
|
||||
if (languageResponse is null)
|
||||
{
|
||||
_logger.LogInformation($"The breakpoint position could not be mapped to a valid range.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return languageResponse.Range;
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.Razor.Logging;
|
||||
using Microsoft.CodeAnalysis.Razor.Protocol;
|
||||
using Microsoft.CodeAnalysis.Razor.Protocol.Debugging;
|
||||
using Microsoft.VisualStudio.LanguageServer.ContainedLanguage;
|
||||
using Microsoft.VisualStudio.LanguageServer.Protocol;
|
||||
|
||||
namespace Microsoft.VisualStudio.Razor.LanguageClient.Debugging;
|
||||
|
||||
[Export(typeof(LSPProximityExpressionsProvider))]
|
||||
internal class DefaultLSPProximityExpressionsProvider : LSPProximityExpressionsProvider
|
||||
{
|
||||
private readonly LSPRequestInvoker _requestInvoker;
|
||||
|
||||
private readonly ILogger _logger;
|
||||
|
||||
[ImportingConstructor]
|
||||
public DefaultLSPProximityExpressionsProvider(
|
||||
LSPRequestInvoker requestInvoker,
|
||||
ILoggerFactory loggerFactory)
|
||||
{
|
||||
_requestInvoker = requestInvoker;
|
||||
_logger = loggerFactory.GetOrCreateLogger<DefaultLSPProximityExpressionsProvider>();
|
||||
}
|
||||
|
||||
public async override Task<IReadOnlyList<string>?> GetProximityExpressionsAsync(LSPDocumentSnapshot documentSnapshot, Position position, CancellationToken cancellationToken)
|
||||
{
|
||||
if (documentSnapshot is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentSnapshot));
|
||||
}
|
||||
|
||||
if (position is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(position));
|
||||
}
|
||||
|
||||
var proximityExpressionsParams = new RazorProximityExpressionsParams()
|
||||
{
|
||||
Position = position,
|
||||
Uri = documentSnapshot.Uri
|
||||
};
|
||||
|
||||
var response = await _requestInvoker.ReinvokeRequestOnServerAsync<RazorProximityExpressionsParams, RazorProximityExpressionsResponse>(
|
||||
documentSnapshot.Snapshot.TextBuffer,
|
||||
LanguageServerConstants.RazorProximityExpressionsEndpoint,
|
||||
RazorLSPConstants.RazorLanguageServerName,
|
||||
proximityExpressionsParams,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var languageResponse = response?.Response;
|
||||
if (languageResponse is null)
|
||||
{
|
||||
_logger.LogInformation($"The proximity expressions could not be resolved.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return languageResponse.Expressions;
|
||||
}
|
||||
}
|
|
@ -1,116 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Utilities;
|
||||
using Microsoft.VisualStudio.LanguageServer.ContainedLanguage;
|
||||
using Microsoft.VisualStudio.LanguageServer.Protocol;
|
||||
using Microsoft.VisualStudio.Razor.Debugging;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Range = Microsoft.VisualStudio.LanguageServer.Protocol.Range;
|
||||
|
||||
namespace Microsoft.VisualStudio.Razor.LanguageClient.Debugging;
|
||||
|
||||
[Export(typeof(RazorBreakpointResolver))]
|
||||
internal class DefaultRazorBreakpointResolver : RazorBreakpointResolver
|
||||
{
|
||||
private readonly FileUriProvider _fileUriProvider;
|
||||
private readonly LSPDocumentManager _documentManager;
|
||||
private readonly LSPBreakpointSpanProvider _breakpointSpanProvider;
|
||||
private readonly MemoryCache<CacheKey, Range> _cache;
|
||||
|
||||
[ImportingConstructor]
|
||||
public DefaultRazorBreakpointResolver(
|
||||
FileUriProvider fileUriProvider,
|
||||
LSPDocumentManager documentManager,
|
||||
LSPBreakpointSpanProvider breakpointSpanProvider)
|
||||
{
|
||||
if (fileUriProvider is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fileUriProvider));
|
||||
}
|
||||
|
||||
if (documentManager is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentManager));
|
||||
}
|
||||
|
||||
if (breakpointSpanProvider is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(breakpointSpanProvider));
|
||||
}
|
||||
|
||||
_fileUriProvider = fileUriProvider;
|
||||
_documentManager = documentManager;
|
||||
_breakpointSpanProvider = breakpointSpanProvider;
|
||||
|
||||
// 4 is a magic number that was determined based on the functionality of VisualStudio. Currently when you set or edit a breakpoint
|
||||
// we get called with two different locations for the same breakpoint. Because of this 2 time call our size must be at least 2,
|
||||
// we grow it to 4 just to be safe for lesser known scenarios.
|
||||
_cache = new MemoryCache<CacheKey, Range>(sizeLimit: 4);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// TODO: Support multiple C# documents per Razor document.
|
||||
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 position = VsLspFactory.CreatePosition(lineIndex, characterIndex);
|
||||
var hostDocumentRange = await _breakpointSpanProvider.GetBreakpointSpanAsync(documentSnapshot, position, cancellationToken).ConfigureAwait(false);
|
||||
if (hostDocumentRange is null)
|
||||
{
|
||||
// can't map the position, invalid breakpoint location.
|
||||
return null;
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
private record CacheKey(Uri DocumentUri, int DocumentVersion, int Line, int Character);
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Utilities;
|
||||
using Microsoft.VisualStudio.LanguageServer.ContainedLanguage;
|
||||
using Microsoft.VisualStudio.LanguageServer.Protocol;
|
||||
using Microsoft.VisualStudio.Razor.Debugging;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
|
||||
namespace Microsoft.VisualStudio.Razor.LanguageClient.Debugging;
|
||||
|
||||
[Export(typeof(RazorProximityExpressionResolver))]
|
||||
internal class DefaultRazorProximityExpressionResolver : RazorProximityExpressionResolver
|
||||
{
|
||||
private readonly FileUriProvider _fileUriProvider;
|
||||
private readonly LSPDocumentManager _documentManager;
|
||||
private readonly LSPProximityExpressionsProvider _proximityExpressionsProvider;
|
||||
private readonly MemoryCache<CacheKey, IReadOnlyList<string>> _cache;
|
||||
|
||||
[ImportingConstructor]
|
||||
public DefaultRazorProximityExpressionResolver(
|
||||
FileUriProvider fileUriProvider,
|
||||
LSPDocumentManager documentManager,
|
||||
LSPProximityExpressionsProvider proximityExpressionsProvider)
|
||||
{
|
||||
if (fileUriProvider is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fileUriProvider));
|
||||
}
|
||||
|
||||
if (documentManager is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(documentManager));
|
||||
}
|
||||
|
||||
if (proximityExpressionsProvider is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(proximityExpressionsProvider));
|
||||
}
|
||||
|
||||
_fileUriProvider = fileUriProvider;
|
||||
_documentManager = documentManager;
|
||||
_proximityExpressionsProvider = proximityExpressionsProvider;
|
||||
|
||||
// 10 is a magic number where this effectively represents our ability to cache the last 10 "hit" breakpoint locations
|
||||
// corresponding proximity expressions which enables us not to go "async" in those re-hit scenarios.
|
||||
_cache = new MemoryCache<CacheKey, IReadOnlyList<string>>(sizeLimit: 10);
|
||||
}
|
||||
|
||||
public override async Task<IReadOnlyList<string>?> TryResolveProximityExpressionsAsync(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 expression resolution 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 resolve expressions here. In practice this shouldn't happen, just being defensive.
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: Support multiple C# documents per Razor document.
|
||||
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 resolving proximity expressions.");
|
||||
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 cachedExpressions))
|
||||
{
|
||||
// We've seen this request before, no need to go async.
|
||||
return cachedExpressions;
|
||||
}
|
||||
|
||||
var position = VsLspFactory.CreatePosition(lineIndex, characterIndex);
|
||||
var proximityExpressions = await _proximityExpressionsProvider.GetProximityExpressionsAsync(documentSnapshot, position, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// Cache range so if we're asked again for this document/line/character we don't have to go async.
|
||||
// Note: If we didn't get any proximity expressions back--likely due to an error--we cache an empty array.
|
||||
_cache.Set(cacheKey, proximityExpressions ?? Array.Empty<string>());
|
||||
|
||||
return proximityExpressions;
|
||||
}
|
||||
|
||||
private record CacheKey(Uri DocumentUri, int DocumentVersion, int Line, int Character);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.LanguageServer.ContainedLanguage;
|
||||
using Microsoft.VisualStudio.LanguageServer.Protocol;
|
||||
|
||||
namespace Microsoft.VisualStudio.Razor.LanguageClient.Debugging;
|
||||
|
||||
internal interface ILSPBreakpointSpanProvider
|
||||
{
|
||||
Task<Range?> GetBreakpointSpanAsync(LSPDocumentSnapshot documentSnapshot, long hostDocumentSyncVersion, Position position, CancellationToken cancellationToken);
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.LanguageServer.ContainedLanguage;
|
||||
using Microsoft.VisualStudio.LanguageServer.Protocol;
|
||||
|
||||
namespace Microsoft.VisualStudio.Razor.LanguageClient.Debugging;
|
||||
|
||||
internal interface ILSPProximityExpressionsProvider
|
||||
{
|
||||
Task<IReadOnlyList<string>?> GetProximityExpressionsAsync(LSPDocumentSnapshot documentSnapshot, long hostDocumentSyncVersion, Position position, CancellationToken cancellationToken);
|
||||
}
|
|
@ -1,14 +1,50 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.Razor.Logging;
|
||||
using Microsoft.CodeAnalysis.Razor.Protocol;
|
||||
using Microsoft.CodeAnalysis.Razor.Protocol.Debugging;
|
||||
using Microsoft.VisualStudio.LanguageServer.ContainedLanguage;
|
||||
using Microsoft.VisualStudio.LanguageServer.Protocol;
|
||||
using Range = Microsoft.VisualStudio.LanguageServer.Protocol.Range;
|
||||
|
||||
namespace Microsoft.VisualStudio.Razor.LanguageClient.Debugging;
|
||||
|
||||
internal abstract class LSPBreakpointSpanProvider
|
||||
[Export(typeof(ILSPBreakpointSpanProvider))]
|
||||
[method: ImportingConstructor]
|
||||
internal class LSPBreakpointSpanProvider(
|
||||
LSPRequestInvoker requestInvoker,
|
||||
ILoggerFactory loggerFactory) : ILSPBreakpointSpanProvider
|
||||
{
|
||||
public abstract Task<Range?> GetBreakpointSpanAsync(LSPDocumentSnapshot documentSnapshot, Position position, CancellationToken cancellationToken);
|
||||
private readonly LSPRequestInvoker _requestInvoker = requestInvoker;
|
||||
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<LSPBreakpointSpanProvider>();
|
||||
|
||||
public async Task<Range?> GetBreakpointSpanAsync(LSPDocumentSnapshot documentSnapshot, long hostDocumentSyncVersion, Position position, CancellationToken cancellationToken)
|
||||
{
|
||||
var languageQueryParams = new RazorBreakpointSpanParams()
|
||||
{
|
||||
Position = position,
|
||||
Uri = documentSnapshot.Uri,
|
||||
HostDocumentSyncVersion = hostDocumentSyncVersion
|
||||
};
|
||||
|
||||
var response = await _requestInvoker.ReinvokeRequestOnServerAsync<RazorBreakpointSpanParams, RazorBreakpointSpanResponse>(
|
||||
documentSnapshot.Snapshot.TextBuffer,
|
||||
LanguageServerConstants.RazorBreakpointSpanEndpoint,
|
||||
RazorLSPConstants.RazorLanguageServerName,
|
||||
languageQueryParams,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var languageResponse = response?.Response;
|
||||
if (languageResponse is null)
|
||||
{
|
||||
_logger.LogInformation($"The breakpoint position could not be mapped to a valid range.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return languageResponse.Range;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,14 +2,49 @@
|
|||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CodeAnalysis.Razor.Logging;
|
||||
using Microsoft.CodeAnalysis.Razor.Protocol;
|
||||
using Microsoft.CodeAnalysis.Razor.Protocol.Debugging;
|
||||
using Microsoft.VisualStudio.LanguageServer.ContainedLanguage;
|
||||
using Microsoft.VisualStudio.LanguageServer.Protocol;
|
||||
|
||||
namespace Microsoft.VisualStudio.Razor.LanguageClient.Debugging;
|
||||
|
||||
internal abstract class LSPProximityExpressionsProvider
|
||||
[Export(typeof(ILSPProximityExpressionsProvider))]
|
||||
[method: ImportingConstructor]
|
||||
internal class LSPProximityExpressionsProvider(
|
||||
LSPRequestInvoker requestInvoker,
|
||||
ILoggerFactory loggerFactory) : ILSPProximityExpressionsProvider
|
||||
{
|
||||
public abstract Task<IReadOnlyList<string>?> GetProximityExpressionsAsync(LSPDocumentSnapshot documentSnapshot, Position position, CancellationToken cancellationToken);
|
||||
private readonly LSPRequestInvoker _requestInvoker = requestInvoker;
|
||||
private readonly ILogger _logger = loggerFactory.GetOrCreateLogger<LSPProximityExpressionsProvider>();
|
||||
|
||||
public async Task<IReadOnlyList<string>?> GetProximityExpressionsAsync(LSPDocumentSnapshot documentSnapshot, long hostDocumentSyncVersion, Position position, CancellationToken cancellationToken)
|
||||
{
|
||||
var proximityExpressionsParams = new RazorProximityExpressionsParams()
|
||||
{
|
||||
Position = position,
|
||||
Uri = documentSnapshot.Uri,
|
||||
HostDocumentSyncVersion = hostDocumentSyncVersion
|
||||
};
|
||||
|
||||
var response = await _requestInvoker.ReinvokeRequestOnServerAsync<RazorProximityExpressionsParams, RazorProximityExpressionsResponse>(
|
||||
documentSnapshot.Snapshot.TextBuffer,
|
||||
LanguageServerConstants.RazorProximityExpressionsEndpoint,
|
||||
RazorLSPConstants.RazorLanguageServerName,
|
||||
proximityExpressionsParams,
|
||||
cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var languageResponse = response?.Response;
|
||||
if (languageResponse is null)
|
||||
{
|
||||
_logger.LogInformation($"The proximity expressions could not be resolved.");
|
||||
return null;
|
||||
}
|
||||
|
||||
return languageResponse.Expressions;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Utilities;
|
||||
using Microsoft.VisualStudio.LanguageServer.ContainedLanguage;
|
||||
using Microsoft.VisualStudio.LanguageServer.Protocol;
|
||||
using Microsoft.VisualStudio.Razor.Debugging;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
using Range = Microsoft.VisualStudio.LanguageServer.Protocol.Range;
|
||||
|
||||
namespace Microsoft.VisualStudio.Razor.LanguageClient.Debugging;
|
||||
|
||||
[Export(typeof(IRazorBreakpointResolver))]
|
||||
[method: ImportingConstructor]
|
||||
internal class RazorBreakpointResolver(
|
||||
FileUriProvider fileUriProvider,
|
||||
LSPDocumentManager documentManager,
|
||||
ILSPBreakpointSpanProvider breakpointSpanProvider) : IRazorBreakpointResolver
|
||||
{
|
||||
private record CacheKey(Uri DocumentUri, long? HostDocumentSyncVersion, int Line, int Character);
|
||||
|
||||
private readonly FileUriProvider _fileUriProvider = fileUriProvider;
|
||||
private readonly LSPDocumentManager _documentManager = documentManager;
|
||||
private readonly ILSPBreakpointSpanProvider _breakpointSpanProvider = breakpointSpanProvider;
|
||||
|
||||
// 4 is a magic number that was determined based on the functionality of VisualStudio. Currently when you set or edit a breakpoint
|
||||
// we get called with two different locations for the same breakpoint. Because of this 2 time call our size must be at least 2,
|
||||
// we grow it to 4 just to be safe for lesser known scenarios.
|
||||
private readonly MemoryCache<CacheKey, Range> _cache = new(sizeLimit: 4);
|
||||
|
||||
public async Task<Range?> TryResolveBreakpointRangeAsync(ITextBuffer textBuffer, int lineIndex, int characterIndex, CancellationToken cancellationToken)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// TODO: Support multiple C# documents per Razor document.
|
||||
if (!documentSnapshot.TryGetVirtualDocument<CSharpVirtualDocumentSnapshot>(out var virtualDocument) ||
|
||||
virtualDocument.HostDocumentSyncVersion is not { } hostDocumentSyncVersion)
|
||||
{
|
||||
Debug.Fail($"Some how there's no C# document associated with the host Razor document {documentUri.OriginalString} when validating breakpoint locations.");
|
||||
return null;
|
||||
}
|
||||
|
||||
var cacheKey = new CacheKey(documentSnapshot.Uri, virtualDocument.HostDocumentSyncVersion, lineIndex, characterIndex);
|
||||
if (_cache.TryGetValue(cacheKey, out var cachedRange))
|
||||
{
|
||||
// We've seen this request before, no need to go async.
|
||||
return cachedRange;
|
||||
}
|
||||
|
||||
var position = VsLspFactory.CreatePosition(lineIndex, characterIndex);
|
||||
var hostDocumentRange = await _breakpointSpanProvider.GetBreakpointSpanAsync(documentSnapshot, hostDocumentSyncVersion, position, cancellationToken).ConfigureAwait(false);
|
||||
if (hostDocumentRange is null)
|
||||
{
|
||||
// can't map the position, invalid breakpoint location.
|
||||
return null;
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the MIT license. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Razor.Utilities;
|
||||
using Microsoft.VisualStudio.LanguageServer.ContainedLanguage;
|
||||
using Microsoft.VisualStudio.LanguageServer.Protocol;
|
||||
using Microsoft.VisualStudio.Razor.Debugging;
|
||||
using Microsoft.VisualStudio.Text;
|
||||
|
||||
namespace Microsoft.VisualStudio.Razor.LanguageClient.Debugging;
|
||||
|
||||
[Export(typeof(IRazorProximityExpressionResolver))]
|
||||
[method: ImportingConstructor]
|
||||
internal class RazorProximityExpressionResolver(
|
||||
FileUriProvider fileUriProvider,
|
||||
LSPDocumentManager documentManager,
|
||||
ILSPProximityExpressionsProvider proximityExpressionsProvider) : IRazorProximityExpressionResolver
|
||||
{
|
||||
private record CacheKey(Uri DocumentUri, long? HostDocumentSyncVersion, int Line, int Character);
|
||||
|
||||
private readonly FileUriProvider _fileUriProvider = fileUriProvider;
|
||||
private readonly LSPDocumentManager _documentManager = documentManager;
|
||||
private readonly ILSPProximityExpressionsProvider _proximityExpressionsProvider = proximityExpressionsProvider;
|
||||
|
||||
// 10 is a magic number where this effectively represents our ability to cache the last 10 "hit" breakpoint locations
|
||||
// corresponding proximity expressions which enables us not to go "async" in those re-hit scenarios.
|
||||
private readonly MemoryCache<CacheKey, IReadOnlyList<string>> _cache = new(sizeLimit: 10);
|
||||
|
||||
public async Task<IReadOnlyList<string>?> TryResolveProximityExpressionsAsync(ITextBuffer textBuffer, int lineIndex, int characterIndex, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!_fileUriProvider.TryGet(textBuffer, out var documentUri))
|
||||
{
|
||||
// Not an addressable Razor document. Do not allow expression resolution 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 resolve expressions here. In practice this shouldn't happen, just being defensive.
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: Support multiple C# documents per Razor document.
|
||||
if (!documentSnapshot.TryGetVirtualDocument<CSharpVirtualDocumentSnapshot>(out var virtualDocument) ||
|
||||
virtualDocument.HostDocumentSyncVersion is not { } hostDocumentSyncVersion)
|
||||
{
|
||||
Debug.Fail($"Some how there's no C# document associated with the host Razor document {documentUri.OriginalString} when resolving proximity expressions.");
|
||||
return null;
|
||||
}
|
||||
|
||||
var cacheKey = new CacheKey(documentSnapshot.Uri, virtualDocument.HostDocumentSyncVersion, lineIndex, characterIndex);
|
||||
if (_cache.TryGetValue(cacheKey, out var cachedExpressions))
|
||||
{
|
||||
// We've seen this request before, no need to go async.
|
||||
return cachedExpressions;
|
||||
}
|
||||
|
||||
var position = VsLspFactory.CreatePosition(lineIndex, characterIndex);
|
||||
var proximityExpressions = await _proximityExpressionsProvider.GetProximityExpressionsAsync(documentSnapshot, hostDocumentSyncVersion, position, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// Cache range so if we're asked again for this document/line/character we don't have to go async.
|
||||
// Note: If we didn't get any proximity expressions back--likely due to an error--we cache an empty array.
|
||||
_cache.Set(cacheKey, proximityExpressions ?? []);
|
||||
|
||||
return proximityExpressions;
|
||||
}
|
||||
}
|
|
@ -16,16 +16,16 @@ namespace Microsoft.VisualStudio.Razor;
|
|||
|
||||
internal partial class RazorLanguageService : IVsLanguageDebugInfo
|
||||
{
|
||||
private readonly RazorBreakpointResolver _breakpointResolver;
|
||||
private readonly RazorProximityExpressionResolver _proximityExpressionResolver;
|
||||
private readonly IRazorBreakpointResolver _breakpointResolver;
|
||||
private readonly IRazorProximityExpressionResolver _proximityExpressionResolver;
|
||||
private readonly ILspServerActivationTracker _lspServerActivationTracker;
|
||||
private readonly IUIThreadOperationExecutor _uiThreadOperationExecutor;
|
||||
private readonly IVsEditorAdaptersFactoryService _editorAdaptersFactory;
|
||||
private readonly JoinableTaskFactory _joinableTaskFactory;
|
||||
|
||||
public RazorLanguageService(
|
||||
RazorBreakpointResolver breakpointResolver,
|
||||
RazorProximityExpressionResolver proximityExpressionResolver,
|
||||
IRazorBreakpointResolver breakpointResolver,
|
||||
IRazorProximityExpressionResolver proximityExpressionResolver,
|
||||
ILspServerActivationTracker lspServerActivationTracker,
|
||||
IUIThreadOperationExecutor uiThreadOperationExecutor,
|
||||
IVsEditorAdaptersFactoryService editorAdaptersFactory,
|
||||
|
|
|
@ -64,8 +64,8 @@ internal sealed class RazorPackage : AsyncPackage
|
|||
container.AddService(typeof(RazorLanguageService), (container, type) =>
|
||||
{
|
||||
var componentModel = (IComponentModel)GetGlobalService(typeof(SComponentModel));
|
||||
var breakpointResolver = componentModel.GetService<RazorBreakpointResolver>();
|
||||
var proximityExpressionResolver = componentModel.GetService<RazorProximityExpressionResolver>();
|
||||
var breakpointResolver = componentModel.GetService<IRazorBreakpointResolver>();
|
||||
var proximityExpressionResolver = componentModel.GetService<IRazorProximityExpressionResolver>();
|
||||
var uiThreadOperationExecutor = componentModel.GetService<IUIThreadOperationExecutor>();
|
||||
var editorAdaptersFactory = componentModel.GetService<IVsEditorAdaptersFactoryService>();
|
||||
var lspServerActivationTracker = componentModel.GetService<ILspServerActivationTracker>();
|
||||
|
|
|
@ -39,7 +39,8 @@ public class RazorBreakpointSpanEndpointTest : LanguageServerTestBase
|
|||
var request = new RazorBreakpointSpanParams()
|
||||
{
|
||||
Uri = documentPath,
|
||||
Position = VsLspFactory.CreatePosition(1, 0)
|
||||
Position = VsLspFactory.CreatePosition(1, 0),
|
||||
HostDocumentSyncVersion = 0,
|
||||
};
|
||||
codeDocument.SetUnsupported();
|
||||
var requestContext = CreateRazorRequestContext(documentContext);
|
||||
|
@ -64,7 +65,8 @@ public class RazorBreakpointSpanEndpointTest : LanguageServerTestBase
|
|||
var request = new RazorBreakpointSpanParams()
|
||||
{
|
||||
Uri = documentPath,
|
||||
Position = VsLspFactory.CreatePosition(1, 0)
|
||||
Position = VsLspFactory.CreatePosition(1, 0),
|
||||
HostDocumentSyncVersion = 0,
|
||||
};
|
||||
var expectedRange = VsLspFactory.CreateSingleLineRange(line: 1, character: 5, length: 14);
|
||||
var requestContext = CreateRazorRequestContext(documentContext);
|
||||
|
@ -89,7 +91,8 @@ public class RazorBreakpointSpanEndpointTest : LanguageServerTestBase
|
|||
var request = new RazorBreakpointSpanParams()
|
||||
{
|
||||
Uri = documentPath,
|
||||
Position = VsLspFactory.CreatePosition(1, 0)
|
||||
Position = VsLspFactory.CreatePosition(1, 0),
|
||||
HostDocumentSyncVersion = 0,
|
||||
};
|
||||
var expectedRange = VsLspFactory.CreateSingleLineRange(line: 1, character: 4, length: 12);
|
||||
var requestContext = CreateRazorRequestContext(documentContext);
|
||||
|
@ -114,7 +117,8 @@ public class RazorBreakpointSpanEndpointTest : LanguageServerTestBase
|
|||
var request = new RazorBreakpointSpanParams()
|
||||
{
|
||||
Uri = documentPath,
|
||||
Position = VsLspFactory.CreatePosition(1, 0)
|
||||
Position = VsLspFactory.CreatePosition(1, 0),
|
||||
HostDocumentSyncVersion = 0,
|
||||
};
|
||||
var expectedRange = VsLspFactory.CreateSingleLineRange(line: 1, character: 5, length: 14);
|
||||
var requestContext = CreateRazorRequestContext(documentContext);
|
||||
|
@ -139,7 +143,8 @@ public class RazorBreakpointSpanEndpointTest : LanguageServerTestBase
|
|||
var request = new RazorBreakpointSpanParams()
|
||||
{
|
||||
Uri = documentPath,
|
||||
Position = VsLspFactory.CreatePosition(1, 0)
|
||||
Position = VsLspFactory.CreatePosition(1, 0),
|
||||
HostDocumentSyncVersion = 0,
|
||||
};
|
||||
var expectedRange = VsLspFactory.CreateSingleLineRange(line: 1, character: 4, length: 12);
|
||||
var requestContext = CreateRazorRequestContext(documentContext);
|
||||
|
@ -165,7 +170,8 @@ public class RazorBreakpointSpanEndpointTest : LanguageServerTestBase
|
|||
var request = new RazorBreakpointSpanParams()
|
||||
{
|
||||
Uri = documentPath,
|
||||
Position = VsLspFactory.CreatePosition(1, 0)
|
||||
Position = VsLspFactory.CreatePosition(1, 0),
|
||||
HostDocumentSyncVersion = 0,
|
||||
};
|
||||
var requestContext = CreateRazorRequestContext(documentContext);
|
||||
|
||||
|
@ -189,7 +195,8 @@ public class RazorBreakpointSpanEndpointTest : LanguageServerTestBase
|
|||
var request = new RazorBreakpointSpanParams()
|
||||
{
|
||||
Uri = documentPath,
|
||||
Position = VsLspFactory.CreatePosition(1, 0)
|
||||
Position = VsLspFactory.CreatePosition(1, 0),
|
||||
HostDocumentSyncVersion = 0,
|
||||
};
|
||||
var requestContext = CreateRazorRequestContext(documentContext);
|
||||
|
||||
|
@ -216,7 +223,8 @@ public class RazorBreakpointSpanEndpointTest : LanguageServerTestBase
|
|||
var request = new RazorBreakpointSpanParams()
|
||||
{
|
||||
Uri = documentPath,
|
||||
Position = VsLspFactory.CreatePosition(1, 0)
|
||||
Position = VsLspFactory.CreatePosition(1, 0),
|
||||
HostDocumentSyncVersion = 0,
|
||||
};
|
||||
var requestContext = CreateRazorRequestContext(documentContext);
|
||||
|
||||
|
@ -243,7 +251,8 @@ public class RazorBreakpointSpanEndpointTest : LanguageServerTestBase
|
|||
var request = new RazorBreakpointSpanParams()
|
||||
{
|
||||
Uri = documentPath,
|
||||
Position = VsLspFactory.CreatePosition(2, 0)
|
||||
Position = VsLspFactory.CreatePosition(2, 0),
|
||||
HostDocumentSyncVersion = 0,
|
||||
};
|
||||
var requestContext = CreateRazorRequestContext(documentContext);
|
||||
|
||||
|
@ -258,7 +267,7 @@ public class RazorBreakpointSpanEndpointTest : LanguageServerTestBase
|
|||
{
|
||||
var sourceDocument = TestRazorSourceDocument.Create(text);
|
||||
var projectEngine = RazorProjectEngine.Create(builder => { });
|
||||
var codeDocument = projectEngine.ProcessDesignTime(sourceDocument, fileKind ?? FileKinds.Legacy, importSources: default, Array.Empty<TagHelperDescriptor>());
|
||||
var codeDocument = projectEngine.ProcessDesignTime(sourceDocument, fileKind ?? FileKinds.Legacy, importSources: default, tagHelpers: []);
|
||||
return codeDocument;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ public class RazorProximityExpressionsEndpointTest : LanguageServerTestBase
|
|||
{
|
||||
Uri = documentPath,
|
||||
Position = VsLspFactory.CreatePosition(1, 0),
|
||||
HostDocumentSyncVersion = 0,
|
||||
};
|
||||
codeDocument.SetUnsupported();
|
||||
var requestContext = CreateRazorRequestContext(documentContext);
|
||||
|
@ -65,6 +66,7 @@ public class RazorProximityExpressionsEndpointTest : LanguageServerTestBase
|
|||
{
|
||||
Uri = documentPath,
|
||||
Position = VsLspFactory.CreatePosition(1, 8),
|
||||
HostDocumentSyncVersion = 0,
|
||||
};
|
||||
var requestContext = CreateRazorRequestContext(documentContext);
|
||||
|
||||
|
@ -90,6 +92,7 @@ public class RazorProximityExpressionsEndpointTest : LanguageServerTestBase
|
|||
{
|
||||
Uri = documentPath,
|
||||
Position = VsLspFactory.CreatePosition(1, 0),
|
||||
HostDocumentSyncVersion = 0,
|
||||
};
|
||||
var requestContext = CreateRazorRequestContext(documentContext);
|
||||
|
||||
|
@ -115,6 +118,7 @@ public class RazorProximityExpressionsEndpointTest : LanguageServerTestBase
|
|||
{
|
||||
Uri = documentPath,
|
||||
Position = VsLspFactory.CreatePosition(1, 0),
|
||||
HostDocumentSyncVersion = 0,
|
||||
};
|
||||
var requestContext = CreateRazorRequestContext(documentContext);
|
||||
|
||||
|
@ -142,6 +146,7 @@ public class RazorProximityExpressionsEndpointTest : LanguageServerTestBase
|
|||
{
|
||||
Uri = documentPath,
|
||||
Position = VsLspFactory.DefaultPosition,
|
||||
HostDocumentSyncVersion = 0,
|
||||
};
|
||||
var requestContext = CreateRazorRequestContext(documentContext);
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ using Range = Microsoft.VisualStudio.LanguageServer.Protocol.Range;
|
|||
|
||||
namespace Microsoft.VisualStudio.Razor.LanguageClient.Debugging;
|
||||
|
||||
public class DefaultRazorBreakpointResolverTest : ToolingTestBase
|
||||
public class RazorBreakpointResolverTest : ToolingTestBase
|
||||
{
|
||||
private const string ValidBreakpointCSharp = "private int foo = 123;";
|
||||
private const string InvalidBreakpointCSharp = "private int bar;";
|
||||
|
@ -27,9 +27,9 @@ public class DefaultRazorBreakpointResolverTest : ToolingTestBase
|
|||
private readonly ITextBuffer _csharpTextBuffer;
|
||||
private readonly Uri _documentUri;
|
||||
private readonly Uri _csharpDocumentUri;
|
||||
private readonly ITextBuffer _hostTextbuffer;
|
||||
private readonly ITextBuffer _hostTextBuffer;
|
||||
|
||||
public DefaultRazorBreakpointResolverTest(ITestOutputHelper testOutput)
|
||||
public RazorBreakpointResolverTest(ITestOutputHelper testOutput)
|
||||
: base(testOutput)
|
||||
{
|
||||
_documentUri = new Uri("file://C:/path/to/file.razor", UriKind.Absolute);
|
||||
|
@ -51,7 +51,7 @@ public class DefaultRazorBreakpointResolverTest : ToolingTestBase
|
|||
{{InvalidBreakpointCSharp}}
|
||||
}
|
||||
""");
|
||||
_hostTextbuffer = new TestTextBuffer(textBufferSnapshot);
|
||||
_hostTextBuffer = new TestTextBuffer(textBufferSnapshot);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -77,7 +77,7 @@ public class DefaultRazorBreakpointResolverTest : ToolingTestBase
|
|||
var resolver = CreateResolverWith(documentManager: documentManager);
|
||||
|
||||
// Act
|
||||
var result = await resolver.TryResolveBreakpointRangeAsync(_hostTextbuffer, lineIndex: 0, characterIndex: 1, DisposalToken);
|
||||
var result = await resolver.TryResolveBreakpointRangeAsync(_hostTextBuffer, lineIndex: 0, characterIndex: 1, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
|
@ -94,7 +94,7 @@ public class DefaultRazorBreakpointResolverTest : ToolingTestBase
|
|||
var resolver = CreateResolverWith(documentManager: documentManager);
|
||||
|
||||
// Act
|
||||
var expressions = await resolver.TryResolveBreakpointRangeAsync(_hostTextbuffer, lineIndex: 0, characterIndex: 1, DisposalToken);
|
||||
var expressions = await resolver.TryResolveBreakpointRangeAsync(_hostTextBuffer, lineIndex: 0, characterIndex: 1, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.Null(expressions);
|
||||
|
@ -107,7 +107,7 @@ public class DefaultRazorBreakpointResolverTest : ToolingTestBase
|
|||
var resolver = CreateResolverWith();
|
||||
|
||||
// Act
|
||||
var breakpointRange = await resolver.TryResolveBreakpointRangeAsync(_hostTextbuffer, lineIndex: 0, characterIndex: 1, DisposalToken);
|
||||
var breakpointRange = await resolver.TryResolveBreakpointRangeAsync(_hostTextBuffer, lineIndex: 0, characterIndex: 1, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.Null(breakpointRange);
|
||||
|
@ -117,11 +117,11 @@ public class DefaultRazorBreakpointResolverTest : ToolingTestBase
|
|||
public async Task TryResolveBreakpointRangeAsync_NotValidBreakpointLocation_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var hostDocumentPosition = GetPosition(InvalidBreakpointCSharp, _hostTextbuffer);
|
||||
var hostDocumentPosition = GetPosition(InvalidBreakpointCSharp, _hostTextBuffer);
|
||||
var resolver = CreateResolverWith();
|
||||
|
||||
// Act
|
||||
var breakpointRange = await resolver.TryResolveBreakpointRangeAsync(_hostTextbuffer, hostDocumentPosition.Line, hostDocumentPosition.Character, DisposalToken);
|
||||
var breakpointRange = await resolver.TryResolveBreakpointRangeAsync(_hostTextBuffer, hostDocumentPosition.Line, hostDocumentPosition.Character, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.Null(breakpointRange);
|
||||
|
@ -131,7 +131,7 @@ public class DefaultRazorBreakpointResolverTest : ToolingTestBase
|
|||
public async Task TryResolveBreakpointRangeAsync_MappableCSharpBreakpointLocation_ReturnsHostBreakpointLocation()
|
||||
{
|
||||
// Arrange
|
||||
var hostDocumentPosition = GetPosition(ValidBreakpointCSharp, _hostTextbuffer);
|
||||
var hostDocumentPosition = GetPosition(ValidBreakpointCSharp, _hostTextBuffer);
|
||||
var hostBreakpointRange = VsLspFactory.CreateSingleLineRange(start: hostDocumentPosition, length: ValidBreakpointCSharp.Length);
|
||||
var projectionProvider = new TestLSPBreakpointSpanProvider(
|
||||
_documentUri,
|
||||
|
@ -142,34 +142,35 @@ public class DefaultRazorBreakpointResolverTest : ToolingTestBase
|
|||
var resolver = CreateResolverWith(projectionProvider: projectionProvider);
|
||||
|
||||
// Act
|
||||
var breakpointRange = await resolver.TryResolveBreakpointRangeAsync(_hostTextbuffer, hostDocumentPosition.Line, hostDocumentPosition.Character, DisposalToken);
|
||||
var breakpointRange = await resolver.TryResolveBreakpointRangeAsync(_hostTextBuffer, hostDocumentPosition.Line, hostDocumentPosition.Character, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(hostBreakpointRange, breakpointRange);
|
||||
}
|
||||
|
||||
private RazorBreakpointResolver CreateResolverWith(
|
||||
private IRazorBreakpointResolver CreateResolverWith(
|
||||
FileUriProvider uriProvider = null,
|
||||
LSPDocumentManager documentManager = null,
|
||||
LSPBreakpointSpanProvider projectionProvider = null)
|
||||
ILSPBreakpointSpanProvider projectionProvider = null)
|
||||
{
|
||||
var documentUri = _documentUri;
|
||||
uriProvider ??= Mock.Of<FileUriProvider>(provider => provider.TryGet(_hostTextbuffer, out documentUri) == true && provider.TryGet(It.IsNotIn(_hostTextbuffer), out It.Ref<Uri>.IsAny) == false, MockBehavior.Strict);
|
||||
uriProvider ??= Mock.Of<FileUriProvider>(provider => provider.TryGet(_hostTextBuffer, out documentUri) == true && provider.TryGet(It.IsNotIn(_hostTextBuffer), out It.Ref<Uri>.IsAny) == false, MockBehavior.Strict);
|
||||
var csharpVirtualDocumentSnapshot = new CSharpVirtualDocumentSnapshot(projectKey: default, _csharpDocumentUri, _csharpTextBuffer.CurrentSnapshot, hostDocumentSyncVersion: 0);
|
||||
LSPDocumentSnapshot documentSnapshot = new TestLSPDocumentSnapshot(_documentUri, 0, csharpVirtualDocumentSnapshot);
|
||||
documentManager ??= Mock.Of<LSPDocumentManager>(manager => manager.TryGetDocument(_documentUri, out documentSnapshot) == true, MockBehavior.Strict);
|
||||
if (projectionProvider is null)
|
||||
{
|
||||
projectionProvider = new Mock<LSPBreakpointSpanProvider>(MockBehavior.Strict).Object;
|
||||
projectionProvider = new Mock<ILSPBreakpointSpanProvider>(MockBehavior.Strict).Object;
|
||||
Mock.Get(projectionProvider)
|
||||
.Setup(projectionProvider => projectionProvider.GetBreakpointSpanAsync(
|
||||
It.IsAny<LSPDocumentSnapshot>(),
|
||||
It.IsAny<long>(),
|
||||
It.IsAny<Position>(),
|
||||
DisposalToken))
|
||||
.ReturnsAsync(value: null);
|
||||
}
|
||||
|
||||
var razorBreakpointResolver = new DefaultRazorBreakpointResolver(uriProvider, documentManager, projectionProvider);
|
||||
var razorBreakpointResolver = new RazorBreakpointResolver(uriProvider, documentManager, projectionProvider);
|
||||
|
||||
return razorBreakpointResolver;
|
||||
}
|
|
@ -20,16 +20,16 @@ using Xunit.Abstractions;
|
|||
|
||||
namespace Microsoft.VisualStudio.Razor.LanguageClient.Debugging;
|
||||
|
||||
public class DefaultRazorProximityExpressionResolverTest : ToolingTestBase
|
||||
public class RazorProximityExpressionResolverTest : ToolingTestBase
|
||||
{
|
||||
private readonly string _validProximityExpressionRoot;
|
||||
private readonly string _invalidProximityExpressionRoot;
|
||||
private readonly ITextBuffer _csharpTextBuffer;
|
||||
private readonly Uri _documentUri;
|
||||
private readonly Uri _csharpDocumentUri;
|
||||
private readonly ITextBuffer _hostTextbuffer;
|
||||
private readonly ITextBuffer _hostTextBuffer;
|
||||
|
||||
public DefaultRazorProximityExpressionResolverTest(ITestOutputHelper testOutput)
|
||||
public RazorProximityExpressionResolverTest(ITestOutputHelper testOutput)
|
||||
: base(testOutput)
|
||||
{
|
||||
_documentUri = new Uri("file://C:/path/to/file.razor", UriKind.Absolute);
|
||||
|
@ -51,7 +51,7 @@ public class DefaultRazorProximityExpressionResolverTest : ToolingTestBase
|
|||
_csharpTextBuffer = new TestTextBuffer(csharpTextSnapshot);
|
||||
|
||||
var textBufferSnapshot = new StringTextSnapshot($$"""@{{{_invalidProximityExpressionRoot}}} @code {{{_validProximityExpressionRoot}}}""");
|
||||
_hostTextbuffer = new TestTextBuffer(textBufferSnapshot);
|
||||
_hostTextBuffer = new TestTextBuffer(textBufferSnapshot);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
@ -79,7 +79,7 @@ public class DefaultRazorProximityExpressionResolverTest : ToolingTestBase
|
|||
var resolver = CreateResolverWith(documentManager: documentManager);
|
||||
|
||||
// Act
|
||||
var result = await resolver.TryResolveProximityExpressionsAsync(_hostTextbuffer, lineIndex: 0, characterIndex: 1, DisposalToken);
|
||||
var result = await resolver.TryResolveProximityExpressionsAsync(_hostTextBuffer, lineIndex: 0, characterIndex: 1, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
|
@ -96,25 +96,25 @@ public class DefaultRazorProximityExpressionResolverTest : ToolingTestBase
|
|||
var resolver = CreateResolverWith(documentManager: documentManager);
|
||||
|
||||
// Act
|
||||
var expressions = await resolver.TryResolveProximityExpressionsAsync(_hostTextbuffer, lineIndex: 0, characterIndex: 1, DisposalToken);
|
||||
var expressions = await resolver.TryResolveProximityExpressionsAsync(_hostTextBuffer, lineIndex: 0, characterIndex: 1, DisposalToken);
|
||||
|
||||
// Assert
|
||||
Assert.Null(expressions);
|
||||
}
|
||||
|
||||
private RazorProximityExpressionResolver CreateResolverWith(
|
||||
private IRazorProximityExpressionResolver CreateResolverWith(
|
||||
FileUriProvider uriProvider = null,
|
||||
LSPDocumentManager documentManager = null)
|
||||
{
|
||||
var documentUri = _documentUri;
|
||||
uriProvider ??= Mock.Of<FileUriProvider>(provider => provider.TryGet(_hostTextbuffer, out documentUri) == true && provider.TryGet(It.IsNotIn(_hostTextbuffer), out It.Ref<Uri>.IsAny) == false, MockBehavior.Strict);
|
||||
uriProvider ??= Mock.Of<FileUriProvider>(provider => provider.TryGet(_hostTextBuffer, out documentUri) == true && provider.TryGet(It.IsNotIn(_hostTextBuffer), out It.Ref<Uri>.IsAny) == false, MockBehavior.Strict);
|
||||
var csharpVirtualDocumentSnapshot = new CSharpVirtualDocumentSnapshot(projectKey: default, _csharpDocumentUri, _csharpTextBuffer.CurrentSnapshot, hostDocumentSyncVersion: 0);
|
||||
LSPDocumentSnapshot documentSnapshot = new TestLSPDocumentSnapshot(_documentUri, 0, csharpVirtualDocumentSnapshot);
|
||||
documentManager ??= Mock.Of<LSPDocumentManager>(
|
||||
manager => manager.TryGetDocument(_documentUri, out documentSnapshot) == true,
|
||||
MockBehavior.Strict);
|
||||
|
||||
var razorProximityExpressionResolver = new DefaultRazorProximityExpressionResolver(
|
||||
var razorProximityExpressionResolver = new RazorProximityExpressionResolver(
|
||||
uriProvider,
|
||||
documentManager,
|
||||
TestLSPProximityExpressionProvider.Instance);
|
||||
|
@ -122,7 +122,7 @@ public class DefaultRazorProximityExpressionResolverTest : ToolingTestBase
|
|||
return razorProximityExpressionResolver;
|
||||
}
|
||||
|
||||
private class TestLSPProximityExpressionProvider : LSPProximityExpressionsProvider
|
||||
private class TestLSPProximityExpressionProvider : ILSPProximityExpressionsProvider
|
||||
{
|
||||
public static readonly TestLSPProximityExpressionProvider Instance = new();
|
||||
|
||||
|
@ -130,7 +130,7 @@ public class DefaultRazorProximityExpressionResolverTest : ToolingTestBase
|
|||
{
|
||||
}
|
||||
|
||||
public override Task<IReadOnlyList<string>> GetProximityExpressionsAsync(LSPDocumentSnapshot documentSnapshot, Position position, CancellationToken cancellationToken)
|
||||
public Task<IReadOnlyList<string>> GetProximityExpressionsAsync(LSPDocumentSnapshot documentSnapshot, long hostDocumentSyncVersion, Position position, CancellationToken cancellationToken)
|
||||
{
|
||||
return SpecializedTasks.Null<IReadOnlyList<string>>();
|
||||
}
|
|
@ -15,7 +15,7 @@ using Range = Microsoft.VisualStudio.LanguageServer.Protocol.Range;
|
|||
|
||||
namespace Microsoft.VisualStudio.Razor.LanguageClient.Debugging;
|
||||
|
||||
internal class TestLSPBreakpointSpanProvider : LSPBreakpointSpanProvider
|
||||
internal class TestLSPBreakpointSpanProvider : ILSPBreakpointSpanProvider
|
||||
{
|
||||
private readonly Uri _documentUri;
|
||||
private readonly IReadOnlyDictionary<Position, Range> _mappings;
|
||||
|
@ -36,7 +36,7 @@ internal class TestLSPBreakpointSpanProvider : LSPBreakpointSpanProvider
|
|||
_mappings = mappings;
|
||||
}
|
||||
|
||||
public override Task<Range> GetBreakpointSpanAsync(LSPDocumentSnapshot documentSnapshot, Position position, CancellationToken cancellationToken)
|
||||
public Task<Range> GetBreakpointSpanAsync(LSPDocumentSnapshot documentSnapshot, long hostDocumentSyncVersion, Position position, CancellationToken cancellationToken)
|
||||
{
|
||||
if (documentSnapshot.Uri != _documentUri)
|
||||
{
|
||||
|
|
|
@ -70,7 +70,7 @@ public class RazorLanguageService_IVsLanguageDebugInfoTest(ITestOutputHelper tes
|
|||
{
|
||||
// Arrange
|
||||
var breakpointRange = VsLspFactory.CreateRange(2, 4, 3, 5);
|
||||
var breakpointResolver = Mock.Of<RazorBreakpointResolver>(resolver => resolver.TryResolveBreakpointRangeAsync(It.IsAny<ITextBuffer>(), 0, 0, It.IsAny<CancellationToken>()) == System.Threading.Tasks.Task.FromResult(breakpointRange), MockBehavior.Strict);
|
||||
var breakpointResolver = Mock.Of<IRazorBreakpointResolver>(resolver => resolver.TryResolveBreakpointRangeAsync(It.IsAny<ITextBuffer>(), 0, 0, It.IsAny<CancellationToken>()) == System.Threading.Tasks.Task.FromResult(breakpointRange), MockBehavior.Strict);
|
||||
var languageService = CreateLanguageServiceWith(breakpointResolver);
|
||||
|
||||
// Act
|
||||
|
@ -146,7 +146,7 @@ public class RazorLanguageService_IVsLanguageDebugInfoTest(ITestOutputHelper tes
|
|||
{
|
||||
// Arrange
|
||||
IReadOnlyList<string> expressions = new[] { "something" };
|
||||
var resolver = Mock.Of<RazorProximityExpressionResolver>(resolver => resolver.TryResolveProximityExpressionsAsync(It.IsAny<ITextBuffer>(), 0, 0, It.IsAny<CancellationToken>()) == System.Threading.Tasks.Task.FromResult(expressions), MockBehavior.Strict);
|
||||
var resolver = Mock.Of<IRazorProximityExpressionResolver>(resolver => resolver.TryResolveProximityExpressionsAsync(It.IsAny<ITextBuffer>(), 0, 0, It.IsAny<CancellationToken>()) == System.Threading.Tasks.Task.FromResult(expressions), MockBehavior.Strict);
|
||||
var languageService = CreateLanguageServiceWith(proximityExpressionResolver: resolver);
|
||||
|
||||
// Act
|
||||
|
@ -174,14 +174,14 @@ public class RazorLanguageService_IVsLanguageDebugInfoTest(ITestOutputHelper tes
|
|||
}
|
||||
|
||||
private RazorLanguageService CreateLanguageServiceWith(
|
||||
RazorBreakpointResolver breakpointResolver = null,
|
||||
RazorProximityExpressionResolver proximityExpressionResolver = null,
|
||||
IRazorBreakpointResolver breakpointResolver = null,
|
||||
IRazorProximityExpressionResolver proximityExpressionResolver = null,
|
||||
IUIThreadOperationExecutor uiThreadOperationExecutor = null,
|
||||
IVsEditorAdaptersFactoryService editorAdaptersFactory = null)
|
||||
{
|
||||
if (breakpointResolver is null)
|
||||
{
|
||||
breakpointResolver = new Mock<RazorBreakpointResolver>(MockBehavior.Strict).Object;
|
||||
breakpointResolver = new Mock<IRazorBreakpointResolver>(MockBehavior.Strict).Object;
|
||||
Mock.Get(breakpointResolver)
|
||||
.Setup(r => r.TryResolveBreakpointRangeAsync(
|
||||
It.IsAny<ITextBuffer>(),
|
||||
|
@ -193,7 +193,7 @@ public class RazorLanguageService_IVsLanguageDebugInfoTest(ITestOutputHelper tes
|
|||
|
||||
if (proximityExpressionResolver is null)
|
||||
{
|
||||
proximityExpressionResolver = new Mock<RazorProximityExpressionResolver>(MockBehavior.Strict).Object;
|
||||
proximityExpressionResolver = new Mock<IRazorProximityExpressionResolver>(MockBehavior.Strict).Object;
|
||||
Mock.Get(proximityExpressionResolver)
|
||||
.Setup(r => r.TryResolveProximityExpressionsAsync(
|
||||
It.IsAny<ITextBuffer>(),
|
||||
|
|
|
@ -17,13 +17,15 @@ public class BreakpointSpanTests(ITestOutputHelper testOutputHelper) : AbstractR
|
|||
|
||||
// Wait for classifications to indicate Razor LSP is up and running
|
||||
await TestServices.Editor.WaitForComponentClassificationAsync(ControlledHangMitigatingCancellationToken);
|
||||
await TestServices.Editor.SetTextAsync("<p>@{ var abc = 123; }</p>", ControlledHangMitigatingCancellationToken);
|
||||
|
||||
// Act
|
||||
await TestServices.Debugger.SetBreakpointAsync(RazorProjectConstants.CounterRazorFile, line: 1, character: 1, ControlledHangMitigatingCancellationToken);
|
||||
await TestServices.RazorProjectSystem.WaitForCSharpVirtualDocumentUpdateAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.CounterRazorFile, async () =>
|
||||
{
|
||||
await TestServices.Editor.SetTextAsync("<p>@{ var abc = 123; }</p>", ControlledHangMitigatingCancellationToken);
|
||||
}, ControlledHangMitigatingCancellationToken);
|
||||
|
||||
// Assert
|
||||
await TestServices.Debugger.VerifyBreakpointAsync(RazorProjectConstants.CounterRazorFile, line: 1, character: 7, ControlledHangMitigatingCancellationToken);
|
||||
Assert.True(await TestServices.Debugger.SetBreakpointAsync(RazorProjectConstants.CounterRazorFile, line: 1, character: 1, ControlledHangMitigatingCancellationToken));
|
||||
|
||||
Assert.True(await TestServices.Debugger.VerifyBreakpointAsync(RazorProjectConstants.CounterRazorFile, line: 1, character: 7, ControlledHangMitigatingCancellationToken));
|
||||
}
|
||||
|
||||
[IdeFact]
|
||||
|
@ -34,15 +36,17 @@ public class BreakpointSpanTests(ITestOutputHelper testOutputHelper) : AbstractR
|
|||
|
||||
// Wait for classifications to indicate Razor LSP is up and running
|
||||
await TestServices.Editor.WaitForComponentClassificationAsync(ControlledHangMitigatingCancellationToken);
|
||||
await TestServices.Editor.SetTextAsync(@"<p>@{
|
||||
var abc = 123;
|
||||
}</p>", ControlledHangMitigatingCancellationToken);
|
||||
|
||||
// Act
|
||||
var result = await TestServices.Debugger.SetBreakpointAsync(RazorProjectConstants.CounterRazorFile, line: 1, character: 1, ControlledHangMitigatingCancellationToken);
|
||||
await TestServices.RazorProjectSystem.WaitForCSharpVirtualDocumentUpdateAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.CounterRazorFile, async () =>
|
||||
{
|
||||
await TestServices.Editor.SetTextAsync("""
|
||||
<p>@{
|
||||
var abc = 123;
|
||||
}</p>
|
||||
""", ControlledHangMitigatingCancellationToken);
|
||||
}, ControlledHangMitigatingCancellationToken);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
Assert.False(await TestServices.Debugger.SetBreakpointAsync(RazorProjectConstants.CounterRazorFile, line: 1, character: 1, ControlledHangMitigatingCancellationToken));
|
||||
}
|
||||
|
||||
[IdeFact]
|
||||
|
@ -53,14 +57,18 @@ public class BreakpointSpanTests(ITestOutputHelper testOutputHelper) : AbstractR
|
|||
|
||||
// Wait for classifications to indicate Razor LSP is up and running
|
||||
await TestServices.Editor.WaitForComponentClassificationAsync(ControlledHangMitigatingCancellationToken);
|
||||
await TestServices.Editor.SetTextAsync(@"<p>@{
|
||||
var abc = 123;
|
||||
}</p>", ControlledHangMitigatingCancellationToken);
|
||||
|
||||
// Act
|
||||
await TestServices.Debugger.SetBreakpointAsync(RazorProjectConstants.CounterRazorFile, line: 2, character: 1, ControlledHangMitigatingCancellationToken);
|
||||
await TestServices.RazorProjectSystem.WaitForCSharpVirtualDocumentUpdateAsync(RazorProjectConstants.BlazorProjectName, RazorProjectConstants.CounterRazorFile, async () =>
|
||||
{
|
||||
await TestServices.Editor.SetTextAsync("""
|
||||
<p>@{
|
||||
var abc = 123;
|
||||
}</p>
|
||||
""", ControlledHangMitigatingCancellationToken);
|
||||
}, ControlledHangMitigatingCancellationToken);
|
||||
|
||||
// Assert
|
||||
await TestServices.Debugger.VerifyBreakpointAsync(RazorProjectConstants.CounterRazorFile, line: 2, character: 4, ControlledHangMitigatingCancellationToken);
|
||||
Assert.True(await TestServices.Debugger.SetBreakpointAsync(RazorProjectConstants.CounterRazorFile, line: 2, character: 1, ControlledHangMitigatingCancellationToken));
|
||||
|
||||
Assert.True(await TestServices.Debugger.VerifyBreakpointAsync(RazorProjectConstants.CounterRazorFile, line: 2, character: 5, ControlledHangMitigatingCancellationToken));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ internal partial class DebuggerInProcess
|
|||
|
||||
foreach (EnvDTE.Breakpoint breakpoint in debugger.Breakpoints)
|
||||
{
|
||||
if (breakpoint.File == fileName &&
|
||||
if (breakpoint.File.EndsWith(fileName) &&
|
||||
breakpoint.FileLine == line &&
|
||||
breakpoint.FileColumn == character)
|
||||
{
|
||||
|
|
|
@ -127,4 +127,43 @@ internal partial class RazorProjectSystemInProcess
|
|||
|
||||
}, TimeSpan.FromMilliseconds(100), cancellationToken);
|
||||
}
|
||||
|
||||
public async Task WaitForCSharpVirtualDocumentUpdateAsync(string projectName, string relativeFilePath, Func<Task> updater, CancellationToken cancellationToken)
|
||||
{
|
||||
var filePath = await TestServices.SolutionExplorer.GetAbsolutePathForProjectRelativeFilePathAsync(projectName, relativeFilePath, cancellationToken);
|
||||
|
||||
var documentManager = await TestServices.Shell.GetComponentModelServiceAsync<LSPDocumentManager>(cancellationToken);
|
||||
|
||||
var uri = new Uri(filePath, UriKind.Absolute);
|
||||
|
||||
long? desiredVersion = null;
|
||||
|
||||
await Helper.RetryAsync(async ct =>
|
||||
{
|
||||
if (documentManager.TryGetDocument(uri, out var snapshot))
|
||||
{
|
||||
if (snapshot.TryGetVirtualDocument<CSharpVirtualDocumentSnapshot>(out var virtualDocument))
|
||||
{
|
||||
if (!virtualDocument.ProjectKey.IsUnknown &&
|
||||
virtualDocument.Snapshot.Length > 0)
|
||||
{
|
||||
if (desiredVersion is null)
|
||||
{
|
||||
desiredVersion = virtualDocument.HostDocumentSyncVersion + 1;
|
||||
await updater();
|
||||
}
|
||||
else if (virtualDocument.HostDocumentSyncVersion == desiredVersion)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}, TimeSpan.FromMilliseconds(100), cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче