Using formatter.nvim and lsp format together

For most filetypes, I prefer to use formatters provided by formatter.nvim. But there are some exceptions, for which I would use lsp format.

To avoid having to manually decide and execute formatter.nvim or lsp format, we can define the decision logic within our formatter.nvim config and assign it to a singluar keybind.

This allows us to simply hit our keybind for formatting, and based on our predefined logic, the file will either be formatted with formatter.nvim or lsp format.

A basic config for formatter.nvim looks like this:

require("formatter").setup({
	logging = true,
	log_level = vim.log.levels.WARN,
	filetype = {
		-- Formatter configurations per filetype go here
		lua = {
			-- format with stylua
			require("formatter.filetypes.lua").stylua,
		},

		-- Define formatter configurations for all filetypes
		["*"] = {
			-- "formatter.filetypes.any" defines default configurations for any
			-- filetype
			require("formatter.filetypes.any").remove_trailing_whitespace,
		},
	},
})

The special "*" filetype, will be run on every call to :Format (the command that runs formatter.nvim).

We can therefore define a function within ["*"] which checks whether we have assigned a formatter to a filetype in formatter.nvim, and if we haven’t, to execute vim.lsp.buf.format() instead.

-- ...

["*"] = {
    function()
        local formatters = require("formatter.util").get_available_formatters_for_ft(vim.bo.filetype)
        if #formatters > 0 then
            print("formatted with formatter.nvim")
            return
        end
        -- check if there are any LSP formatters
        local lsp_clients = vim.lsp.buf_get_clients()
        for _, client in pairs(lsp_clients) do
            if client.server_capabilities.document_formatting then
                print("formatted with lsp")
                vim.lsp.buf.format()
                return
            end
        end
        print("no formatters found from formatter.nvim OR lsp")
    end,
},

All together it looks like this:

local bufopts = { noremap = true, silent = true }
vim.keymap.set({ "v", "n" }, "<leader>f", ":Format<cr>", bufopts)

require("formatter").setup({
	logging = true,
	log_level = vim.log.levels.WARN,
	filetype = {
		lua = {
			require("formatter.filetypes.lua").stylua,
		},

		["*"] = {
			require("formatter.filetypes.any").remove_trailing_whitespace,
			function()
				local formatters = require("formatter.util").get_available_formatters_for_ft(vim.bo.filetype)
				if #formatters > 0 then
					print("formatted with formatter.nvim")
					return
				end
				local lsp_clients = vim.lsp.buf_get_clients()
				for _, client in pairs(lsp_clients) do
					if client.server_capabilities.document_formatting then
						print("formatted with lsp")
						vim.lsp.buf.format()
						return
					end
				end
				print("no formatters found from formatter.nvim OR lsp")
			end,
		},
	},
})

With this setup, if we run :Format on a lua file, we will format with formatter.nvim using stylua. If we wanted instead to format our lua files with lua-language-server (a lsp for lua), we can simply remove the config for lua filetypes:

-- ...
	filetype = {
		-- lua = {
		-- 	require("formatter.filetypes.lua").stylua,
		-- },

		["*"] = {
			require("formatter.filetypes.any").remove_trailing_whitespace,
			function()
                -- code below unchanged


For more context you can check the solution source here in Github issues.