brenton-leighton / multiple-cursors.nvim

A multi-cursor plugin for Neovim that works in normal, insert/replace, or visual modes, and with almost every command
Apache License 2.0
167 stars 4 forks source link

fix: use byte col to call extmarks api #14

Closed mrbeardad closed 7 months ago

mrbeardad commented 8 months ago

Neovim use 3 kinds of column:

  1. Byte column, 0-based index, this is used for most of nvim.api, include extmarks api
  2. Character column, 1-based index, this is used for cursor left/right move, and is used in this plugin
  3. Virtual column, 1-based index, this is used for cursor up/down move, curswant is a virtual column but not a character column

For example,

1234567
abc你好

has 3 bytes, 1 chracter and 2 display width. So

In this pr, I've convert all vc.col into byte column when call extmarks api. However, there are too many vc.curswant = vc.col, so I make the pr to discuss the solution for it.

brenton-leighton commented 8 months ago

There's another function, [getcurpos()](https://neovim.io/doc/user/builtin.html#getcurpos()), that gives the byte index for the cursor. Would it solve the problem to use that instead (as well as cursor() instead of setcursorcharpos())? I used getcursorcharpos() because I thought it would handle extended characters, but maybe I have it backwards.

mrbeardad commented 8 months ago

No. The problem about column for extmarks api has been solved with this pr, the remaining problem is, curswant is a virtual column, it is relative with character display width, and affect up/down motion. All vc.curswant = vc.col is wrong, it needs to be converted first.

local text = vim.fn.strcharpart(vim.fn.getline(lnum), 0, col)
local virtcol = vim.fn.strdisplaywidth(text)
mrbeardad commented 8 months ago
local text = vim.fn.strcharpart(vim.fn.getline(lnum), 0, col)
local virtcol = vim.fn.strdisplaywidth(text)

However, the behavior is slightly different from the builtin motion, becase when cursor at a character that has 2-char display width, the curswant can be either the first char or the second char, but the code above always set curswant to the second-char position

12
你

The curswant of cursor that on is 1 if it has moved down from 1, or 2 from 2. so always recover vc.curswant from vc.col is not a good idea

brenton-leighton commented 7 months ago

Does this branch fix the position issues? I've changed all the position functions to use the byte count versions. I think there's still a problem with the move right command, because this is implemented manually. But otherwise, is it just a problem with the extmarks?

mrbeardad commented 7 months ago

image

The display width needs to be considered when set end_col, check this

mrbeardad commented 7 months ago

Left/Right motion use character column, up/down motion use virtual column(display width). Why don't use normal command to move cursor? Thus, user setting could be considered automatically, such as whichwrap,

brenton-leighton commented 7 months ago

Left/Right motion use character column, up/down motion use virtual column(display width).

What do you mean by virtual column? The curswant value?

Why don't use normal command to move cursor?

Originally I tried using virtualedit = "onemore" for insert mode but it didn't seem to work. I thought it was because setting the option wasn't "api-fast" (so it would occur in the event loop like feedkeys). I think I realise now that the issue is that the $ command doesn't work in insert mode even with virtualedit="onemore.

Thus, user setting could be considered automatically, such as whichwrap,

I'll try changing things to use "onemore" and whichwrap

mrbeardad commented 7 months ago

What do you mean by virtual column? The curswant value?

Virtual column is the last screen position occupied by the character at that position, different unicode character maybe has different display width. For example, the display width of a is 1, and is 2, so a occupies 1 screen column, occupies 2 screen column. The virtual column of a is 1 and is 5.

1234567
abc你好

Assuming the cursor is at , the curswant of the cursor is 6, the virtual column of is 7. Then you moved the cursor up and it was placed on the 6 where the virtual colmun equal to the curswant.

[:h virtcol()](https://neovim.io/doc/user/builtin.html#virtcol())