fannheyward / coc-rust-analyzer

rust-analyzer extension for coc.nvim
MIT License
1.13k stars 39 forks source link

Range formatting doesn't appear to work #1125

Open bombela opened 1 year ago

bombela commented 1 year ago

What's the output of :CocInfo

    3 vim version: NVIM v0.8.1
    4 node version: v18.12.1
    5 coc.nvim version: 0.0.82-b7375d5f 2023-01-30 05:09:03 +0800
    6 coc.nvim directory: /home/bombela/.config/nvim/plugged/coc.nvim
    7 term: xterm-256color
    8 platform: linux

What's the output of :CocCommand rust-analyzer.serverVersion

[coc.nvim] rust-analyzer 0.4.1442-standalone

What's your coc-rust-analyzer version? You can get it from :CocList extensions

coc-rust-analyzer 0.72.1

In my coc-config I also have:

    3   "rust-analyzer.updates.checkOnStartup": "false",
    4   "rust-analyzer.rustfmt.enableRangeFormatting": true,
    5   "rust-analyzer.updates.channel": "nightly",

And :echo CocHasProvider('formatRange') returns v:true.

(by the way, "rust-analyzer.rustfmt.enableRangeFormatting": true enables the feature but "rust-analyzer": { "rustfmt.enableRangeFormatting": true } does not. Took me 45minutes to realize that).

Then I do :set formatexpr=CocAction('formatSelected'). Now I expect gq on a visual selection to format similar to RustFmt. Instead it formats the same as the default vim behavior. :CoInfo reports nothing of interest besides Request action: formatSelected [].

As a baseline, I can compare with rustfmt nightly and :'<,'>RustFmtRange.

Just to make sure things are happening at all, I can :set formatexpr=CocAction('wontwork'), and in this case it silently doesn't format anything. And :CoInfo reports Request error: wontwork [] Error: Action "wontwork" not exist.

I can also :set formatexpr=CocAction('format'), which does indeed format the whole file like RustFmt, confirming again that something is really executed.

So it appears that CocAction('formatSelected') with coc-rust-analyzer somehow falls-back to the default neovim formatting. I am not sure how to debug the issue further.

fannheyward commented 1 year ago

:set formatexpr=CocAction('formatSelected')

Reproduced, fail to do formatting.

xmap <leader>f  <Plug>(coc-format-selected)
nmap <leader>f  <Plug>(coc-format-selected)

works as expected.

Will debug on the first one.

fannheyward commented 1 year ago

My usage with coc-format-selected, whole line or multiple lines:

  1. V to select whole line
  2. coc-format-selected
  3. the selected line will be formatted

with gq:

  1. V to select whole line, for example current line number is 21
  2. gq
  3. nothing happens

From coc, https://github.com/neoclide/coc.nvim/blob/94dc1051415eed1f0ffa39446ed22c63e3d8eb14/src/handler/format.ts#L197-L200

v:lnum,v:count,mode() returns 21, 0, n, coc stops to do formatting because count = 0.

another test, gggqG, v:lnum,v:count,mode() returns 1 0 n, coc stops cause count = 0.

Have no idea why vim returns v:count 0.

:h formatexpr document:

    The |v:lnum|  variable holds the first line to be formatted.
    The |v:count| variable holds the number of lines to be formatted.
bombela commented 1 year ago

Thank you for investigating! I confirm v:count is zero after running gq (over multiple lines). I guess when the format handler returns -1, neovim falls-back to the default?

I will see if I can debug some more. I wonder if vim behaves similar.

fannheyward commented 1 year ago

format handler returns -1, neovim falls-back to the default

:h formatexpr:

When the expression evaluates to non-zero Vim will fall back to using
the internal format mechanism.

v:count is zero after running gq (over multiple lines)

This is the key issue, have no idea why v:count is 0.

if vim behaves similar.

The same.

bombela commented 1 year ago
function! Test()
  echomsg "->" v:lnum v:count v:char mode()
  return 0
endfunction

:set formatexpr=Test()

v:lnum and v:count are both correct in the :messages output.

I don't really know how the async stuff in typescript/nodejs interacts with neovim. But I think what we observe is a race condition. The value of v:count is only valid during the synchronous execution of the expression in formatexp. After that, the selection is cleared, and v:count becomes zero.

fannheyward commented 1 year ago

@bombela set formatexpr=CocAction('formatSelected') works with clangd, but not rust-analyzer.

Both gq2j or V select two lines then gq, works as expected with clangd.