Repository: go
Issue: x/tools/gopls: add willSaveWaitUntil support for formatting and goimports 
Labels: help wanted,FeatureRequest,gopls,Tools,Refactoring

### 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` (https://github.com/neovim/neovim/pull/21315). I started gopls to see if it supports this as it can [save a roundtrip](https://github.com/Microsoft/language-server-protocol/issues/726) and would allow me to get rid of my gopls-specific goimports stanza. It currently looks like this:

```lua
-- 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:

```lua
{
  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
  }
}
```