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 }));
        }
Esempio n. 3
0
        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));
        }