Example #1
0
        public async Task <CommandOrCodeActionContainer> Handle(CodeActionParams request, CancellationToken cancellationToken)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return(Array.Empty <CommandOrCodeAction>());
            }

            // On Windows, VSCode still gives us file URIs like "file:///c%3a/...", so we need to escape them
            IReadOnlyDictionary <string, MarkerCorrection> corrections = await _analysisService.GetMostRecentCodeActionsForFileAsync(
                _workspaceService.GetFile(request.TextDocument.Uri)).ConfigureAwait(false);

            if (corrections == null)
            {
                return(Array.Empty <CommandOrCodeAction>());
            }

            var codeActions = new List <CommandOrCodeAction>();

            // If there are any code fixes, send these commands first so they appear at top of "Code Fix" menu in the client UI.
            foreach (Diagnostic diagnostic in request.Context.Diagnostics)
            {
                if (string.IsNullOrEmpty(diagnostic.Code.String))
                {
                    _logger.LogWarning(
                        $"textDocument/codeAction skipping diagnostic with empty Code field: {diagnostic.Source} {diagnostic.Message}");

                    continue;
                }

                string diagnosticId = AnalysisService.GetUniqueIdFromDiagnostic(diagnostic);
                if (corrections.TryGetValue(diagnosticId, out MarkerCorrection correction))
                {
                    codeActions.Add(new CodeAction
                    {
                        Title = correction.Name,
                        Kind  = CodeActionKind.QuickFix,
                        Edit  = new WorkspaceEdit
                        {
                            DocumentChanges = new Container <WorkspaceEditDocumentChange>(
                                new WorkspaceEditDocumentChange(
                                    new TextDocumentEdit
                            {
                                TextDocument = new VersionedTextDocumentIdentifier
                                {
                                    Uri = request.TextDocument.Uri
                                },
                                Edits = new TextEditContainer(correction.Edits.Select(ScriptRegion.ToTextEdit))
                            }))
                        }
                    });
                }
            }

            // Add "show documentation" commands last so they appear at the bottom of the client UI.
            // These commands do not require code fixes. Sometimes we get a batch of diagnostics
            // to create commands for. No need to create multiple show doc commands for the same rule.
            var ruleNamesProcessed = new HashSet <string>();

            foreach (Diagnostic diagnostic in request.Context.Diagnostics)
            {
                if (!diagnostic.Code.IsString || string.IsNullOrEmpty(diagnostic.Code.String))
                {
                    continue;
                }

                if (string.Equals(diagnostic.Source, "PSScriptAnalyzer", StringComparison.OrdinalIgnoreCase) &&
                    !ruleNamesProcessed.Contains(diagnostic.Code.String))
                {
                    ruleNamesProcessed.Add(diagnostic.Code.String);
                    var title = $"Show documentation for: {diagnostic.Code.String}";
                    codeActions.Add(new CodeAction
                    {
                        Title = title,
                        // This doesn't fix anything, but I'm adding it here so that it shows up in VS Code's
                        // Quick fix UI. The VS Code team is working on a way to support documentation CodeAction's better
                        // but this is good for now until that's ready.
                        Kind    = CodeActionKind.QuickFix,
                        Command = new Command
                        {
                            Title     = title,
                            Name      = "PowerShell.ShowCodeActionDocumentation",
                            Arguments = JArray.FromObject(new[] { diagnostic.Code.String })
                        }
                    });
                }
            }

            return(codeActions);
        }
        public async Task <CommandOrCodeActionContainer> Handle(CodeActionParams request, CancellationToken cancellationToken)
        {
            IReadOnlyDictionary <string, MarkerCorrection> corrections = await _analysisService.GetMostRecentCodeActionsForFileAsync(request.TextDocument.Uri.ToString());

            if (corrections == null)
            {
                // TODO: Find out if we can cache this empty value
                return(new CommandOrCodeActionContainer());
            }

            var codeActions = new List <CommandOrCodeAction>();

            // If there are any code fixes, send these commands first so they appear at top of "Code Fix" menu in the client UI.
            foreach (Diagnostic diagnostic in request.Context.Diagnostics)
            {
                if (diagnostic.Code.IsLong)
                {
                    _logger.LogWarning(
                        $"textDocument/codeAction skipping diagnostic with non-string code {diagnostic.Code.Long}: {diagnostic.Source} {diagnostic.Message}");
                }
                else if (string.IsNullOrEmpty(diagnostic.Code.String))
                {
                    _logger.LogWarning(
                        $"textDocument/codeAction skipping diagnostic with empty Code field: {diagnostic.Source} {diagnostic.Message}");

                    continue;
                }


                string diagnosticId = AnalysisService.GetUniqueIdFromDiagnostic(diagnostic);
                if (corrections.TryGetValue(diagnosticId, out MarkerCorrection correction))
                {
                    codeActions.Add(new Command()
                    {
                        Title     = correction.Name,
                        Name      = "PowerShell.ApplyCodeActionEdits",
                        Arguments = JArray.FromObject(correction.Edits)
                    });
                }
            }

            // Add "show documentation" commands last so they appear at the bottom of the client UI.
            // These commands do not require code fixes. Sometimes we get a batch of diagnostics
            // to create commands for. No need to create multiple show doc commands for the same rule.
            var ruleNamesProcessed = new HashSet <string>();

            foreach (Diagnostic diagnostic in request.Context.Diagnostics)
            {
                if (!diagnostic.Code.IsString || string.IsNullOrEmpty(diagnostic.Code.String))
                {
                    continue;
                }

                if (string.Equals(diagnostic.Source, "PSScriptAnalyzer", StringComparison.OrdinalIgnoreCase) &&
                    !ruleNamesProcessed.Contains(diagnostic.Code.String))
                {
                    ruleNamesProcessed.Add(diagnostic.Code.String);

                    codeActions.Add(
                        new Command
                    {
                        Title     = $"Show documentation for \"{diagnostic.Code}\"",
                        Name      = "PowerShell.ShowCodeActionDocumentation",
                        Arguments = JArray.FromObject(new[] { diagnostic.Code })
                    });
                }
            }

            return(codeActions);
        }