public async Task Handle_ExtractCodeBlockWithUsing() { // Arrange var documentPath = "c:/Test.razor"; var documentUri = new Uri(documentPath); var contents = $"@page \"/test\"\n@using System.Diagnostics{Environment.NewLine}@code {{ private var x = 1; }}"; var codeDocument = CreateCodeDocument(contents); var resolver = new ExtractToCodeBehindCodeActionResolver(Dispatcher, CreateDocumentResolver(documentPath, codeDocument), FilePathNormalizer); var actionParams = new ExtractToCodeBehindCodeActionParams { Uri = documentUri, RemoveStart = contents.IndexOf("@code", StringComparison.Ordinal), ExtractStart = contents.IndexOf("{", StringComparison.Ordinal), ExtractEnd = contents.IndexOf("}", StringComparison.Ordinal), RemoveEnd = contents.IndexOf("}", StringComparison.Ordinal), }; var data = JObject.FromObject(actionParams); // Act var workspaceEdit = await resolver.ResolveAsync(data, default); // Assert Assert.NotNull(workspaceEdit); Assert.NotNull(workspaceEdit.DocumentChanges); Assert.Equal(3, workspaceEdit.DocumentChanges !.Count()); var documentChanges = workspaceEdit.DocumentChanges !.ToArray(); var createFileChange = documentChanges[0]; Assert.True(createFileChange.IsCreateFile); var editCodeDocumentChange = documentChanges[1]; Assert.NotNull(editCodeDocumentChange.TextDocumentEdit); var editCodeDocumentEdit = editCodeDocumentChange.TextDocumentEdit !.Edits.First(); Assert.True(editCodeDocumentEdit.Range.Start.TryGetAbsoluteIndex(codeDocument.GetSourceText(), _logger, out var removeStart)); Assert.Equal(actionParams.RemoveStart, removeStart); Assert.True(editCodeDocumentEdit.Range.End.TryGetAbsoluteIndex(codeDocument.GetSourceText(), _logger, out var removeEnd)); Assert.Equal(actionParams.RemoveEnd, removeEnd); var editCodeBehindChange = documentChanges[2]; Assert.NotNull(editCodeBehindChange.TextDocumentEdit); var editCodeBehindEdit = editCodeBehindChange.TextDocumentEdit !.Edits.First(); Assert.Contains("using System.Diagnostics", editCodeBehindEdit.NewText, StringComparison.Ordinal); Assert.Contains("public partial class Test", editCodeBehindEdit.NewText, StringComparison.Ordinal); Assert.Contains("private var x = 1", editCodeBehindEdit.NewText, StringComparison.Ordinal); Assert.Contains("namespace test.Pages", editCodeBehindEdit.NewText, StringComparison.Ordinal); }
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 })); }
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)); }