public async Task Handle_ExistingComponent_SupportsFileCreationFalse_ReturnsResults()
        {
            // Arrange
            var documentPath = "c:/Test.razor";
            var contents     = "<Component></Component>";
            var request      = new CodeActionParams()
            {
                TextDocument = new TextDocumentIdentifier(new Uri(documentPath)),
                Range        = new Range(new Position(0, 0), new Position(0, 0)),
            };

            var location = new SourceLocation(1, -1, -1);
            var context  = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(contents.IndexOf("Component", StringComparison.Ordinal), 9), supportsFileCreation: false);

            var provider = new ComponentAccessibilityCodeActionProvider(new DefaultTagHelperFactsService(), FilePathNormalizer);

            // Act
            var commandOrCodeActionContainer = await provider.ProvideAsync(context, default);

            // Assert
            Assert.Collection(commandOrCodeActionContainer,
                              e =>
            {
                Assert.Equal("@using Fully.Qualified", e.Title);
                Assert.NotNull(e.Data);
                Assert.Null(e.Edit);
            },
                              e =>
            {
                Assert.Equal("Fully.Qualified.Component", e.Title);
                Assert.NotNull(e.Edit);
                Assert.NotNull(e.Edit.DocumentChanges);
                Assert.Null(e.Data);
            });
        }
        private static RazorCodeActionContext CreateRazorCodeActionContext(CodeActionParams request, SourceLocation location, string filePath, string text, SourceSpan componentSourceSpan, bool supportsFileCreation = true)
        {
            var shortComponent = TagHelperDescriptorBuilder.Create(ComponentMetadata.Component.TagHelperKind, "Fully.Qualified.Component", "TestAssembly");

            shortComponent.TagMatchingRule(rule => rule.TagName = "Component");
            var fullyQualifiedComponent = TagHelperDescriptorBuilder.Create(ComponentMetadata.Component.TagHelperKind, "Fully.Qualified.Component", "TestAssembly");

            fullyQualifiedComponent.TagMatchingRule(rule => rule.TagName = "Fully.Qualified.Component");

            var tagHelpers = new[] { shortComponent.Build(), fullyQualifiedComponent.Build() };

            var sourceDocument = TestRazorSourceDocument.Create(text, filePath: filePath, relativePath: filePath);
            var projectEngine  = RazorProjectEngine.Create(builder => builder.AddTagHelpers(tagHelpers));
            var codeDocument   = projectEngine.ProcessDesignTime(sourceDocument, FileKinds.Component, Array.Empty <RazorSourceDocument>(), tagHelpers);

            var cSharpDocument               = codeDocument.GetCSharpDocument();
            var diagnosticDescriptor         = new RazorDiagnosticDescriptor("RZ10012", () => "", RazorDiagnosticSeverity.Error);
            var diagnostic                   = RazorDiagnostic.Create(diagnosticDescriptor, componentSourceSpan);
            var cSharpDocumentWithDiagnostic = RazorCSharpDocument.Create(cSharpDocument.GeneratedCode, cSharpDocument.Options, new[] { diagnostic });

            codeDocument.SetCSharpDocument(cSharpDocumentWithDiagnostic);

            var documentSnapshot = Mock.Of <DocumentSnapshot>(document =>
                                                              document.GetGeneratedOutputAsync() == Task.FromResult(codeDocument) &&
                                                              document.GetTextAsync() == Task.FromResult(codeDocument.GetSourceText()) &&
                                                              document.Project.TagHelpers == tagHelpers, MockBehavior.Strict);

            var sourceText = SourceText.From(text);

            var context = new RazorCodeActionContext(request, documentSnapshot, codeDocument, location, sourceText, supportsFileCreation, supportsCodeActionResolve: true);

            return(context);
        }
コード例 #3
0
        public async Task ProvideAsync_FunctionsBlock_SingleLine_ValidCodeActions_ReturnsProvidedCodeAction()
        {
            // Arrange
            var documentPath = "c:/Test.razor";
            var contents     = "@functions { Path; }";
            var request      = new CodeActionParams()
            {
                TextDocument = new TextDocumentIdentifier(new Uri(documentPath)),
                Range        = new Range(),
                Context      = new CodeActionContext()
            };

            var location = new SourceLocation(13, -1, -1);
            var context  = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(13, 4));

            context.CodeDocument.SetFileKind(FileKinds.Legacy);

            var provider = new DefaultCSharpCodeActionProvider();

            // Act
            var providedCodeActions = await provider.ProvideAsync(context, _supportedCodeActions, default);

            // Assert
            Assert.Equal(_supportedCodeActions.Length, providedCodeActions.Count);
            var providedNames = providedCodeActions.Select(action => action.Name);
            var expectedNames = _supportedCodeActions.Select(action => action.Name);

            Assert.Equal(expectedNames, providedNames);
        }
        public async Task Handle_NewComponent_SupportsFileCreationTrue_ReturnsResult()
        {
            // Arrange
            var documentPath = "c:/Test.razor";
            var contents     = "<NewComponent></NewComponent>";
            var request      = new CodeActionParams()
            {
                TextDocument = new TextDocumentIdentifier(new Uri(documentPath)),
                Range        = new Range(new Position(0, 0), new Position(0, 0)),
            };

            var location = new SourceLocation(1, -1, -1);
            var context  = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(contents.IndexOf("Component", StringComparison.Ordinal), 9), supportsFileCreation: true);

            var provider = new ComponentAccessibilityCodeActionProvider(new DefaultTagHelperFactsService(), FilePathNormalizer);

            // Act
            var commandOrCodeActionContainer = await provider.ProvideAsync(context, default);

            // Assert
            var command = Assert.Single(commandOrCodeActionContainer);

            Assert.Equal("Create component from tag", command.Title);
            Assert.NotNull(command.Data);
        }
コード例 #5
0
        public async Task ProvideAsync_InvalidCodeActions_ReturnsNoCodeActions()
        {
            // Arrange
            var documentPath = "c:/Test.razor";
            var contents     = "@code { Path; }";
            var request      = new CodeActionParams()
            {
                TextDocument = new TextDocumentIdentifier(new Uri(documentPath)),
                Range        = new Range(),
                Context      = new CodeActionContext()
            };

            var location = new SourceLocation(8, -1, -1);
            var context  = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(8, 4));

            context.CodeDocument.SetFileKind(FileKinds.Legacy);

            var provider = new DefaultCSharpCodeActionProvider();

            var codeActions = new RazorCodeAction[]
            {
                new RazorCodeAction()
                {
                    Title = "Do something not really supported in razor",
                    Name  = "Non-existant name"
                }
            };

            // Act
            var providedCodeActions = await provider.ProvideAsync(context, codeActions, default);

            // Assert
            Assert.Empty(providedCodeActions);
        }
コード例 #6
0
        public async Task ProvideAsync_SupportsCodeActionResolveFalse_ValidCodeActions_ReturnsEmpty()
        {
            // Arrange
            var documentPath = "c:/Test.razor";
            var contents     = "@code { Path; }";
            var request      = new CodeActionParams()
            {
                TextDocument = new TextDocumentIdentifier(new Uri(documentPath)),
                Range        = new Range(),
                Context      = new CodeActionContext()
            };

            var location = new SourceLocation(8, -1, -1);
            var context  = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(8, 4), supportsCodeActionResolve: false);

            context.CodeDocument.SetFileKind(FileKinds.Legacy);

            var provider = new DefaultCSharpCodeActionProvider();

            // Act
            var providedCodeActions = await provider.ProvideAsync(context, _supportedCodeActions, default);

            // Assert
            Assert.Empty(providedCodeActions);
        }
        public async Task ProvideCodeActionsAsync_CannotLookupVirtualDocument_ReturnsNullAsync()
        {
            // Arrange
            var testDocUri = new Uri("C:/path/to/file.razor");
            LSPDocumentSnapshot testDocument = new TestLSPDocumentSnapshot(testDocUri, 0);

            var documentManager = new Mock <TrackingLSPDocumentManager>(MockBehavior.Strict);

            documentManager.Setup(manager => manager.TryGetDocument(It.IsAny <Uri>(), out testDocument))
            .Returns(true);
            var target  = new DefaultRazorLanguageServerCustomMessageTarget(documentManager.Object);
            var request = new CodeActionParams()
            {
                TextDocument = new TextDocumentIdentifier()
                {
                    Uri = new Uri("C:/path/to/file.razor")
                }
            };

            // Act
            var result = await target.ProvideCodeActionsAsync(request, CancellationToken.None);

            // Assert
            Assert.Null(result);
        }
コード例 #8
0
        private static VSCodeAction GenerateVSCodeAction(
            CodeActionParams request,
            CodeAction codeAction,
            CodeActionKind codeActionKind,
            string currentTitle = "")
        {
            using var _ = ArrayBuilder <VSCodeAction> .GetInstance(out var nestedActions);

            if (!string.IsNullOrEmpty(currentTitle))
            {
                // Adding a delimiter for nested code actions, e.g. 'Suppress or Configure issues|Suppress IDEXXXX|in Source'
                currentTitle += '|';
            }

            currentTitle += codeAction.Title;

            // Nested code actions' unique identifiers consist of: parent code action unique identifier + '|' + title of code action
            foreach (var action in codeAction.NestedCodeActions)
            {
                nestedActions.Add(GenerateVSCodeAction(request, action, codeActionKind, currentTitle));
            }

            return(new VSCodeAction
            {
                Title = codeAction.Title,
                Kind = codeActionKind,
                Diagnostics = request.Context.Diagnostics,
                Children = nestedActions.ToArray(),
                Data = new CodeActionResolveData(currentTitle, request.Range, request.TextDocument)
            });
        }
コード例 #9
0
ファイル: CodeActionHelpers.cs プロジェクト: Mughisi/roslyn
        private static VSInternalCodeAction GenerateVSCodeAction(
            CodeActionParams request,
            SourceText documentText,
            IUnifiedSuggestedAction suggestedAction,
            LSP.CodeActionKind codeActionKind,
            UnifiedSuggestedActionSetPriority setPriority,
            LSP.Range?applicableRange,
            int currentSetNumber,
            ref int currentHighestSetNumber,
            string currentTitle = "")
        {
            if (!string.IsNullOrEmpty(currentTitle))
            {
                // Adding a delimiter for nested code actions, e.g. 'Suppress or Configure issues|Suppress IDEXXXX|in Source'
                currentTitle += '|';
            }

            var codeAction = suggestedAction.OriginalCodeAction;

            currentTitle += codeAction.Title;

            // Nested code actions' unique identifiers consist of: parent code action unique identifier + '|' + title of code action
            var nestedActions = GenerateNestedVSCodeActions(request, documentText, suggestedAction, codeActionKind, ref currentHighestSetNumber, currentTitle);

            return(new VSInternalCodeAction
            {
                Title = codeAction.Title,
                Kind = codeActionKind,
                Diagnostics = request.Context.Diagnostics,
                Children = nestedActions,
                Priority = UnifiedSuggestedActionSetPriorityToPriorityLevel(setPriority),
                Group = $"Roslyn{currentSetNumber}",
                ApplicableRange = applicableRange,
                Data = new CodeActionResolveData(currentTitle, codeAction.CustomTags, request.Range, request.TextDocument)
            });
        public override async Task <VSCodeAction[]> ProvideCodeActionsAsync(CodeActionParams codeActionParams, CancellationToken cancellationToken)
        {
            if (codeActionParams is null)
            {
                throw new ArgumentNullException(nameof(codeActionParams));
            }

            if (!_documentManager.TryGetDocument(codeActionParams.TextDocument.Uri, out var documentSnapshot))
            {
                return(null);
            }

            if (!documentSnapshot.TryGetVirtualDocument <CSharpVirtualDocumentSnapshot>(out var csharpDoc))
            {
                return(null);
            }

            codeActionParams.TextDocument.Uri = csharpDoc.Uri;

            var results = await _requestInvoker.ReinvokeRequestOnMultipleServersAsync <CodeActionParams, VSCodeAction[]>(
                Methods.TextDocumentCodeActionName,
                LanguageServerKind.CSharp.ToContentType(),
                SupportsCSharpCodeActions,
                codeActionParams,
                cancellationToken).ConfigureAwait(false);

            return(results.SelectMany(l => l).ToArray());
        }
コード例 #11
0
ファイル: CodeActionHelpers.cs プロジェクト: Mughisi/roslyn
            static VSInternalCodeAction[] GenerateNestedVSCodeActions(
                CodeActionParams request,
                SourceText documentText,
                IUnifiedSuggestedAction suggestedAction,
                CodeActionKind codeActionKind,
                ref int currentHighestSetNumber,
                string currentTitle)
            {
                if (suggestedAction is not UnifiedSuggestedActionWithNestedActions suggestedActionWithNestedActions)
                {
                    return(Array.Empty <VSInternalCodeAction>());
                }

                using var _ = ArrayBuilder <VSInternalCodeAction> .GetInstance(out var nestedActions);

                foreach (var nestedActionSet in suggestedActionWithNestedActions.NestedActionSets)
                {
                    // Nested code action sets should each have a unique set number that is not yet assigned to any set.
                    var nestedSetNumber = ++currentHighestSetNumber;
                    foreach (var nestedSuggestedAction in nestedActionSet.Actions)
                    {
                        nestedActions.Add(GenerateVSCodeAction(
                                              request, documentText, nestedSuggestedAction, codeActionKind, nestedActionSet.Priority,
                                              applicableRange: nestedActionSet.ApplicableToSpan.HasValue
                                ? ProtocolConversions.TextSpanToRange(nestedActionSet.ApplicableToSpan.Value, documentText) : null,
                                              nestedSetNumber, ref currentHighestSetNumber, currentTitle));
                    }
                }

                return(nestedActions.ToArray());
            }
コード例 #12
0
        public async Task Handle_MissingDiagnostics_ReturnsEmpty()
        {
            // Arrange
            var documentPath = "c:/Test.razor";
            var contents     = "";
            var request      = new CodeActionParams()
            {
                TextDocument = new TextDocumentIdentifier(new Uri(documentPath)),
                Range        = new Range(),
                Context      = new CodeActionContext()
                {
                    Diagnostics = null
                }
            };

            var location = new SourceLocation(0, -1, -1);
            var context  = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(0, 0));

            context.CodeDocument.SetFileKind(FileKinds.Legacy);

            var provider          = new TypeAccessibilityCodeActionProvider();
            var csharpCodeActions = new[] {
                new RazorCodeAction()
                {
                    Title = "System.Net.Dns"
                }
            };

            // Act
            var results = await provider.ProvideAsync(context, csharpCodeActions, default);

            // Assert
            Assert.Empty(results);
        }
コード例 #13
0
        public async Task Handle_InvalidCodeActions_ReturnsEmpty()
        {
            // Arrange
            var documentPath = "c:/Test.razor";
            var contents     = "";
            var request      = new CodeActionParams()
            {
                TextDocument = new TextDocumentIdentifier(new Uri(documentPath)),
                Range        = new Range(),
                Context      = new CodeActionContext()
                {
                    Diagnostics = new Container <Diagnostic>(
                        new Diagnostic()
                    {
                        Severity = DiagnosticSeverity.Error,
                        Code     = new DiagnosticCode("CS0246")
                    }
                        )
                }
            };

            var location = new SourceLocation(0, -1, -1);
            var context  = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(0, 0));

            context.CodeDocument.SetFileKind(FileKinds.Legacy);

            var provider          = new TypeAccessibilityCodeActionProvider();
            var csharpCodeActions = Array.Empty <RazorCodeAction>();

            // Act
            var results = await provider.ProvideAsync(context, csharpCodeActions, default);

            // Assert
            Assert.Empty(results);
        }
        public void NonStandardCharactersTest(string expected)
        {
            var model = new CodeActionParams()
            {
                Context = new CodeActionContext()
                {
                    Diagnostics = new[] { new Diagnostic()
                                          {
                                              Code     = new DiagnosticCode("abcd"),
                                              Message  = "message",
                                              Range    = new Range(new Position(1, 1), new Position(2, 2)),
                                              Severity = DiagnosticSeverity.Error,
                                              Source   = "csharp"
                                          } }
                },
                Range        = new Range(new Position(1, 1), new Position(2, 2)),
                TextDocument = new TextDocumentIdentifier()
                {
                    // ТаЉ - Chinese for tree
                    Uri = new Uri("file:///test/123/%E6%A0%91.cs")
                }
            };
            var result = Fixture.SerializeObject(model);

            result.Should().Be(expected);

            var deresult = new Serializer(ClientVersion.Lsp3).DeserializeObject <CodeActionParams>(expected);

            deresult.Should().BeEquivalentTo(model);
        }
コード例 #15
0
        public async Task Handle_OneCodeActionProviderWithMultipleCodeActions()
        {
            // Arrange
            var documentPath       = "C:/path/to/Page.razor";
            var codeDocument       = CreateCodeDocument("@code {}");
            var documentResolver   = CreateDocumentResolver(documentPath, codeDocument);
            var codeActionEndpoint = new CodeActionEndpoint(
                _documentMappingService,
                new RazorCodeActionProvider[] {
                new MockMultipleRazorCodeActionProvider(),
            },
                Array.Empty <CSharpCodeActionProvider>(),
                Dispatcher,
                documentResolver,
                _languageServer,
                _languageServerFeatureOptions)
            {
                _supportsCodeActionResolve = false
            };

            var request = new CodeActionParams()
            {
                TextDocument = new TextDocumentIdentifier(new Uri(documentPath)),
                Range        = new Range(new Position(0, 1), new Position(0, 1)),
                Context      = new OmniSharpVSCodeActionContext()
            };

            // Act
            var commandOrCodeActionContainer = await codeActionEndpoint.Handle(request, default);

            // Assert
            Assert.Equal(2, commandOrCodeActionContainer.Count());
        }
        public async Task Handle_InFunctionsDirective()
        {
            // Arrange
            var documentPath = "c:/Test.razor";
            var contents     = "@page \"/test\"\n@functions { private var x = 1; }";
            var request      = new CodeActionParams()
            {
                TextDocument = new TextDocumentIdentifier(new Uri(documentPath)),
                Range        = new Range(),
            };

            var location = new SourceLocation(contents.IndexOf("functions", StringComparison.Ordinal), -1, -1);
            var context  = CreateRazorCodeActionContext(request, location, documentPath, contents);

            var provider = new ExtractToCodeBehindCodeActionProvider();

            // Act
            var commandOrCodeActionContainer = await provider.ProvideAsync(context, default);

            // Assert
            var codeAction = Assert.Single(commandOrCodeActionContainer);
            var razorCodeActionResolutionParams = codeAction.Data as RazorCodeActionResolutionParams;
            var actionParams = razorCodeActionResolutionParams.Data as ExtractToCodeBehindCodeActionParams;

            Assert.Equal(14, actionParams.RemoveStart);
            Assert.Equal(24, actionParams.ExtractStart);
            Assert.Equal(47, actionParams.ExtractEnd);
            Assert.Equal(47, actionParams.RemoveEnd);
        }
コード例 #17
0
        public override Task <CommandOrCodeActionContainer> Handle(CodeActionParams request, CancellationToken cancellationToken)
        {
            var documentUri        = request.TextDocument.Uri;
            var compilationContext = this.compilationManager.GetCompilation(documentUri);

            if (compilationContext == null)
            {
                return(Task.FromResult(new CommandOrCodeActionContainer()));
            }

            var requestStartOffset = PositionHelper.GetOffset(compilationContext.LineStarts, request.Range.Start);
            var requestEndOffset   = request.Range.Start != request.Range.End
                ? PositionHelper.GetOffset(compilationContext.LineStarts, request.Range.End)
                : requestStartOffset;

            var compilation   = compilationContext.Compilation;
            var semanticModel = compilation.GetEntrypointSemanticModel();
            var diagnostics   = semanticModel.GetAllDiagnostics();

            var quickFixes = diagnostics
                             .Where(fixable =>
                                    fixable.Span.ContainsInclusive(requestStartOffset) ||
                                    fixable.Span.ContainsInclusive(requestEndOffset) ||
                                    (requestStartOffset <= fixable.Span.Position && fixable.GetEndPosition() <= requestEndOffset))
                             .OfType <IFixable>()
                             .SelectMany(fixable => fixable.Fixes.Select(fix => CreateQuickFix(request.TextDocument.Uri, compilationContext, fix)));

            List <CommandOrCodeAction> commandOrCodeActions = new();

            commandOrCodeActions.AddRange(quickFixes);

            var coreCompilerErrors = diagnostics
                                     .Where(diagnostic => !diagnostic.CanBeSuppressed());
            var diagnosticsThatCanBeSuppressed = diagnostics
                                                 .Where(diagnostic =>
                                                        (diagnostic.Span.ContainsInclusive(requestStartOffset) ||
                                                         diagnostic.Span.ContainsInclusive(requestEndOffset) ||
                                                         (requestStartOffset <= diagnostic.Span.Position && diagnostic.GetEndPosition() <= requestEndOffset)))
                                                 .Except(coreCompilerErrors);

            HashSet <string> diagnosticCodesToSuppressInline = new();

            foreach (IDiagnostic diagnostic in diagnosticsThatCanBeSuppressed)
            {
                if (!diagnosticCodesToSuppressInline.Contains(diagnostic.Code))
                {
                    diagnosticCodesToSuppressInline.Add(diagnostic.Code);

                    var commandOrCodeAction = DisableDiagnostic(documentUri, diagnostic.Code, semanticModel.SourceFile, diagnostic.Span, compilationContext.LineStarts);

                    if (commandOrCodeAction is not null)
                    {
                        commandOrCodeActions.Add(commandOrCodeAction);
                    }
                }
            }

            return(Task.FromResult(new CommandOrCodeActionContainer(commandOrCodeActions)));
        }
コード例 #18
0
        internal async Task <RazorCodeActionContext> GenerateRazorCodeActionContextAsync(CodeActionParams request, CancellationToken cancellationToken)
        {
            var documentSnapshot = await _projectSnapshotManagerDispatcher.RunOnDispatcherThreadAsync(() =>
            {
                _documentResolver.TryResolveDocument(request.TextDocument.Uri.GetAbsoluteOrUNCPath(), out var documentSnapshot);
                return(documentSnapshot);
            }, cancellationToken).ConfigureAwait(false);

            if (documentSnapshot is null)
            {
                return(null);
            }

            var codeDocument = await documentSnapshot.GetGeneratedOutputAsync().ConfigureAwait(false);

            if (codeDocument.IsUnsupported())
            {
                return(null);
            }

            var sourceText = await documentSnapshot.GetTextAsync().ConfigureAwait(false);

            // VS Provides `CodeActionParams.Context.SelectionRange` in addition to
            // `CodeActionParams.Range`. The `SelectionRange` is relative to where the
            // code action was invoked (ex. line 14, char 3) whereas the `Range` is
            // always at the start of the line (ex. line 14, char 0). We want to utilize
            // the relative positioning to ensure we provide code actions for the appropriate
            // context.
            //
            // Note: VS Code doesn't provide a `SelectionRange`.
            var vsCodeActionContext = (OmniSharpVSCodeActionContext)request.Context;

            if (vsCodeActionContext.SelectionRange != null)
            {
                request = request with {
                    Range = vsCodeActionContext.SelectionRange
                };
            }

            var linePosition = new LinePosition(
                request.Range.Start.Line,
                request.Range.Start.Character);
            var hostDocumentIndex = sourceText.Lines.GetPosition(linePosition);
            var location          = new SourceLocation(
                hostDocumentIndex,
                request.Range.Start.Line,
                request.Range.Start.Character);

            var context = new RazorCodeActionContext(
                request,
                documentSnapshot,
                codeDocument,
                location,
                sourceText,
                _languageServerFeatureOptions.SupportsFileManipulation,
                _supportsCodeActionResolve);

            return(context);
        }
        public async Task ShouldRouteToCorrect_Request_WithManyHandlers()
        {
            var textDocumentSyncHandler  = TextDocumentSyncHandlerExtensions.With(DocumentSelector.ForPattern("**/*.cs"), "csharp");
            var textDocumentSyncHandler2 = TextDocumentSyncHandlerExtensions.With(DocumentSelector.ForPattern("**/*.cake"), "csharp");

            textDocumentSyncHandler.Handle(Arg.Any <DidSaveTextDocumentParams>(), Arg.Any <CancellationToken>()).Returns(Unit.Value);
            textDocumentSyncHandler2.Handle(Arg.Any <DidSaveTextDocumentParams>(), Arg.Any <CancellationToken>()).Returns(Unit.Value);

            var codeActionHandler = Substitute.For <ICodeActionHandler>();

            codeActionHandler.GetRegistrationOptions().Returns(new CodeActionRegistrationOptions()
            {
                DocumentSelector = DocumentSelector.ForPattern("**/*.cs")
            });
            codeActionHandler
            .Handle(Arg.Any <CodeActionParams>(), Arg.Any <CancellationToken>())
            .Returns(new CommandOrCodeActionContainer());

            var registry           = new TestLanguageServerRegistry();
            var codeActionDelegate = Substitute.For <Func <CodeActionParams, CancellationToken, Task <CommandOrCodeActionContainer> > >();

            codeActionDelegate.Invoke(Arg.Any <CodeActionParams>(), Arg.Any <CancellationToken>())
            .Returns(new CommandOrCodeActionContainer());
            registry.OnCodeAction(
                codeActionDelegate,
                new CodeActionRegistrationOptions()
            {
                DocumentSelector = DocumentSelector.ForPattern("**/*.cake")
            }
                );

            var textDocumentIdentifiers = new TextDocumentIdentifiers();

            AutoSubstitute.Provide(textDocumentIdentifiers);
            var handlerCollection = new HandlerCollection(SupportedCapabilitiesFixture.AlwaysTrue, textDocumentIdentifiers)
            {
                textDocumentSyncHandler, textDocumentSyncHandler2, codeActionHandler
            };

            handlerCollection.Add(registry.Handlers);
            AutoSubstitute.Provide <IHandlerCollection>(handlerCollection);
            AutoSubstitute.Provide <IEnumerable <ILspHandlerDescriptor> >(handlerCollection);
            var mediator = AutoSubstitute.Resolve <LspRequestRouter>();

            var id      = Guid.NewGuid().ToString();
            var @params = new CodeActionParams()
            {
                TextDocument = new TextDocumentIdentifier(new Uri("file:///c:/test/123.cake"))
            };

            var request = new Request(id, DocumentNames.CodeAction, JObject.Parse(JsonConvert.SerializeObject(@params, new Serializer(ClientVersion.Lsp3).Settings)));

            await mediator.RouteRequest(mediator.GetDescriptor(request), request, CancellationToken.None);

            await codeActionHandler.Received(0).Handle(Arg.Any <CodeActionParams>(), Arg.Any <CancellationToken>());

            await codeActionDelegate.Received(1).Invoke(Arg.Any <CodeActionParams>(), Arg.Any <CancellationToken>());
        }
コード例 #20
0
        public async Task ProvideCodeActionsAsync_ReturnsCodeActionsAsync()
        {
            // Arrange
            var testDocUri        = new Uri("C:/path/to/file.razor");
            var testVirtualDocUri = new Uri("C:/path/to/file2.razor.g");
            var testCSharpDocUri  = new Uri("C:/path/to/file.razor.g.cs");

            var testVirtualDocument          = new TestVirtualDocumentSnapshot(testVirtualDocUri, 0);
            var csharpVirtualDocument        = new CSharpVirtualDocumentSnapshot(testCSharpDocUri, Mock.Of <ITextSnapshot>(MockBehavior.Strict), 0);
            LSPDocumentSnapshot testDocument = new TestLSPDocumentSnapshot(testDocUri, 0, testVirtualDocument, csharpVirtualDocument);

            var documentManager = new Mock <TrackingLSPDocumentManager>(MockBehavior.Strict);

            documentManager.Setup(manager => manager.TryGetDocument(It.IsAny <Uri>(), out testDocument))
            .Returns(true);

            var languageServer1Response = new[] { new VSCodeAction()
                                                  {
                                                      Title = "Response 1"
                                                  } };
            var languageServer2Response = new[] { new VSCodeAction()
                                                  {
                                                      Title = "Response 2"
                                                  } };
            IEnumerable <VSCodeAction[]> expectedResults = new List <VSCodeAction[]>()
            {
                languageServer1Response, languageServer2Response
            };
            var requestInvoker = new Mock <LSPRequestInvoker>(MockBehavior.Strict);

            requestInvoker.Setup(invoker => invoker.ReinvokeRequestOnMultipleServersAsync <CodeActionParams, VSCodeAction[]>(
                                     Methods.TextDocumentCodeActionName,
                                     LanguageServerKind.CSharp.ToContentType(),
                                     It.IsAny <Func <JToken, bool> >(),
                                     It.IsAny <CodeActionParams>(),
                                     It.IsAny <CancellationToken>()
                                     )).Returns(Task.FromResult(expectedResults));

            var uIContextManager = new Mock <RazorUIContextManager>(MockBehavior.Strict);
            var disposable       = new Mock <IDisposable>(MockBehavior.Strict);

            var target  = new DefaultRazorLanguageServerCustomMessageTarget(documentManager.Object, JoinableTaskContext, requestInvoker.Object, uIContextManager.Object, disposable.Object);
            var request = new CodeActionParams()
            {
                TextDocument = new LanguageServer.Protocol.TextDocumentIdentifier()
                {
                    Uri = testDocUri
                }
            };

            // Act
            var result = await target.ProvideCodeActionsAsync(request, CancellationToken.None);

            // Assert
            Assert.Collection(result,
                              r => Assert.Equal(languageServer1Response[0].Title, r.Title),
                              r => Assert.Equal(languageServer2Response[0].Title, r.Title));
        }
コード例 #21
0
ファイル: CodeActionHelpers.cs プロジェクト: josh-127/roslyn
        /// <summary>
        /// Get, order, and filter code actions, and then transform them into VSCodeActions.
        /// </summary>
        /// <remarks>
        /// Used by CodeActionsHandler.
        /// </remarks>
        public static async Task <VSInternalCodeAction[]> GetVSCodeActionsAsync(
            CodeActionParams request,
            CodeActionsCache codeActionsCache,
            Document document,
            CodeActionOptions options,
            ICodeFixService codeFixService,
            ICodeRefactoringService codeRefactoringService,
            CancellationToken cancellationToken)
        {
            var actionSets = await GetActionSetsAsync(
                document, options, codeFixService, codeRefactoringService, request.Range, cancellationToken).ConfigureAwait(false);

            if (actionSets.IsDefaultOrEmpty)
            {
                return(Array.Empty <VSInternalCodeAction>());
            }

            await codeActionsCache.UpdateActionSetsAsync(document, request.Range, actionSets, cancellationToken).ConfigureAwait(false);

            var documentText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);

            // Each suggested action set should have a unique set number, which is used for grouping code actions together.
            var currentHighestSetNumber = 0;

            using var _ = ArrayBuilder <VSInternalCodeAction> .GetInstance(out var codeActions);

            foreach (var set in actionSets)
            {
                var currentSetNumber = ++currentHighestSetNumber;
                foreach (var suggestedAction in set.Actions)
                {
                    // Filter out code actions with options since they'll show dialogs and we can't remote the UI and the options.
                    if (suggestedAction.OriginalCodeAction is CodeActionWithOptions)
                    {
                        continue;
                    }

                    // TO-DO: Re-enable code actions involving package manager once supported by LSP.
                    // https://github.com/dotnet/roslyn/issues/48698
                    if (suggestedAction.OriginalCodeAction.Tags.Equals(WellKnownTagArrays.NuGet))
                    {
                        continue;
                    }

                    codeActions.Add(GenerateVSCodeAction(
                                        request, documentText,
                                        suggestedAction: suggestedAction,
                                        codeActionKind: GetCodeActionKindFromSuggestedActionCategoryName(set.CategoryName !),
                                        setPriority: set.Priority,
                                        applicableRange: set.ApplicableToSpan.HasValue ? ProtocolConversions.TextSpanToRange(set.ApplicableToSpan.Value, documentText) : null,
                                        currentSetNumber: currentSetNumber,
                                        currentHighestSetNumber: ref currentHighestSetNumber));
                }
            }

            return(codeActions.ToArray());
        }
コード例 #22
0
        public override async Task <CommandOrCodeActionContainer> Handle(CodeActionParams request, CancellationToken cancellationToken)
        {
            var omnisharpRequest = new GetCodeActionsRequest
            {
                FileName  = Helpers.FromUri(request.TextDocument.Uri),
                Column    = request.Range.Start.Character,
                Line      = request.Range.Start.Line,
                Selection = Helpers.FromRange(request.Range),
            };

            var omnisharpResponse = await _getActionsHandler.Handle(omnisharpRequest);

            var codeActions = new List <CodeAction>();

            foreach (var ca in omnisharpResponse.CodeActions)
            {
                CodeActionKind kind;
                if (ca.Identifier.StartsWith("using "))
                {
                    kind = CodeActionKind.QuickFix;
                }
                else if (ca.Identifier.StartsWith("Inline "))
                {
                    kind = CodeActionKind.RefactorInline;
                }
                else if (ca.Identifier.StartsWith("Extract "))
                {
                    kind = CodeActionKind.RefactorExtract;
                }
                else if (ca.Identifier.StartsWith("Change "))
                {
                    kind = CodeActionKind.QuickFix;
                }
                else
                {
                    kind = CodeActionKind.Refactor;
                }

                codeActions.Add(
                    new CodeAction
                {
                    Title       = ca.Name,
                    Kind        = kind,
                    Diagnostics = new Container <Diagnostic>(),
                    Edit        = new WorkspaceEdit(),
                    Command     = Command.Create("omnisharp/executeCodeAction")
                                  .WithArguments(new CommandData()
                    {
                        Uri        = request.TextDocument.Uri,
                        Identifier = ca.Identifier,
                        Name       = ca.Name,
                        Range      = request.Range,
                    })
                                  with {
                        Title = ca.Name
                    }
                });
        public async Task Handle_ValidCodeAction_VS_ReturnsCodeActions()
        {
            // Arrange
            var documentPath = "c:/Test.razor";
            var contents     = "@code { Path; }";
            var request      = new CodeActionParams()
            {
                TextDocument = new TextDocumentIdentifier(new Uri(documentPath)),
                Range        = new Range(),
                Context      = new CodeActionContext()
                {
                    Diagnostics = new Container <Diagnostic>()
                }
            };

            var location = new SourceLocation(0, -1, -1);
            var context  = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(8, 4), supportsCodeActionResolve: true);

            context.CodeDocument.SetFileKind(FileKinds.Legacy);

            var provider          = new TypeAccessibilityCodeActionProvider();
            var csharpCodeActions = new[] {
                new RazorCodeAction()
                {
                    Title = "System.IO.Path",
                    Name  = "FullyQualify"
                },
                new RazorCodeAction()
                {
                    Title = "using System.IO;",
                    Name  = "AddImport"
                }
            };

            // Act
            var results = await provider.ProvideAsync(context, csharpCodeActions, default);

            // Assert
            Assert.Collection(results,
                              r =>
            {
                Assert.Equal("@using System.IO", r.Title);
                Assert.Null(r.Edit);
                Assert.NotNull(r.Data);
                var resolutionParams = (r.Data as JObject).ToObject <RazorCodeActionResolutionParams>();
                Assert.Equal(LanguageServerConstants.CodeActions.AddUsing, resolutionParams.Action);
            },
                              r =>
            {
                Assert.Equal("System.IO.Path", r.Title);
                Assert.Null(r.Edit);
                Assert.NotNull(r.Data);
            }
                              );
        }
コード例 #24
0
        public async Task GetCSharpCodeActionsFromLanguageServerAsync_ReturnsCodeActions()
        {
            // Arrange
            var documentPath           = "C:/path/to/Page.razor";
            var codeDocument           = CreateCodeDocument("@code {}");
            var documentResolver       = CreateDocumentResolver(documentPath, codeDocument);
            var projectedRange         = new Range(new Position(15, 2), new Position(15, 2));
            var documentMappingService = CreateDocumentMappingService(projectedRange);
            var languageServer         = CreateLanguageServer();
            var codeActionEndpoint     = new CodeActionEndpoint(
                documentMappingService,
                Array.Empty <RazorCodeActionProvider>(),
                new CSharpCodeActionProvider[] {
                new MockCSharpCodeActionProvider()
            },
                Dispatcher,
                documentResolver,
                languageServer,
                _languageServerFeatureOptions)
            {
                _supportsCodeActionResolve = false
            };

            var initialRange = new Range(new Position(0, 1), new Position(0, 1));
            var request      = new CodeActionParams()
            {
                TextDocument = new TextDocumentIdentifier(new Uri(documentPath)),
                Range        = initialRange,
                Context      = new OmniSharpVSCodeActionContext()
                {
                    SelectionRange = initialRange
                }
            };

            var context = await codeActionEndpoint.GenerateRazorCodeActionContextAsync(request, default);

            // Act
            var results = await codeActionEndpoint.GetCSharpCodeActionsFromLanguageServerAsync(context, default);

            // Assert
            Assert.Single(results);
            var diagnostics = results.Single().Diagnostics.ToArray();

            Assert.Equal(2, diagnostics.Length);

            // Diagnostic ranges contain the projected range for
            // 1. Range
            // 2. SelectionRange
            //
            // This helps verify that the CodeActionEndpoint is mapping the
            // ranges correctly using the mapping service
            Assert.Equal(projectedRange, diagnostics[0].Range);
            Assert.Equal(projectedRange, diagnostics[1].Range);
        }
        public async Task Handle_InvalidDiagnostics_ReturnsEmpty()
        {
            // Arrange
            var documentPath = "c:/Test.razor";
            var contents     = "";
            var request      = new CodeActionParams()
            {
                TextDocument = new TextDocumentIdentifier(new Uri(documentPath)),
                Range        = new Range(),
                Context      = new CodeActionContext()
                {
                    Diagnostics = new Container <Diagnostic>(
                        new Diagnostic()
                    {
                        // Invalid as Error is expected
                        Severity = DiagnosticSeverity.Warning,
                        Code     = new DiagnosticCode("CS0534")
                    },
                        new Diagnostic()
                    {
                        // Invalid as CS error code is expected
                        Severity = DiagnosticSeverity.Error,
                        Code     = new DiagnosticCode(0246)
                    },
                        new Diagnostic()
                    {
                        // Invalid as CS0534 or CS0535 is expected
                        Severity = DiagnosticSeverity.Error,
                        Code     = new DiagnosticCode("CS0183")
                    }
                        )
                }
            };

            var location = new SourceLocation(0, -1, -1);
            var context  = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(0, 0));

            context.CodeDocument.SetFileKind(FileKinds.Legacy);

            var provider          = new ImplementInterfaceAbstractClassCodeActionProvider();
            var csharpCodeActions = new[] {
                new RazorCodeAction()
                {
                    Title = "Implement abstract class"
                }
            };

            // Act
            var results = await provider.ProvideAsync(context, csharpCodeActions, default);

            // Assert
            Assert.Empty(results);
        }
        public async Task Handle_InvalidDiagnostics_VSCode_ReturnsEmpty()
        {
            // Arrange
            var documentPath = "c:/Test.razor";
            var contents     = "";
            var request      = new CodeActionParams()
            {
                TextDocument = new TextDocumentIdentifier(new Uri(documentPath)),
                Range        = new Range(),
                Context      = new CodeActionContext()
                {
                    Diagnostics = new Container <Diagnostic>(
                        new Diagnostic()
                    {
                        // Invalid as Error is expected
                        Severity = DiagnosticSeverity.Warning,
                        Code     = new DiagnosticCode("CS0246")
                    },
                        new Diagnostic()
                    {
                        // Invalid as CS error code is expected
                        Severity = DiagnosticSeverity.Error,
                        Code     = new DiagnosticCode(0246)
                    },
                        new Diagnostic()
                    {
                        // Invalid as CS0246 or CS0103 is expected
                        Severity = DiagnosticSeverity.Error,
                        Code     = new DiagnosticCode("CS0183")
                    }
                        )
                }
            };

            var location = new SourceLocation(0, -1, -1);
            var context  = CreateRazorCodeActionContext(request, location, documentPath, contents, new SourceSpan(0, 0), supportsCodeActionResolve: false);

            context.CodeDocument.SetFileKind(FileKinds.Legacy);

            var provider          = new TypeAccessibilityCodeActionProvider();
            var csharpCodeActions = new[] {
                new RazorCodeAction()
                {
                    Title = "System.Net.Dns"
                }
            };

            // Act
            var results = await provider.ProvideAsync(context, csharpCodeActions, default);

            // Assert
            Assert.Empty(results);
        }
コード例 #27
0
        public async Task RequestsCancellation()
        {
            var textDocumentSyncHandler = TextDocumentSyncHandlerExtensions.With(DocumentSelector.ForPattern("**/*.cs"), "csharp");

            textDocumentSyncHandler.Handle(Arg.Any <DidSaveTextDocumentParams>(), Arg.Any <CancellationToken>()).Returns(Unit.Value);

            var codeActionHandler = Substitute.For <ICodeActionHandler>();

            codeActionHandler.GetRegistrationOptions().Returns(new CodeActionRegistrationOptions()
            {
                DocumentSelector = DocumentSelector.ForPattern("**/*.cs")
            });
            codeActionHandler
            .Handle(Arg.Any <CodeActionParams>(), Arg.Any <CancellationToken>())
            .Returns(async(c) =>
            {
                await Task.Delay(1000, c.Arg <CancellationToken>());
                throw new XunitException("Task was not cancelled in time!");
                return(new CommandOrCodeActionContainer());
            });

            var collection = new HandlerCollection(SupportedCapabilitiesFixture.AlwaysTrue, new TextDocumentIdentifiers())
            {
                textDocumentSyncHandler, codeActionHandler
            };

            AutoSubstitute.Provide <IHandlerCollection>(collection);
            AutoSubstitute.Provide <IEnumerable <ILspHandlerDescriptor> >(collection);
            var mediator = AutoSubstitute.Resolve <LspRequestRouter>();

            var id      = Guid.NewGuid().ToString();
            var @params = new CodeActionParams()
            {
                TextDocument = new TextDocumentIdentifier(new Uri("file:///c:/test/123.cs")),
                Range        = new Range(new Position(1, 1), new Position(2, 2)),
                Context      = new CodeActionContext()
                {
                    Diagnostics = new Container <Diagnostic>()
                }
            };

            var request = new Request(id, "textDocument/codeAction", JObject.Parse(JsonConvert.SerializeObject(@params, new Serializer(ClientVersion.Lsp3).Settings)));

            var response = ((IRequestRouter <ILspHandlerDescriptor>)mediator).RouteRequest(request, CancellationToken.None);

            mediator.CancelRequest(id);
            var result = await response;

            result.IsError.Should().BeTrue();
            result.Error.Should().BeEquivalentTo(new RequestCancelled());
        }
        public async Task ShouldRouteToCorrect_Request_WithManyHandlers()
        {
            var textDocumentSyncHandler  = TextDocumentSyncHandlerExtensions.With(DocumentSelector.ForPattern("**/*.cs"));
            var textDocumentSyncHandler2 = TextDocumentSyncHandlerExtensions.With(DocumentSelector.ForPattern("**/*.cake"));

            textDocumentSyncHandler.Handle(Arg.Any <DidSaveTextDocumentParams>()).Returns(Task.CompletedTask);
            textDocumentSyncHandler2.Handle(Arg.Any <DidSaveTextDocumentParams>()).Returns(Task.CompletedTask);

            var codeActionHandler = Substitute.For <ICodeActionHandler>();

            codeActionHandler.GetRegistrationOptions().Returns(new TextDocumentRegistrationOptions()
            {
                DocumentSelector = DocumentSelector.ForPattern("**/*.cs")
            });
            codeActionHandler
            .Handle(Arg.Any <CodeActionParams>(), Arg.Any <CancellationToken>())
            .Returns(new CommandContainer());

            var codeActionHandler2 = Substitute.For <ICodeActionHandler>();

            codeActionHandler2.GetRegistrationOptions().Returns(new TextDocumentRegistrationOptions()
            {
                DocumentSelector = DocumentSelector.ForPattern("**/*.cake")
            });
            codeActionHandler2
            .Handle(Arg.Any <CodeActionParams>(), Arg.Any <CancellationToken>())
            .Returns(new CommandContainer());

            var collection = new HandlerCollection {
                textDocumentSyncHandler, textDocumentSyncHandler2, codeActionHandler, codeActionHandler2
            };
            var handlerMatcherCollection = new HandlerMatcherCollection {
                new TextDocumentMatcher(_testLoggerFactory.CreateLogger <TextDocumentMatcher>(), collection.TextDocumentSyncHandlers)
            };
            var mediator = new LspRequestRouter(collection, _testLoggerFactory, handlerMatcherCollection, new Serializer());

            var id      = Guid.NewGuid().ToString();
            var @params = new CodeActionParams()
            {
                TextDocument = new TextDocumentIdentifier(new Uri("file:///c:/test/123.cake"))
            };

            var request = new Request(id, DocumentNames.CodeAction, JObject.Parse(JsonConvert.SerializeObject(@params, new Serializer(ClientVersion.Lsp3).Settings)));

            await mediator.RouteRequest(mediator.GetDescriptor(request), request);

            await codeActionHandler.Received(0).Handle(Arg.Any <CodeActionParams>(), Arg.Any <CancellationToken>());

            await codeActionHandler2.Received(1).Handle(Arg.Any <CodeActionParams>(), Arg.Any <CancellationToken>());
        }
コード例 #29
0
 public RazorCodeActionContext(
     CodeActionParams request,
     DocumentSnapshot documentSnapshot,
     RazorCodeDocument codeDocument,
     SourceLocation location,
     SourceText sourceText,
     bool supportsFileCreation)
 {
     Request              = request ?? throw new ArgumentNullException(nameof(request));
     DocumentSnapshot     = documentSnapshot ?? throw new ArgumentNullException(nameof(documentSnapshot));
     CodeDocument         = codeDocument ?? throw new ArgumentNullException(nameof(codeDocument));
     Location             = location;
     SourceText           = sourceText ?? throw new ArgumentNullException(nameof(sourceText));
     SupportsFileCreation = supportsFileCreation;
 }
コード例 #30
0
        public async Task Handle_NoDocument()
        {
            // Arrange
            var documentPath       = "C:/path/to/Page.razor";
            var codeActionEndpoint = new CodeActionEndpoint(new RazorCodeActionProvider[] { }, Dispatcher, EmptyDocumentResolver, LoggerFactory);
            var request            = new CodeActionParams()
            {
                TextDocument = new TextDocumentIdentifier(new Uri(documentPath)),
                Range        = new Range(new Position(0, 1), new Position(0, 1)),
            };

            // Act
            var commandOrCodeActionContainer = await codeActionEndpoint.Handle(request, default);

            // Assert
            Assert.Null(commandOrCodeActionContainer);
        }