Skip to content
This repository has been archived by the owner on Aug 12, 2023. It is now read-only.

Formatting on save

Jose Alvarez edited this page May 1, 2022 · 13 revisions

Sync Formatting

This is the most reliable way to format on save, since it blocks Neovim until results are applied (or the source times out).

Neovim 0.7 and below

require("null-ls").setup({
    -- you can reuse a shared lspconfig on_attach callback here
    on_attach = function(client)
        if client.resolved_capabilities.document_formatting then
            vim.cmd([[
            augroup LspFormatting
                autocmd! * <buffer>
                autocmd BufWritePre <buffer> lua vim.lsp.buf.formatting_sync()
            augroup END
            ]])
        end
    end,
})

Async Formatting

Caveats (please read!)

Async formatting works by sending a formatting request, then applying and writing results once they're received. This lets you move the cursor, scroll, and otherwise interact with the window while waiting for results, which can make formatting seem more responsive (but doesn't actually speed it up).

The async formatting implementation here comes with the following caveats:

  • If you edit the buffer in between sending a request and receiving results, those results won't be applied.

  • Each save will result in writing the file to the disk twice.

  • :wq will not format the file before quitting.

The most reliable way to format files on save is to use a sync formatting method, as described above.

Code

NOTE: This snippet is not part of null-ls. If it doesn't work for you, please open a discussion (not an issue). If you want to solve a bug or improve its behavior, feel free to edit this page.

_G.formatting = function(bufnr)
    bufnr = tonumber(bufnr) or vim.api.nvim_get_current_buf()

    vim.lsp.buf_request(
        bufnr,
        "textDocument/formatting",
        { textDocument = { uri = vim.uri_from_bufnr(bufnr) } },
        function(err, res)
            if err then
                local err_msg = type(err) == "string" and err or err.message
                -- you can modify the log message / level (or ignore it completely)
                vim.notify("formatting: " .. err_msg, vim.log.levels.WARN)
                return
            end

            -- don't apply results if buffer is unloaded or has been modified
            if not vim.api.nvim_buf_is_loaded(bufnr) or vim.api.nvim_buf_get_option(bufnr, "modified") then
                return
            end

            if res then
                vim.lsp.util.apply_text_edits(res, bufnr)
                vim.api.nvim_buf_call(bufnr, function()
                    vim.cmd("silent noautocmd update")
                end)
            end
        end
    )
end

local null_ls = require("null-ls")

null_ls.setup({
    -- add your sources / config options here
    sources = ...,
    on_attach = function(client, bufnr)
        if client.supports_method("textDocument/formatting") then
            -- wrap in an augroup to prevent duplicate autocmds
            vim.cmd([[
            augroup LspFormatting
                autocmd! * <buffer>
                autocmd BufWritePost <buffer> lua formatting(vim.fn.expand("<abuf>"))
            augroup END
            ]])
        end
    end,
})
Clone this wiki locally