Home

Awesome

conform.nvim

Lightweight yet powerful formatter plugin for Neovim

<!-- TOC --> <!-- /TOC -->

Requirements

Features

Installation

conform.nvim supports all the usual plugin managers

<details> <summary>lazy.nvim</summary>
{
  'stevearc/conform.nvim',
  opts = {},
}

For a more thorough configuration involving lazy-loading, see Lazy loading with lazy.nvim.

</details> <details> <summary>Packer</summary>
require("packer").startup(function()
  use({
    "stevearc/conform.nvim",
    config = function()
      require("conform").setup()
    end,
  })
end)
</details> <details> <summary>Paq</summary>
require("paq")({
  { "stevearc/conform.nvim" },
})
</details> <details> <summary>vim-plug</summary>
Plug 'stevearc/conform.nvim'
</details> <details> <summary>dein</summary>
call dein#add('stevearc/conform.nvim')
</details> <details> <summary>Pathogen</summary>
git clone --depth=1 https://github.com/stevearc/conform.nvim.git ~/.vim/bundle/
</details> <details> <summary>Neovim native package</summary>
git clone --depth=1 https://github.com/stevearc/conform.nvim.git \
  "${XDG_DATA_HOME:-$HOME/.local/share}"/nvim/site/pack/conform/start/conform.nvim
</details>

Setup

At a minimum, you will need to set up some formatters by filetype

require("conform").setup({
  formatters_by_ft = {
    lua = { "stylua" },
    -- Conform will run multiple formatters sequentially
    python = { "isort", "black" },
    -- Use a sub-list to run only the first available formatter
    javascript = { { "prettierd", "prettier" } },
  },
})

Then you can use conform.format() just like you would vim.lsp.buf.format(). For example, to format on save:

vim.api.nvim_create_autocmd("BufWritePre", {
  pattern = "*",
  callback = function(args)
    require("conform").format({ bufnr = args.buf })
  end,
})

As a shortcut, conform will optionally set up this format-on-save autocmd for you

require("conform").setup({
  format_on_save = {
    -- These options will be passed to conform.format()
    timeout_ms = 500,
    lsp_format = "fallback",
  },
})

See conform.format() for more details about the parameters.

Conform also provides a formatexpr, same as the LSP client:

vim.o.formatexpr = "v:lua.require'conform'.formatexpr()"

To view configured and available formatters, as well as to see the log file, run :ConformInfo

Formatters

You can view this list in vim with :help conform-formatters

<details> <summary>Expand to see all formatters</summary> <!-- FORMATTERS --> <!-- /FORMATTERS --> </details>

Customizing formatters

You can override/add to the default values of formatters

require("conform").setup({
  formatters = {
    yamlfix = {
      -- Change where to find the command
      command = "local/path/yamlfix",
      -- Adds environment args to the yamlfix formatter
      env = {
        YAMLFIX_SEQUENCE_STYLE = "block_style",
      },
    },
  },
})

-- These can also be set directly
require("conform").formatters.yamlfix = {
  env = {
    YAMLFIX_SEQUENCE_STYLE = "block_style",
  },
}

-- This can also be a function that returns the config,
-- which can be useful if you're doing lazy loading
require("conform").formatters.yamlfix = function(bufnr)
  return {
    command = require("conform.util").find_executable({
      "local/path/yamlfix",
    }, "yamlfix"),
  }
end

In addition to being able to override any of the original properties on the formatter, there is another property for easily adding additional arguments to the format command

require("conform").formatters.shfmt = {
  prepend_args = { "-i", "2" },
  -- The base args are { "-filename", "$FILENAME" } so the final args will be
  -- { "-i", "2", "-filename", "$FILENAME" }
}
-- prepend_args can be a function, just like args
require("conform").formatters.shfmt = {
  prepend_args = function(self, ctx)
    return { "-i", "2" }
  end,
}

If you want to overwrite the entire formatter definition and not merge with the default values, pass inherit = false. This is also the default behavior if there is no built-in formatter with the given name, which can be used to add your own custom formatters.

require("conform").formatters.shfmt = {
  inherit = false,
  command = "shfmt",
  args = { "-i", "2", "-filename", "$FILENAME" },
}

Recipes

<!-- RECIPES --> <!-- /RECIPES -->

Advanced topics

<!-- ADVANCED --> <!-- /ADVANCED -->

Options

A complete list of all configuration options

<!-- OPTIONS -->
require("conform").setup({
  -- Map of filetype to formatters
  formatters_by_ft = {
    lua = { "stylua" },
    -- Conform will run multiple formatters sequentially
    go = { "goimports", "gofmt" },
    -- Use a sub-list to run only the first available formatter
    javascript = { { "prettierd", "prettier" } },
    -- You can use a function here to determine the formatters dynamically
    python = function(bufnr)
      if require("conform").get_formatter_info("ruff_format", bufnr).available then
        return { "ruff_format" }
      else
        return { "isort", "black" }
      end
    end,
    -- Use the "*" filetype to run formatters on all filetypes.
    ["*"] = { "codespell" },
    -- Use the "_" filetype to run formatters on filetypes that don't
    -- have other formatters configured.
    ["_"] = { "trim_whitespace" },
  },
  -- If this is set, Conform will run the formatter on save.
  -- It will pass the table to conform.format().
  -- This can also be a function that returns the table.
  format_on_save = {
    -- I recommend these options. See :help conform.format for details.
    lsp_format = "fallback",
    timeout_ms = 500,
  },
  -- If this is set, Conform will run the formatter asynchronously after save.
  -- It will pass the table to conform.format().
  -- This can also be a function that returns the table.
  format_after_save = {
    lsp_format = "fallback",
  },
  -- Set the log level. Use `:ConformInfo` to see the location of the log file.
  log_level = vim.log.levels.ERROR,
  -- Conform will notify you when a formatter errors
  notify_on_error = true,
  -- Custom formatters and overrides for built-in formatters
  formatters = {
    my_formatter = {
      -- This can be a string or a function that returns a string.
      -- When defining a new formatter, this is the only field that is required
      command = "my_cmd",
      -- A list of strings, or a function that returns a list of strings
      -- Return a single string instead of a list to run the command in a shell
      args = { "--stdin-from-filename", "$FILENAME" },
      -- If the formatter supports range formatting, create the range arguments here
      range_args = function(self, ctx)
        return { "--line-start", ctx.range.start[1], "--line-end", ctx.range["end"][1] }
      end,
      -- Send file contents to stdin, read new contents from stdout (default true)
      -- When false, will create a temp file (will appear in "$FILENAME" args). The temp
      -- file is assumed to be modified in-place by the format command.
      stdin = true,
      -- A function that calculates the directory to run the command in
      cwd = require("conform.util").root_file({ ".editorconfig", "package.json" }),
      -- When cwd is not found, don't run the formatter (default false)
      require_cwd = true,
      -- When stdin=false, use this template to generate the temporary file that gets formatted
      tmpfile_format = ".conform.$RANDOM.$FILENAME",
      -- When returns false, the formatter will not be used
      condition = function(self, ctx)
        return vim.fs.basename(ctx.filename) ~= "README.md"
      end,
      -- Exit codes that indicate success (default { 0 })
      exit_codes = { 0, 1 },
      -- Environment variables. This can also be a function that returns a table.
      env = {
        VAR = "value",
      },
      -- Set to false to disable merging the config with the base definition
      inherit = true,
      -- When inherit = true, add these additional arguments to the beginning of the command.
      -- When inherit = true, add these additional arguments to the command.
      -- This can also be a function, like args
      prepend_args = { "--use-tabs" },
      -- When inherit = true, add these additional arguments to the end of the command.
      -- This can also be a function, like args
      append_args = { "--trailing-comma" },
    },
    -- These can also be a function that returns the formatter
    other_formatter = function(bufnr)
      return {
        command = "my_cmd",
      }
    end,
  },
})

-- You can set formatters_by_ft and formatters directly
require("conform").formatters_by_ft.lua = { "stylua" }
require("conform").formatters.my_formatter = {
  command = "my_cmd",
}
<!-- /OPTIONS -->

Formatter options

<!-- FORMATTER_OPTIONS --> <!-- /FORMATTER_OPTIONS -->

API

<!-- API -->

setup(opts)

setup(opts)

ParamTypeDesc
optsnil|conform.setupOpts
formatters_by_ftnil|table<string, conform.FiletypeFormatter>Map of filetype to formatters
format_on_savenil|conform.FormatOpts|fun(bufnr: integer): nil|conform.FormatOptsIf this is set, Conform will run the formatter on save. It will pass the table to conform.format(). This can also be a function that returns the table.
format_after_savenil|conform.FormatOpts|fun(bufnr: integer): nil|conform.FormatOptsIf this is set, Conform will run the formatter asynchronously after save. It will pass the table to conform.format(). This can also be a function that returns the table.
log_levelnil|integerSet the log level (e.g. vim.log.levels.DEBUG). Use :ConformInfo to see the location of the log file.
notify_on_errornil|booleanConform will notify you when a formatter errors (default true).
formattersnil|table<string, conform.FormatterConfigOverride|fun(bufnr: integer): nil|conform.FormatterConfigOverride>Custom formatters and overrides for built-in formatters.

format(opts, callback)

format(opts, callback): boolean
Format a buffer

ParamTypeDesc
optsnil|conform.FormatOpts
timeout_msnil|integerTime in milliseconds to block for formatting. Defaults to 1000. No effect if async = true.
bufnrnil|integerFormat this buffer (default 0)
asyncnil|booleanIf true the method won't block. Defaults to false. If the buffer is modified before the formatter completes, the formatting will be discarded.
dry_runnil|booleanIf true don't apply formatting changes to the buffer
formattersnil|string[]List of formatters to run. Defaults to all formatters for the buffer filetype.
lsp_formatnil|conform.LspFormatOptsConfigure if and when LSP should be used for formatting. Defaults to "never".
> "never"never use the LSP for formatting (default)
> "fallback"LSP formatting is used when no other formatters are available
> "prefer"use only LSP formatting when available
> "first"LSP formatting is used when available and then other formatters
> "last"other formatters are used then LSP formatting when available
quietnil|booleanDon't show any notifications for warnings or failures. Defaults to false.
rangenil|tableRange to format. Table must contain start and end keys with {row, col} tuples using (1,0) indexing. Defaults to current selection in visual mode
idnil|integerPassed to vim.lsp.buf.format when using LSP formatting
namenil|stringPassed to vim.lsp.buf.format when using LSP formatting
filternil|fun(client: table): booleanPassed to vim.lsp.buf.format when using LSP formatting
callbacknil|fun(err: nil|string, did_edit: nil|boolean)Called once formatting has completed

Returns:

TypeDesc
booleanTrue if any formatters were attempted

list_formatters(bufnr)

list_formatters(bufnr): conform.FormatterInfo[]
Retrieve the available formatters for a buffer

ParamTypeDesc
bufnrnil|integer

list_all_formatters()

list_all_formatters(): conform.FormatterInfo[]
List information about all filetype-configured formatters

get_formatter_info(formatter, bufnr)

get_formatter_info(formatter, bufnr): conform.FormatterInfo
Get information about a formatter (including availability)

ParamTypeDesc
formatterstringThe name of the formatter
bufnrnil|integer

will_fallback_lsp(options)

will_fallback_lsp(options): boolean
Check if the buffer will use LSP formatting when lsp_format = "fallback"

ParamTypeDesc
optionsnil|tableOptions passed to vim.lsp.buf.format
<!-- /API -->

Acknowledgements

Thanks to