Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

x/tools/gopls: add willSaveWaitUntil support for formatting and goimports #57281

Open
aktau opened this issue Dec 13, 2022 · 1 comment
Open
Labels
FeatureRequest gopls Issues related to the Go language server, gopls. help wanted Tools This label describes issues relating to any tools in the x/tools repository.
Milestone

Comments

@aktau
Copy link
Contributor

aktau commented Dec 13, 2022

gopls version

$ gopls -v version
Build info
----------
golang.org/x/tools/gopls v0.10.1
    golang.org/x/tools/gopls@v0.10.1 h1:JoHe17pdZ8Vsa24/GUO8iTVTKPh0EOBiWpPop7XJybI=
    github.com/BurntSushi/toml@v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
    github.com/google/go-cmp@v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
    github.com/sergi/go-diff@v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
    golang.org/x/exp@v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
    golang.org/x/exp/typeparams@v0.0.0-20220722155223-a9213eeb770e h1:7Xs2YCOpMlNqSQSmrrnhlzBXIE/bpMecZplbLePTJvE=
    golang.org/x/mod@v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
    golang.org/x/sync@v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
    golang.org/x/sys@v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
    golang.org/x/text@v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
    golang.org/x/tools@v0.2.1-0.20221101170700-b5bc717366b2 h1:KBm+UwBaO/tdQ35tfGvxH1FUCiXRg4MoTzkznsdeab8=
    golang.org/x/vuln@v0.0.0-20221010193109-563322be2ea9 h1:KaYZQUtEEaV8aVADIHAuYBTjo77aUcCvC7KTGKM3J1I=
    honnef.co/go/tools@v0.3.3 h1:oDx7VAwstgpYpb3wv0oxiZlxY+foCpRAwY7Vk6XpAgA=
    mvdan.cc/gofumpt@v0.3.1 h1:avhhrOmv0IuvQVK7fvwV91oFSGAk5/6Po8GXTzICeu8=
    mvdan.cc/xurls/v2@v2.4.0 h1:tzxjVAj+wSBmDcF6zBB7/myTy3gX9xvi8Tyr28AuQgc=
go: go1.20-dev +a813be86df

go env

If this is relevant, I'll fill it in, just ask. For now I consider this spam.

What did you do?

After Neovim added support for textDocument/willSaveWaitUntil (neovim/neovim#21315). I started gopls to see if it supports this as it can save a roundtrip and would allow me to get rid of my gopls-specific goimports stanza. It currently looks like this:

-- on_attach handler
-- ...
    -- Enable format-on-save when available (see :help lsp-config).
    --
    -- TODO: When gopls implements willSaveWaitUntil, remove this.
    if client.server_capabilities.documentFormattingProvider then
      -- With gopls, textDocument/formatting only runs gofmt. If we also want
      -- goimports a specific code action. See
      -- https://github.com/Microsoft/language-server-protocol/issues/726.
      if vim.bo[bufnr].filetype == "go" then
        aucmd("BufWritePre", function() goimports() end)
      end
      aucmd("BufWritePre", function() vim.lsp.buf.format({timeout_ms=1000}) end)
    end
-- ...

local function goimports()
  local params = vim.lsp.util.make_range_params()
  params.context = { only = { "source.organizeImports" } }

  local response = vim.lsp.buf_request_sync(0, "textDocument/codeAction", params, 1000)
  for _, r in pairs(response or {}) do
    for _, action in pairs(r.result or {}) do
      -- textDocument/codeAction can return either Command[] or CodeAction[]. If
      -- it is a CodeAction, it can have either an edit, a command or both.
      -- Edits should be executed first.
      if action.edit then
        vim.lsp.util.apply_workspace_edit(action.edit, "UTF-8")
      end
      if action.command then
        -- If the response was a Command[], then the inner "command' is a
        -- string, if the response was a CodeAction, then the inner command is a
        -- Command.
        local command = type(action.command) == "table" and action.command or action
        vim.lsp.buf.execute_command(command)
      end
    end
  end
end

What did you expect to see?

I expected the server_capabilities to contain:

server_capabilities = {
  -- ...
  textDocumentSync = { 
    -- ...
    willSaveWaitUntil = true,
  },
}

So that Neovim (and other LSP clients that support this) can notify gopls before saving and apply last-minute edits returned from gopls.

What did you see instead?

The server_capabilities does not contain what I expected.

Editor and settings

Neovim 0.9.0-dev (HEAD as of today). The client capabilities are:

{
  callHierarchy = {
    dynamicRegistration = false
  },
  textDocument = {
    codeAction = {
      codeActionLiteralSupport = {
        codeActionKind = {
          valueSet = { "", "quickfix", "refactor", "refactor.extract", "refactor.inline", "refactor.rewrite", "source", "source.organizeImports" }
        }
      },
      dataSupport = true,
      dynamicRegistration = false,
      isPreferredSupport = true,
      resolveSupport = {
        properties = { "edit" }
      }
    },
    completion = {
      completionItem = {
        commitCharactersSupport = true,
        deprecatedSupport = true,
        documentationFormat = { "markdown", "plaintext" },
        insertReplaceSupport = true,
        insertTextModeSupport = {
          valueSet = { 1, 2 }
        },
        labelDetailsSupport = true,
        preselectSupport = true,
        resolveSupport = {
          properties = { "documentation", "detail", "additionalTextEdits" }
        },
        snippetSupport = true,
        tagSupport = {
          valueSet = { 1 }
        }
      },
      completionItemKind = {
        valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }
      },
      completionList = {
        itemDefaults = { "commitCharacters", "editRange", "insertTextFormat", "insertTextMode", "data" }
      },
      contextSupport = true,
      dynamicRegistration = false,
      insertTextMode = 1
    },
    declaration = {
      linkSupport = true
    },
    definition = {
      linkSupport = true
    },
    documentHighlight = {
      dynamicRegistration = false
    },
    documentSymbol = {
      dynamicRegistration = false,
      hierarchicalDocumentSymbolSupport = true,
      symbolKind = {
        valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }
      }
    },
    hover = {
      contentFormat = { "markdown", "plaintext" },
      dynamicRegistration = false
    },
    implementation = {
      linkSupport = true
    },
    publishDiagnostics = {
      relatedInformation = true,
      tagSupport = {
        valueSet = { 1, 2 }
      }
    },
    references = {
      dynamicRegistration = false
    },
    rename = {
      dynamicRegistration = false,
      prepareSupport = true
    },
    semanticTokens = {
      augmentsSyntaxTokens = true,
      dynamicRegistration = false,
      formats = { "relative" },
      multilineTokenSupport = false,
      overlappingTokenSupport = true,
      requests = {
        full = {
          delta = true
        },
        range = false
      },
      serverCancelSupport = false,
      tokenModifiers = { "declaration", "definition", "readonly", "static", "deprecated", "abstract", "async", "modification", "documentation", "defaultLibrary" },
      tokenTypes = { "namespace", "type", "class", "enum", "interface", "struct", "typeParameter", "parameter", "variable", "property", "enumMember", "event", "function", "method", "macro", "keyword", "modifier", "comment", "string", "number", "regexp", "operator", "decorator" }
    },
    signatureHelp = {
      dynamicRegistration = false,
      signatureInformation = {
        activeParameterSupport = true,
        documentationFormat = { "markdown", "plaintext" },
        parameterInformation = {
          labelOffsetSupport = true
        }
      }
    },
    synchronization = {
      didSave = true,
      dynamicRegistration = false,
      willSave = true,
      willSaveWaitUntil = true
    },
    typeDefinition = {
      linkSupport = true
    }
  },
  window = {
    showDocument = {
      support = true
    },
    showMessage = {
      messageActionItem = {
        additionalPropertiesSupport = false
      }
    },
    workDoneProgress = true
  },
  workspace = {
    applyEdit = true,
    configuration = true,
    semanticTokens = {
      refreshSupport = true
    },
    symbol = {
      dynamicRegistration = false,
      hierarchicalWorkspaceSymbolSupport = true,
      symbolKind = {
        valueSet = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }
      }
    },
    workspaceEdit = {
      resourceOperations = { "rename", "create", "delete" }
    },
    workspaceFolders = true
  }
}
@gopherbot gopherbot added Tools This label describes issues relating to any tools in the x/tools repository. gopls Issues related to the Go language server, gopls. labels Dec 13, 2022
@gopherbot gopherbot added this to the Unreleased milestone Dec 13, 2022
@findleyr
Copy link
Contributor

Thanks for the feature request, and the explanation of why this would help you. Putting this in our queue as a feature that we want to support, but it is also an opportunity for external contribution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
FeatureRequest gopls Issues related to the Go language server, gopls. help wanted Tools This label describes issues relating to any tools in the x/tools repository.
Projects
None yet
Development

No branches or pull requests

3 participants