public async Task Handle_ResolveEditBasedCodeActionCommand() { // Arrange var codeActionEndpoint = new CodeActionResolutionEndpoint( Array.Empty <RazorCodeActionResolver>(), new CSharpCodeActionResolver[] { new MockCSharpCodeActionResolver("Test"), }, LoggerFactory); var requestParams = new RazorCodeActionResolutionParams() { Action = LanguageServerConstants.CodeActions.EditBasedCodeActionCommand, Language = LanguageServerConstants.CodeActions.Languages.Razor, Data = JToken.FromObject(new WorkspaceEdit()) }; var request = new CodeAction() { Title = "Valid request", Data = JToken.FromObject(requestParams) }; // Act var razorCodeAction = await codeActionEndpoint.Handle(request, default); // Assert Assert.NotNull(razorCodeAction.Edit); }
internal static bool TryCreateAddUsingResolutionParams(string fullyQualifiedName, DocumentUri uri, out string @namespace, out RazorCodeActionResolutionParams resolutionParams) { @namespace = GetNamespaceFromFQN(fullyQualifiedName); if (string.IsNullOrEmpty(@namespace)) { @namespace = null; resolutionParams = null; return(false); } var actionParams = new AddUsingsCodeActionParams { Uri = uri, Namespace = @namespace }; resolutionParams = new RazorCodeActionResolutionParams { Action = LanguageServerConstants.CodeActions.AddUsing, Language = LanguageServerConstants.CodeActions.Languages.Razor, Data = actionParams, }; return(true); }
public async Task Handle_Valid_CSharpCodeAction_WithMultipleLanguageResolvers() { // Arrange var codeActionEndpoint = new CodeActionResolutionEndpoint( new RazorCodeActionResolver[] { new MockRazorCodeActionResolver("TestRazor"), }, new CSharpCodeActionResolver[] { new MockCSharpCodeActionResolver("TestCSharp"), }, LoggerFactory); var requestParams = new RazorCodeActionResolutionParams() { Action = "TestCSharp", Language = LanguageServerConstants.CodeActions.Languages.CSharp, Data = JObject.FromObject(new CSharpCodeActionParams()) }; var request = new CodeAction() { Title = "Valid request", Data = JToken.FromObject(requestParams) }; // Act var razorCodeAction = await codeActionEndpoint.Handle(request, default); // Assert Assert.NotNull(razorCodeAction.Edit); }
internal static CodeAction CreateAddUsingCodeAction(string fullyQualifiedName, DocumentUri uri) { var @namespace = GetNamespaceFromFQN(fullyQualifiedName); if (string.IsNullOrEmpty(@namespace)) { return(null); } var actionParams = new AddUsingsCodeActionParams { Uri = uri, Namespace = @namespace }; var resolutionParams = new RazorCodeActionResolutionParams { Action = LanguageServerConstants.CodeActions.AddUsing, Language = LanguageServerConstants.CodeActions.Languages.Razor, Data = actionParams, }; return(new CodeAction() { Title = $"@using {@namespace}", Data = JToken.FromObject(resolutionParams) }); }
private void AddCreateComponentFromTag(RazorCodeActionContext context, MarkupStartTagSyntax startTag, List <RazorCodeAction> container) { var path = context.Request.TextDocument.Uri.GetAbsoluteOrUNCPath(); path = _filePathNormalizer.Normalize(path); var newComponentPath = Path.Combine(Path.GetDirectoryName(path), $"{startTag.Name.Content}.razor"); if (File.Exists(newComponentPath)) { return; } var actionParams = new CreateComponentCodeActionParams { Uri = context.Request.TextDocument.Uri, Path = newComponentPath, }; var resolutionParams = new RazorCodeActionResolutionParams { Action = LanguageServerConstants.CodeActions.CreateComponentFromTag, Data = actionParams, }; container.Add(new RazorCodeAction() { Title = CreateComponentFromTagTitle, Data = resolutionParams }); }
// Internal for testing internal async Task <CodeAction> ResolveCSharpCodeActionAsync( CodeAction codeAction, RazorCodeActionResolutionParams resolutionParams, CancellationToken cancellationToken) { if (!(resolutionParams.Data is JObject csharpParamsObj)) { Debug.Fail($"Invalid CSharp CodeAction Received."); return(codeAction); } var csharpParams = csharpParamsObj.ToObject <CSharpCodeActionParams>(); codeAction.Data = csharpParams.Data as JToken; if (!_csharpCodeActionResolvers.TryGetValue(resolutionParams.Action, out var resolver)) { Debug.Fail($"No resolver registered for {GetCodeActionId(resolutionParams)}."); return(codeAction); } var resolvedCodeAction = await resolver.ResolveAsync(csharpParams, codeAction, cancellationToken); return(resolvedCodeAction); }
internal async Task <CodeAction> ResolveCSharpCodeActionAsync( CodeAction codeAction, RazorCodeActionResolutionParams resolutionParams, CancellationToken cancellationToken) { if (resolutionParams.Data is not JObject csharpParamsObj) { _logger.LogError("Invalid CodeAction Received."); Debug.Fail($"Invalid CSharp CodeAction Received."); return(codeAction); } var csharpParams = csharpParamsObj.ToObject <CSharpCodeActionParams>(); codeAction = codeAction with { Data = csharpParams.Data as JToken }; if (!_csharpCodeActionResolvers.TryGetValue(resolutionParams.Action, out var resolver)) { var codeActionId = GetCodeActionId(resolutionParams); _logger.LogWarning("No resolver registered for {codeActionId}", codeActionId); Debug.Fail($"No resolver registered for {codeActionId}."); return(codeAction); } var resolvedCodeAction = await resolver.ResolveAsync(csharpParams, codeAction, cancellationToken); return(resolvedCodeAction); }
private void AddCreateComponentFromTag(RazorCodeActionContext context, MarkupStartTagSyntax startTag, List <CommandOrCodeAction> container) { var path = context.Request.TextDocument.Uri.GetAbsoluteOrUNCPath(); path = _filePathNormalizer.Normalize(path); var newComponentPath = Path.Combine(Path.GetDirectoryName(path), $"{startTag.Name.Content}.razor"); if (File.Exists(newComponentPath)) { return; } var actionParams = new CreateComponentCodeActionParams { Uri = context.Request.TextDocument.Uri, Path = newComponentPath, }; var data = JObject.FromObject(actionParams); var resolutionParams = new RazorCodeActionResolutionParams { Action = LanguageServerConstants.CodeActions.CreateComponentFromTag, Data = data, }; var serializedParams = JToken.FromObject(resolutionParams); var arguments = new JArray(serializedParams); container.Add(new CommandOrCodeAction(new Command { Title = "Create component from tag", Name = LanguageServerConstants.RazorCodeActionRunnerCommand, Arguments = arguments, })); }
public async Task Handle_Valid_RazorCodeAction_WithoutResolver() { // Arrange var codeActionEndpoint = new CodeActionResolutionEndpoint( Array.Empty <RazorCodeActionResolver>(), Array.Empty <CSharpCodeActionResolver>(), LoggerFactory); var requestParams = new RazorCodeActionResolutionParams() { Action = "Test", Language = LanguageServerConstants.CodeActions.Languages.Razor, Data = new AddUsingsCodeActionParams() }; var request = new CodeAction() { Title = "Valid request", Data = JToken.FromObject(requestParams) }; #if DEBUG // Act & Assert (Throws due to debug assert on no Razor.Test resolver) await Assert.ThrowsAnyAsync <Exception>(async() => await codeActionEndpoint.Handle(request, default)); #else // Act var resolvedCodeAction = await codeActionEndpoint.Handle(request, default); // Assert Assert.Null(resolvedCodeAction.Edit); #endif }
public async Task Handle_Valid_RazorCodeAction_WithResolver() { // Arrange var codeActionEndpoint = new CodeActionResolutionEndpoint( new RazorCodeActionResolver[] { new MockRazorCodeActionResolver("Test"), }, Array.Empty <CSharpCodeActionResolver>(), LoggerFactory); var requestParams = new RazorCodeActionResolutionParams() { Action = "Test", Language = LanguageServerConstants.CodeActions.Languages.Razor, Data = new AddUsingsCodeActionParams() }; var request = new CodeAction() { Title = "Valid request", Data = JToken.FromObject(requestParams) }; // Act var razorCodeAction = await codeActionEndpoint.Handle(request, default); // Assert Assert.NotNull(razorCodeAction.Edit); }
public async Task Handle_Valid_CSharpCodeAction_WithRazorResolver_ResolvesNull() { // Arrange var codeActionEndpoint = new CodeActionResolutionEndpoint( new RazorCodeActionResolver[] { new MockRazorCodeActionResolver("Test"), }, Array.Empty <CSharpCodeActionResolver>(), LoggerFactory); var requestParams = new RazorCodeActionResolutionParams() { Action = "Test", Language = LanguageServerConstants.CodeActions.Languages.CSharp, Data = JObject.FromObject(new CSharpCodeActionParams()) }; var request = new CodeAction() { Title = "Valid request", Data = JToken.FromObject(requestParams) }; #if DEBUG // Act & Assert (Throws due to debug asserts) await Assert.ThrowsAnyAsync <Exception>(async() => await codeActionEndpoint.Handle(request, default)); #else // Act var resolvedCodeAction = await codeActionEndpoint.Handle(request, default); // Assert Assert.Null(resolvedCodeAction.Edit); #endif }
public async Task ResolveCSharpCodeAction_ResolveMultipleLanguageProviders() { // Arrange var codeActionEndpoint = new CodeActionResolutionEndpoint( new RazorCodeActionResolver[] { new MockRazorNullCodeActionResolver("A"), new MockRazorCodeActionResolver("B"), }, new CSharpCodeActionResolver[] { new MockCSharpNullCodeActionResolver("C"), new MockCSharpCodeActionResolver("D"), }, LoggerFactory); var codeAction = new CodeAction(); var request = new RazorCodeActionResolutionParams() { Action = "D", Language = LanguageServerConstants.CodeActions.Languages.CSharp, Data = JObject.FromObject(new CSharpCodeActionParams()) }; // Act var resolvedCodeAction = await codeActionEndpoint.ResolveCSharpCodeActionAsync(codeAction, request, default); // Assert Assert.NotNull(resolvedCodeAction.Edit); }
// Internal for testing internal async Task <WorkspaceEdit> GetWorkspaceEditAsync(RazorCodeActionResolutionParams resolutionParams, CancellationToken cancellationToken) { _logger.LogInformation($"Resolving workspace edit for action `{resolutionParams.Action}`."); if (!_resolvers.TryGetValue(resolutionParams.Action, out var resolver)) { Debug.Fail($"No resolver registered for {resolutionParams.Action}."); return(default);
// Internal for testing internal async Task <CodeAction> ResolveRazorCodeActionAsync( CodeAction codeAction, RazorCodeActionResolutionParams resolutionParams, CancellationToken cancellationToken) { if (!_razorCodeActionResolvers.TryGetValue(resolutionParams.Action, out var resolver)) { Debug.Fail($"No resolver registered for {GetCodeActionId(resolutionParams)}."); return(codeAction); } codeAction.Edit = await resolver.ResolveAsync(resolutionParams.Data as JObject, cancellationToken).ConfigureAwait(false); return(codeAction); }
private void AddComponentAccessFromTag(RazorCodeActionContext context, MarkupStartTagSyntax startTag, List <CommandOrCodeAction> container) { var matching = FindMatchingTagHelpers(context, startTag); // For all the matches, add options for add @using and fully qualify foreach (var tagHelperPair in matching.Values) { if (tagHelperPair.FullyQualified is null) { continue; } var fullyQualifiedComponentName = tagHelperPair.Short.Name; // We assume .Name is the fully qualified component name DefaultRazorTagHelperBinderPhase.ComponentDirectiveVisitor.TrySplitNamespaceAndType(fullyQualifiedComponentName, out var namespaceSpan, out var _); var namespaceName = tagHelperPair.Short.Name.Substring(namespaceSpan.Start, namespaceSpan.Length); var actionParams = new AddUsingsCodeActionParams { Uri = context.Request.TextDocument.Uri, Namespace = namespaceName, }; var data = JObject.FromObject(actionParams); var resolutionParams = new RazorCodeActionResolutionParams { Action = LanguageServerConstants.CodeActions.AddUsing, Data = data, }; var serializedParams = JToken.FromObject(resolutionParams); var arguments = new JArray(serializedParams); // Insert @using container.Add(new CommandOrCodeAction(new Command { Title = $"@using {namespaceName}", Name = LanguageServerConstants.RazorCodeActionRunnerCommand, Arguments = arguments, })); // Fully qualify container.Add(new CommandOrCodeAction(new CodeAction { Title = $"{tagHelperPair.Short.Name}", Edit = CreateRenameTagEdit(context, startTag, tagHelperPair.Short.Name), })); } }
public async Task Handle_Resolve() { // Arrange var codeActionEndpoint = new CodeActionResolutionEndpoint(new RazorCodeActionResolver[] { new MockCodeActionResolver("Test"), }, LoggerFactory); var request = new RazorCodeActionResolutionParams() { Action = "Test", Data = null }; // Act var workspaceEdit = await codeActionEndpoint.Handle(request, default); // Assert Assert.NotNull(workspaceEdit); }
public async Task Handle_ResolveMultipleProviders_SecondMatches() { // Arrange var codeActionEndpoint = new CodeActionResolutionEndpoint(new RazorCodeActionResolver[] { new NullMockCodeActionResolver("A"), new MockCodeActionResolver("B"), }, LoggerFactory); var request = new RazorCodeActionResolutionParams() { Action = "B", Data = null }; // Act var workspaceEdit = await codeActionEndpoint.Handle(request, default); // Assert Assert.NotNull(workspaceEdit); }
public async Task GetWorkspaceEditAsync_ResolveMultipleProviders_FirstMatches() { // Arrange var codeActionEndpoint = new CodeActionResolutionEndpoint(new RazorCodeActionResolver[] { new MockCodeActionResolver("A"), new NullMockCodeActionResolver("B"), }, LoggerFactory); var request = new RazorCodeActionResolutionParams() { Action = "A", Data = new AddUsingsCodeActionParams() }; // Act var workspaceEdit = await codeActionEndpoint.GetWorkspaceEditAsync(request, default); // Assert Assert.NotNull(workspaceEdit); }
internal async Task <CodeAction> ResolveRazorCodeActionAsync( CodeAction codeAction, RazorCodeActionResolutionParams resolutionParams, CancellationToken cancellationToken) { if (!_razorCodeActionResolvers.TryGetValue(resolutionParams.Action, out var resolver)) { var codeActionId = GetCodeActionId(resolutionParams); _logger.LogWarning("No resolver registered for {codeActionId}", codeActionId); Debug.Fail($"No resolver registered for {codeActionId}."); return(codeAction); } var edit = await resolver.ResolveAsync(resolutionParams.Data as JObject, cancellationToken).ConfigureAwait(false); codeAction = codeAction with { Edit = edit }; return(codeAction); }
private void AddCreateComponentFromTag(RazorCodeActionContext context, MarkupStartTagSyntax startTag, List <RazorCodeAction> container) { if (context is null) { return; } if (!context.SupportsFileCreation) { return; } var path = context.Request.TextDocument.Uri.GetAbsoluteOrUNCPath(); path = _filePathNormalizer.Normalize(path); var newComponentPath = Path.Combine(Path.GetDirectoryName(path), $"{startTag.Name.Content}.razor"); if (File.Exists(newComponentPath)) { return; } var actionParams = new CreateComponentCodeActionParams { Uri = context.Request.TextDocument.Uri, Path = newComponentPath, }; var resolutionParams = new RazorCodeActionResolutionParams { Action = LanguageServerConstants.CodeActions.CreateComponentFromTag, Language = LanguageServerConstants.CodeActions.Languages.Razor, Data = actionParams, }; container.Add(new RazorCodeAction() { Title = RazorLS.Resources.Create_Component_FromTag_Title, Data = JToken.FromObject(resolutionParams) }); }
public async Task <RazorCodeActionResolutionResponse> Handle(RazorCodeActionResolutionParams request, CancellationToken cancellationToken) { if (request is null) { throw new ArgumentNullException(nameof(request)); } _logger.LogDebug($"Resolving action {request.Action} with data {request.Data}."); if (!_resolvers.TryGetValue(request.Action, out var resolver)) { Debug.Fail($"No resolver registered for {request.Action}."); return(new RazorCodeActionResolutionResponse()); } var edit = await resolver.ResolveAsync(request.Data, cancellationToken).ConfigureAwait(false); return(new RazorCodeActionResolutionResponse() { Edit = edit }); }
public async Task Handle_Valid_RazorCodeAction_Resolve() { // Arrange var codeActionEndpoint = new CodeActionResolutionEndpoint(new RazorCodeActionResolver[] { new MockCodeActionResolver("Test"), }, LoggerFactory); var requestParams = new RazorCodeActionResolutionParams() { Action = "Test", Data = new AddUsingsCodeActionParams() }; var request = new RazorCodeAction() { Title = "Valid request", Data = JObject.FromObject(requestParams) }; // Act var razorCodeAction = await codeActionEndpoint.Handle(request, default); // Assert Assert.NotNull(razorCodeAction.Edit); }
public async Task ResolveRazorCodeAction_ResolveMultipleRazorProviders_SecondMatches() { // Arrange var codeActionEndpoint = new CodeActionResolutionEndpoint( new RazorCodeActionResolver[] { new MockRazorNullCodeActionResolver("A"), new MockRazorCodeActionResolver("B"), }, Array.Empty <CSharpCodeActionResolver>(), LoggerFactory); var codeAction = new CodeAction(); var request = new RazorCodeActionResolutionParams() { Action = "B", Language = LanguageServerConstants.CodeActions.Languages.Razor, Data = new AddUsingsCodeActionParams() }; // Act var resolvedCodeAction = await codeActionEndpoint.ResolveRazorCodeActionAsync(codeAction, request, default); // Assert Assert.NotNull(resolvedCodeAction.Edit); }
private static string GetCodeActionId(RazorCodeActionResolutionParams resolutionParams) => $"`{resolutionParams.Language}.{resolutionParams.Action}`";
override public Task <CommandOrCodeActionContainer> ProvideAsync(RazorCodeActionContext context, CancellationToken cancellationToken) { if (context is null) { return(EmptyResult); } if (!FileKinds.IsComponent(context.CodeDocument.GetFileKind())) { return(EmptyResult); } var change = new SourceChange(context.Location.AbsoluteIndex, length: 0, newText: string.Empty); var syntaxTree = context.CodeDocument.GetSyntaxTree(); if (syntaxTree?.Root is null) { return(EmptyResult); } var owner = syntaxTree.Root.LocateOwner(change); var node = owner.Ancestors().FirstOrDefault(n => n.Kind == SyntaxKind.RazorDirective); if (node == null || !(node is RazorDirectiveSyntax directiveNode)) { return(EmptyResult); } // Make sure we've found a @code or @functions if (directiveNode.DirectiveDescriptor != ComponentCodeDirective.Directive && directiveNode.DirectiveDescriptor != FunctionsDirective.Directive) { return(EmptyResult); } // No code action if malformed if (directiveNode.GetDiagnostics().Any(d => d.Severity == RazorDiagnosticSeverity.Error)) { return(EmptyResult); } var cSharpCodeBlockNode = directiveNode.Body.DescendantNodes().FirstOrDefault(n => n is CSharpCodeBlockSyntax); if (cSharpCodeBlockNode is null) { return(EmptyResult); } if (HasUnsupportedChildren(cSharpCodeBlockNode)) { return(EmptyResult); } // Do not provide code action if the cursor is inside the code block if (context.Location.AbsoluteIndex > cSharpCodeBlockNode.SpanStart) { return(EmptyResult); } var actionParams = new ExtractToCodeBehindCodeActionParams() { Uri = context.Request.TextDocument.Uri, ExtractStart = cSharpCodeBlockNode.Span.Start, ExtractEnd = cSharpCodeBlockNode.Span.End, RemoveStart = directiveNode.Span.Start, RemoveEnd = directiveNode.Span.End }; var data = JObject.FromObject(actionParams); var resolutionParams = new RazorCodeActionResolutionParams() { Action = LanguageServerConstants.CodeActions.ExtractToCodeBehindAction, Data = data, }; var serializedParams = JToken.FromObject(resolutionParams); var arguments = new JArray(serializedParams); var container = new List <CommandOrCodeAction> { new Command() { Title = "Extract block to code behind", Name = LanguageServerConstants.RazorCodeActionRunnerCommand, Arguments = arguments, } }; return(Task.FromResult((CommandOrCodeActionContainer)container)); }
public override Task <RazorCodeAction[]> ProvideAsync(RazorCodeActionContext context, CancellationToken cancellationToken) { if (context is null) { return(EmptyResult); } if (!FileKinds.IsComponent(context.CodeDocument.GetFileKind())) { return(EmptyResult); } var change = new SourceChange(context.Location.AbsoluteIndex, length: 0, newText: string.Empty); var syntaxTree = context.CodeDocument.GetSyntaxTree(); if (syntaxTree?.Root is null) { return(EmptyResult); } var owner = syntaxTree.Root.LocateOwner(change); if (owner == null) { Debug.Fail("Owner should never be null."); return(EmptyResult); } var node = owner.Ancestors().FirstOrDefault(n => n.Kind == SyntaxKind.RazorDirective); if (node == null || !(node is RazorDirectiveSyntax directiveNode)) { return(EmptyResult); } // Make sure we've found a @code or @functions if (directiveNode.DirectiveDescriptor != ComponentCodeDirective.Directive && directiveNode.DirectiveDescriptor != FunctionsDirective.Directive) { return(EmptyResult); } // No code action if malformed if (directiveNode.GetDiagnostics().Any(d => d.Severity == RazorDiagnosticSeverity.Error)) { return(EmptyResult); } var csharpCodeBlockNode = directiveNode.Body.DescendantNodes().FirstOrDefault(n => n is CSharpCodeBlockSyntax); if (csharpCodeBlockNode is null) { return(EmptyResult); } if (HasUnsupportedChildren(csharpCodeBlockNode)) { return(EmptyResult); } // Do not provide code action if the cursor is inside the code block if (context.Location.AbsoluteIndex > csharpCodeBlockNode.SpanStart) { return(EmptyResult); } var actionParams = new ExtractToCodeBehindCodeActionParams() { Uri = context.Request.TextDocument.Uri, ExtractStart = csharpCodeBlockNode.Span.Start, ExtractEnd = csharpCodeBlockNode.Span.End, RemoveStart = directiveNode.Span.Start, RemoveEnd = directiveNode.Span.End }; var resolutionParams = new RazorCodeActionResolutionParams() { Action = LanguageServerConstants.CodeActions.ExtractToCodeBehindAction, Data = actionParams, }; var codeAction = new RazorCodeAction() { Title = Title, Data = resolutionParams }; return(Task.FromResult(new[] { codeAction })); }