diff --git a/internal/lsp/code_action.go b/internal/lsp/code_action.go index c49b19e4..e320f73d 100644 --- a/internal/lsp/code_action.go +++ b/internal/lsp/code_action.go @@ -26,57 +26,42 @@ func (s *Server) codeAction(ctx context.Context, params *protocol.CodeActionPara return nil, err } var codeActions []protocol.CodeAction - // Determine what code actions we should take based on the diagnostics. - if findImportErrors(params.Context.Diagnostics) { - edits, err := organizeImports(ctx, view, spn) - if err != nil { - return nil, err - } - if len(edits) > 0 { - // TODO(rstambler): Handle params.Context.Only when VSCode-Go uses a - // version of vscode-languageclient that fixes - // https://github.com/Microsoft/vscode-languageserver-node/issues/442. + // TODO(rstambler): Handle params.Context.Only when VSCode-Go uses a + // version of vscode-languageclient that fixes + // https://github.com/Microsoft/vscode-languageserver-node/issues/442. + edits, err := organizeImports(ctx, view, spn) + if err != nil { + return nil, err + } + if len(edits) > 0 { + codeActions = append(codeActions, protocol.CodeAction{ + Title: "Organize Imports", + Kind: protocol.SourceOrganizeImports, + Edit: &protocol.WorkspaceEdit{ + Changes: &map[string][]protocol.TextEdit{ + string(spn.URI()): edits, + }, + }, + }) + // If we also have diagnostics, we can associate them with quick fixes. + if findImportErrors(params.Context.Diagnostics) { + // TODO(rstambler): Separate this into a set of codeActions per diagnostic, + // where each action is the addition or removal of one import. + // This can only be done when https://golang.org/issue/31493 is resolved. codeActions = append(codeActions, protocol.CodeAction{ - Title: "Organize Imports", - Kind: protocol.SourceOrganizeImports, + Title: "Organize All Imports", // clarify that all imports will change + Kind: protocol.QuickFix, Edit: &protocol.WorkspaceEdit{ Changes: &map[string][]protocol.TextEdit{ - string(spn.URI()): edits, + string(uri): edits, }, }, }) - // Add any quick fixes for each import-related diagnostic that we see. - fixes, err := quickFixes(spn.URI(), params.Context.Diagnostics, edits) - if err != nil { - return nil, err - } - codeActions = append(codeActions, fixes...) } } return codeActions, nil } -// findImports determines if a given diagnostic represents an error that could -// be fixed by organizing imports. -// TODO(rstambler): We need a better way to check this than string matching. -func findImportErrors(diagnostics []protocol.Diagnostic) bool { - for _, diagnostic := range diagnostics { - // "undeclared name: X" may be an unresolved import. - if strings.HasPrefix(diagnostic.Message, "undeclared name: ") { - return true - } - // "could not import: X" may be an invalid import. - if strings.HasPrefix(diagnostic.Message, "could not import: ") { - return true - } - // "X imported but not used" is an unused import. - if strings.HasSuffix(diagnostic.Message, " imported but not used") { - return true - } - } - return false -} - func organizeImports(ctx context.Context, v source.View, s span.Span) ([]protocol.TextEdit, error) { f, m, err := newColumnMap(ctx, v, s.URI()) if err != nil { @@ -101,19 +86,23 @@ func organizeImports(ctx context.Context, v source.View, s span.Span) ([]protoco return ToProtocolEdits(m, edits) } -// TODO(rstambler): Separate this into a set of codeActions per diagnostic, -// where each action is the addition or removal of one import. -// This can only be done when https://golang.org/issue/31493 is resolved. -func quickFixes(uri span.URI, diagnostics []protocol.Diagnostic, edits []protocol.TextEdit) ([]protocol.CodeAction, error) { - return []protocol.CodeAction{ - { - Title: "Organize All Imports", - Kind: protocol.QuickFix, - Edit: &protocol.WorkspaceEdit{ - Changes: &map[string][]protocol.TextEdit{ - string(uri): edits, - }, - }, - }, - }, nil +// findImports determines if a given diagnostic represents an error that could +// be fixed by organizing imports. +// TODO(rstambler): We need a better way to check this than string matching. +func findImportErrors(diagnostics []protocol.Diagnostic) bool { + for _, diagnostic := range diagnostics { + // "undeclared name: X" may be an unresolved import. + if strings.HasPrefix(diagnostic.Message, "undeclared name: ") { + return true + } + // "could not import: X" may be an invalid import. + if strings.HasPrefix(diagnostic.Message, "could not import: ") { + return true + } + // "X imported but not used" is an unused import. + if strings.HasSuffix(diagnostic.Message, " imported but not used") { + return true + } + } + return false }