Repository: neovim/neovim
File: runtime/lua/vim/lsp/on_type_formatting.lua (Lua)

Code before:

--- @param client vim.lsp.Client
--- @param bufnr integer
local function attach_otf(client, bufnr)
  local client_id = client.id
  ---@type lsp.DocumentOnTypeFormattingOptions?
  local otf_capabilities =
    vim.tbl_get(client.server_capabilities, 'documentOnTypeFormattingProvider')
  if not otf_capabilities then
    vim.notify(
      string.format(
        'Client with id %d does not support textDocument/onTypeFormatting requests',
        client_id
      ),
      vim.log.levels.WARN
    )
    return
  end

  -- Set on_key callback, clearing first in case it was already registered.
  vim.on_key(nil, type_formatting_ns)
  vim.on_key(on_key_cb, type_formatting_ns)

  -- Populate the buf handle data. We cannot use defaulttable here because then an empty table will
  -- be created for each unique keystroke
  local buf_handle = buf_handles[bufnr] or {}
  buf_handles[bufnr] = buf_handle

  local trigger = buf_handle[otf_capabilities.firstTriggerCharacter] or {}
  buf_handle[otf_capabilities.firstTriggerCharacter] = trigger
  trigger[client_id] = client

  for _, char in ipairs(otf_capabilities.moreTriggerCharacter or {}) do
    trigger = buf_handle[char] or {}
    buf_handle[char] = trigger
    trigger[client_id] = client
  end

  local group = api.nvim_create_augroup(get_buf_augroup(bufnr), { clear = true })
  api.nvim_create_autocmd('LspDetach', {
    buffer = bufnr,
    desc = 'Detach on-type formatting module when the client detaches',
    group = group,
    callback = function(args)
      detach_otf(args.data.client_id, bufnr)
    end,
  })
end

---@param enable boolean
---@param client vim.lsp.Client

Review comment (bug):
`get_buf_augroup` is only used once, why is it needed? there should be no need for buffer-specific augroups since you can delete per-buffer handlers for a given augroup without deleting them in other buffers (for the same augroup).

`nvim_clear_autocmds()` accepts a `buffer` arg. If that has a bug, it's still better to use `:au! ... <buffer>` instead of creating a bunch of specific augroups ?

(Note: the above doesnt apply to `get_client_augroup` since there are no client-specific augroups. (Though we really should allow user-defined "keys" in the future).)

Code after:
  ---@type lsp.DocumentOnTypeFormattingOptions
  local otf_capabilities =
    assert(vim.tbl_get(client.server_capabilities, 'documentOnTypeFormattingProvider'))

  -- Set on_key callback, clearing first in case it was already registered.
  vim.on_key(nil, ns)
  vim.on_key(on_key, ns)

  -- Populate the buf handle data. We cannot use defaulttable here because then an empty table will
  -- be created for each unique keystroke
  local buf_handle = buf_handles[bufnr] or {}
  buf_handles[bufnr] = buf_handle

  local trigger = buf_handle[otf_capabilities.firstTriggerCharacter] or {}
  buf_handle[otf_capabilities.firstTriggerCharacter] = trigger
  trigger[client_id] = client

  for _, char in ipairs(otf_capabilities.moreTriggerCharacter or {}) do
    trigger = buf_handle[char] or {}
    buf_handle[char] = trigger
    trigger[client_id] = client
  end

  api.nvim_clear_autocmds({ group = augroup, buffer = bufnr })
  api.nvim_create_autocmd('LspDetach', {
    buffer = bufnr,
    desc = 'Detach on-type formatting module when the client detaches',
    group = augroup,
    callback = function(args)
      local detached_client = assert(lsp.get_client_by_id(args.data.client_id))
      detach(detached_client, bufnr)
    end,
  })
end

api.nvim_create_autocmd('LspAttach', {
  desc = 'Enable on-type formatting for all buffers with individually-enabled clients.',
  callback = function(ev)
    local buf = ev.buf
    local client = assert(lsp.get_client_by_id(ev.data.client_id))
    if client._otf_enabled then
      attach(client, buf)
    end
  end,
})

---@param enable boolean
---@param client vim.lsp.Client
local function toggle_for_client(enable, client)
  local handler = enable and attach or detach