I’m slowly learning Rust, and as usual I’m using Vim to write my code. I’m using rust.vim for syntax highlighting, and vim-lsp and rust-analyzer for Language Server Protocol support.

As I wrote more code I felt that saving my source file was getting slower, sometimes taking up to 10 seconds! Eventually this got annoying enough that I used Vim’s profiling support (:help profiling) to identify the problematic code:

:profile start profile.log
:profile file *
:profile func *
:w
:profile stop
:q

The resulting profile.log was 13360 lines long! I looked for the functions taking the longest time with:

rg 'Total time' profile.log | sort -k 3n
...
Total time:   0.117917000
Total time:   0.122267000
Total time:   0.281978000
Total time:   0.376979000
Total time:  11.539883000
Total time:  11.540752000

Clearly one function is a real problem. I searched for Total time: 11.540752000, which brought me to a trace where the only long running call was:

    1  11.540633000   0.000257000     call s:RunRustfmt(s:RustfmtCommand(), '', v:true)

This tells me that the problematic function is autoformatting the file, and when I tested by disabling autoformatting saving was almost instantaneous.

Searching for Total time: 11.539883000 brought me to a longer trace containing:

    1                11.433683000         call setline(1, l:content)

setline() is a Vim function that changes the content of the file. Something is reacting to the file contents being changed, but what? Searching for vim setline very slow found some StackOverflow posts asking about setline() and vim-lsp interacting badly. Disabling vim-lsp made autoformatting very quick, confirming that setline() interacts badly with vim-lsp. The hypothesised problem is that vim-lsp keeps feeding greater and greater prefixes of the file to the LSP server, explaining why it slows down as the file grows.

I find LSP valuable, and autoformat valuable, so I wanted to find a way to keep both. I read through the vim-lsp documentation looking for an option to help with this, e.g. waiting longer between updates to the LSP server. Instead I found an alternative way to implement autoformat! vim-lsp provides a command, LspDocumentFormatSync, that formats the buffer using the LSP server. I disabled vim-rust’s autoformat support, and added an autocmd to autoformat the buffer contents before writing it.

let g:rustfmt_autosave = 0
autocmd BufWritePre *.rs LspDocumentFormatSync

This gives me autoformat and vim-lsp, without any slowdown :)