Post

Neovim Auto-Format (conform.nvim) & Auto-Save (auto-save.nvim) Masterclass You didn't Know you Need

Do you come from Obsidian for taking notes and are used to auto-save, you don't know what auto-format is and how it can benefit you?

Neovim Auto-Format (conform.nvim) & Auto-Save (auto-save.nvim) Masterclass You didn't Know you Need

Contents

Table of contents

YouTube video

Pre-requisites


  • If you don’t want to watch the video above, and you already have your own neovim config, and want to quickly get started and follow along in this tutorial
  • Run the git clone commands below to clone my dotfiles in your .config directory and we will run my config below
1
2
mkdir -p ~/.config
git clone git@github.com:linkarzu/dotfiles-latest ~/.config/linkarzu/dotfiles-latest
  • Open the newly downloaded neobean config with:
1
NVIM_APPNAME=linkarzu/dotfiles-latest/neovim/neobean nvim
  • You can create an alias in your .bashrc or .zshrc file to run my config
1
alias neobean='NVIM_APPNAME=linkarzu/dotfiles-latest/neovim/neobean nvim'
  • Then to run this config, just run neobean


  • If you don’t even have neovim yet, of course you will need to install it first, so if you’re just getting started, I have a video for you:

If you like this, and want to support me

  • I create and edit my videos in an M1 mac mini, and it’s starting to stay behind in the editing side of things, tends to slow me down a bit, I’d like to upgrade the machine I use for all my videos to a mac mini with these specs:
    • Apple M4 Pro chip with 14‑core CPU, 20‑core GPU, 16-core Neural Engine
    • 24GB unified memory
    • 1TB SSD storage
    • 10 Gigabit Ethernet
  • If you want to help me reach my goal, you can donate here

Image

Discord server

  • After following this guide or even watching the related video, you:
    • Have questions related to one of the tools, configs or scripts that I use
    • Would like me to expand a bit more on how something is done
    • Or simply would like to talk and meet other community members that share your same interests
  • join the discord server in this link
  • Access to the discord server is only for YouTube community members

I’m opening the discord channel to the public, I think that for at least 3 months, I’m only accepting a few members at the moment, to get used to things and manage it, so if you want to join, please reach out via one of my social media links below and I’ll add you

Image

Follow me on social media

  • The following links will be in the YouTube video description:
    • Each one of the videos shown
    • A link to this blogpost

How do you manage your passwords?

Image

Auto format

Why do I auto format

  • I demo auto-format in the video, but if you want to have a visual representation on what auto-format does
  • auto format turns this:

Image auto format not applied YET as I’m in insert mode

  • Into this without you doing anything
  • In my case when I go from insert mode to normal mode:
    • auto-save will kick in after 2 seconds
    • that auto-save will trigger auto-format

Image auto format after auto save triggered by normal mode

  • This is very important for you to keep in mind related to my config
  • Every time I auto-save a file, it is auto-formatted
  • This is configurable and can be disabled, you can auto-save but not auto-format, and then format when you desire

  • I like auto-formatting because it keeps my markdown documents on point and uniform, also if editing lua files usually related to my neovim config, auto-formatting helps me quickly see if I made a mistake and keep indentation uniform
  • I deal with a lot of .yml files at work (kubernetes related) and auto-formatting also helps me make less mistakes and keep things uniform
  • In the section below I’ll demo how I use auto-formatting

Auto-format features and demo

  • I use the LazyVim distro, and I basically use the default config with a few tweaks
  • I want to auto-format when the focus is lost or I leave a buffer
  • DEMO FocusLost used when switching from skitty-notes
  • DEMO BufLeave is used when switching between 2 buffers
  • To see what formatter is applied to a file, run
1
:ConformInfo
  • What auto-formats my markdown .md files is prettier
  • What auto-formats my markdown .yml files is prettier
    • See this one for example ~/github/dotfiles-latest/alacritty/themes/dracula/dracula.yml
  • What auto-formats my markdown .lua files is stylua
    • See this one for example ~/github/dotfiles-latest/neovim/neobean/lua/plugins/render-markdown.lua
    • That stylua config comes from the lazyvim distro see here

  • I install prettier as a :LazyExtra in the lazyvim distro
  • I install it through ~/github/dotfiles-latest/neovim/neobean/lua/config/lazy.lua
  • It basically installs prettier through Mason, you can see that here

conform.nvim plugin

  • stevearc/conform.nvim
  • This is the plugin used for formatting, you can configure it to use different formatters for different file types
  • I use this plugin with the default config, I make a small change and add this autocmd to the top of my plugin config file
  • This is what I demoed above, format when FocusLost and BufLeave
  • Notice that I have a condition:
    • DEMO Only format if the current mode is normal mode
    • DEMO Only format if autoformat is enabled for the buffer or globally
    • DEMO In the LazyVim distro run :LazyFormatInfo to see the status of formatting
    • Also you can use the keymaps to toggle formatting:
      • <leader>uf - Toggle auto format (Global)
      • <leader>uF - Toggle auto format (Buffer)
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
27
-- Auto-format when focus is lost or I leave the buffer
-- Useful if on skitty-notes or a regular buffer and switch somewhere else the
-- formatting doesn't stay all messed up
-- I found this autocmd example in the readme
-- https://github.com/stevearc/conform.nvim/blob/master/README.md#setup
-- "FocusLost" used when switching from skitty-notes
-- "BufLeave" is used when switching between 2 buffers
vim.api.nvim_create_autocmd({ "FocusLost", "BufLeave" }, {
  pattern = "*",
  callback = function(args)
    local buf = args.buf or vim.api.nvim_get_current_buf()
    -- Only format if the current mode is normal mode
    -- Only format if autoformat is enabled for the current buffer (if
    -- autoformat disabled globally the buffers inherits it, see :LazyFormatInfo)
    if LazyVim.format.enabled(buf) and vim.fn.mode() == "n" then
      -- Add a small delay to the formatting so it doesn’t interfere with
      -- CopilotChat’s or grug-far buffer initialization, this helps me to not
      -- get errors when using the "BufLeave" event above, if not using
      -- "BufLeave" the delay is not needed
      vim.defer_fn(function()
        if vim.api.nvim_buf_is_valid(buf) then
          require("conform").format({ bufnr = buf })
        end
      end, 100)
    end
  end,
})
  • My config can be found in my dotfiles in plugins/conform.lua
  • The file is
    • ~/github/dotfiles-latest/neovim/neobean/lua/plugins/conform.lua
  • ⭐⭐⭐⭐ Remember to star my dotfiles ⭐⭐⭐⭐

Line wrapping at 80 characters

  • I don’t like my lines to be longer than 80 characters, helps me with readability and overall consistency of my files.
  • If I open an obsidian markdown file in neovim, line lengths are all over the place, so I prefer to follow the markdown guidelines.
  • Set the option vim.opt.textwidth = 80 in ~/github/dotfiles-latest/neovim/neobean/lua/config/options.lua
  • When text reaches this textwidth limit, it automatically wraps to the next line.
  • This will automatically switch to the line below as you are typing and reach the 80 characters
  • This will NOT auto wrap:
    • Existing lines in a document after you enable the option
    • Long lines that you paste into a file
    • This is handled by prettier as seen below

Configure prettier

  • Remember that prettier is one of the formatters that conform.nvim uses, if you use a different formatter for a different type of file, and want to update the formatting rules, you have to update that other formatter.
  • In this video I will quickly show you how it’s done for prettier so you can get the idea

ProseWrap

  • This will autoformat existing lines over 80 characters and also long lines that you paste that exceed the 80 characters
  • We configure it in the .prettierrc.yaml file, see below

.prettierrc.yaml

  • Remember that I install prettier as a :LazyExtra
  • This is the file in which you configure the different formatting rules for prettier, not only for markdown but for other file types
  • This is where I enable ProseWrap
  • I add the ~/github/dotfiles-latest/.prettierrc.yaml file, to my $HOME directory
  • I keep the file in my dotfiles and create a symlink in my home directory that points to the .prettierrc.yaml file
  • My .prettierrc.yaml is here in my dots -> main/.prettierrc.yaml

  • Source for the text below
  • The configuration file will be resolved starting from the location of the file being formatted, and searching up the file tree until a config file is (or isn’t) found.
  • Prettier intentionally doesn’t support any kind of global configuration. This is to make sure that when a project is copied to another computer, Prettier’s behavior stays the same. Otherwise, Prettier wouldn’t be able to guarantee that everybody in a team gets the same consistent results.

Disable autoformatting in certain sections

  • Prettier is enabled and will autoformat your file, but there are some times that you don’t need prettier to autoformat, example below:
    • Notice that I’m also disabling markdownlint
    • DEMO so you see what happens when I remove this

This is a message that renders correctly in the page because it’s not autoformatted

Paste unformatted text

  • After enabling this formatting for markdown files, since lines are broken at 80 characters, you’re going to notice that when you copy text from a markdown file into slack, all the lines are going to be broken, and your messages will be formatted like crap, and we don’t want that
  • I shared this solution in the neovim subreddit but it was too advanced for MFs to understand, so they didn’t get it, but if you follow my config, you’ll understand what I’m talking about
  • I came up with a solution and I go over this solution in detail in this video:

Auto save

Why do I auto save?

  • I come from Obsidian for taking notes, and writing markdown files is something I do a lot in Neovim, saving very often is too tiring and just not needed, I haven’t had to type alt+w, or ctrl+s or :wq in quite a long time
  • But before auto-save I remember writing very often, it was some sort of a tick, so I just said “Why bother?”
  • auto-save is really controversial, I think there’s more people against it than people that actually use it
  • Look at this reddit post I created around 8 months ago Save files automatically with auto-save.nvim (plugin not mine)
  • A point brought up by one of the plugin’s maintainers in the reddit post was:
    • If you are tracking the current project in github, why bother saving?
      • And this makes sense for me, my final save location for a file I’m working on, is GitHub
      • This blogpost article I’m writing right now ends up in GitHub and if I need to revert, I revert a git commit, so I really don’t care if saves are performed often by a plugin, as long as I don’t need to do the saves myself
      • DEMO fuck up the article and discard github changes

Auto-save features and demo

  • The auto-save plugin allows me to customize several things

Events that trigger an immediate save

  • This means that if my focus is lost, for example, I switch from the skitty-notes app on the right to my Ghostty terminal on the left, this means an immediate save, or if I switch to another buffer
1
immediate_save = { "BufLeave", "FocusLost", "QuitPre", "VimSuspend" }, -- vim events that trigger an immediate save
  • For example, I want to avoid this immediate_save during certain conditions
  • One of them, which is really important for me, is do not run an immediate_save if I’m on insert mode, so even if the FocusLost event happens, that won’t save
  • We will demo this in the Set a condition on when or not to save section
  • For now, all you need to know, is that this can be configured too

Events that trigger a defer save

  • Save after certain amount of time when something was changed in the file
  • In other words, it saves after the debounce_delay, I have it set to 2 seconds and I’ll explain why later
  • If I exit insert mode, is because I finished typing something, so if I jump to normal mode, it will wait the debounce_delay time before saving, if I perform another change before the debounce_delay completes, the timer will restart
  • This avoids saving way too many times
  • In the video I demo how I don’t save when:
    • In visual mode
    • In flash
    • Why I don’t save when in these?
      • Because I usually go into these 2 modes when I’m in the middle of editing text
      • If I jump into visual mode I usually will delete text or move it to another section
      • If I jump into flash is because I want to jump to a section and quickly keep editing text
      • I don’t want an auto-save to trigger in the middle of that process, because that will trigger an auto-format and disrupt my workflow minimally

1
2
3
4
5
6
7
-- vim events that trigger a deferred save (saves after `debounce_delay`)
defer_save = {
  "InsertLeave",
  "TextChanged",
  { "User", pattern = "VisualLeave" },
  { "User", pattern = "FlashJumpEnd" },
},

Events that cancel a defer save

  • If a save was going to take place after the debounce_delay and one of these events happen, the save is canceled
  • This is logical, because if I’m in insert mode, it means I’m editing the file, and I don’t want to save
1
2
3
4
5
cancel_deferred_save = {
  "InsertEnter",
  { "User", pattern = "VisualEnter" },
  { "User", pattern = "FlashJumpStart" },
},

Set a condition on when or not to save

  • I use this condition because I don’t want to save when I’m in insert mode and I switch to another application
  • For example, let’s say I’m watching a Udemy course and taking notes, sometimes I stay in insert mode and switch to the video, then come back to neovim and this will auto-save because of the FocusLost -> immediate_save event
  • DEMO in video
1
2
3
4
5
6
7
8
9
-- Do not save when I'm in insert mode
-- Do NOT ADD VISUAL MODE HERE or the cancel_deferred_save wont' work
-- If I STAY in insert mode and switch to another app, like YouTube to
-- take notes, the BufLeave or FocusLost immediate_save will be ignored
-- and the save will not be triggered
local mode = vim.fn.mode()
if mode == "i" then
  return false
end
  • Do not save when in Harpoon or if the filetype is mysql (dadbod)
  • DEMO in video
1
2
3
4
5
6
7
8
9
10
11
-- Disable auto-save for the harpoon plugin, otherwise it just opens and closes
-- https://github.com/ThePrimeagen/harpoon/issues/434
--
-- don't save for `sql` file types
-- I do this so when working with dadbod the file is not saved every time
-- I make a change, and a SQL query executed
-- Run `:set filetype?` on a dadbod query to make sure of the filetype
local filetype = vim.bo[buf].filetype
if filetype == "harpoon" or filetype == "mysql" then
  return false
end
  • I do not want to save when in an active snippet
  • I use luasnip for my snippets, and blink as my completion engine, so when I insert a snippet that requires me to jump through different items, I don’t want an auto-save to happen
  • DEMO in video
1
2
3
if require("luasnip").in_snippet() then
  return false
end

Don’t auto-format after auto-save

  • If for example, you do not want to trigger auto-format when you save, you can set the option
1
noautocmd = false,

debounce_delay

  • Delay after which a pending save is executed (default 1000)
  • I used this with a value of 750 for a long time, the problem is that I also auto-format when I save, so my file was being formatted way to often
  • A lot of times I jump out if insert mode just to navigate somewhere else, and keep editing, if set to 750 you won’t have enough time to move somewhere else, so a save and format will happen, adding a small delay while the file is formatted
  • I wanted to avoid this, so I increased it to 2,000, for me that’s the sweet spot as of Jan 25th 2025, I used to have it set to 3,000 but it felt like too much, I also tried 5,000 and didn’t like it, too much
  • I still need my auto-formatting to happen, but not as quick

auto-save.nvim plugin

Be really careful in choosing the correct plugin, because the one I’m currently using is a fork of Pocco81/auto-save.nvim and this Pocco81 plugin hasn’t been maintained in years, so it causes issues when you try to undo and redo

  • You will be able to find my configuration in my dotfiles plugins/auto-save.lua
  • The file is:
    • ~/github/dotfiles-latest/neovim/neobean/lua/plugins/auto-save.lua
  • ⭐⭐⭐⭐ Remember to star my dotfiles ⭐⭐⭐⭐

What do you think about autosave and autoformat?

  • Do you like them?
  • Do you hate them?
  • You use them?
  • I’d like to know what people think about these features, so let me know in the YouTube comments
  • Also, remember to Join the discord channel to troubleshoot, talk to others, offer help or just hang out
This post is licensed under CC BY 4.0 by the author.