[vim/vim] `getpos()` reports different end columns upon second activation of `getposregion()` when using `virtualedit=all` (Issue #16276)

8 views
Skip to first unread message

mcauley-penney

unread,
Dec 21, 2024, 3:00:11 PM12/21/24
to vim/vim, Subscribed

Steps to reproduce

  1. Open vim with the below config. This config affords reporting the output of getregionpos() on entering charwise, linewise, and blockwise visual, and affords reporting getpos('.') on mp.
set noshowmode
set virtualedit=all
set noruler

" getregionpos
nnoremap gp :echom "== getpos() results == " . string(getpos('.'))<CR>

function! ReportGetRegionPos()
  let l:options = { 'type': mode(), 'eol': 1 }
  let l:pos = getregionpos(getpos('v'), getpos('.'), options)

echom "== getregionpos() results == " . string(l:pos)
endfunction

augroup ReportGetRegionPos
  autocmd!

  autocmd ModeChanged *:[vV\x16] if &operatorfunc ==# '' |
      \ call ReportGetRegionPos() |
      \ endif


  autocmd CursorMoved * if mode() ==# 'v' || mode() ==# 'V' || mode() ==# "\<C-V>" |
      \ call ReportGetRegionPos() |
      \ endif

augroup END
  1. Create a file (any should do)

$ echo "Lorem ipsum dolor sit amet, consectetur adipiscing elit." > test.txt

  1. Navigate to the end of the line using virtual edit. Stay in this position for the rest of the steps.

  2. activate getpos('.') and check result.

  3. activate getpos('.') and check result. The second time, they will still be the same.

  4. Enter visual mode (activating getregionpos()), check the result. It should still be the same as the two activations of getpos('.').

  5. Exit visual mode and activate getpos again. This result should be different, exactly one less column than before.

  6. Enter visual mode again, thereby activating getregionpos again. The result should be the same as the last result of getpos, one less than the original value. We see that, in this case, getregionpos is accurately reflecting this change in getpos.

https://github.com/user-attachments/assets/b095964e-838a-4dbd-8049-b9a650fc687c

Expected behaviour

The expected behavior is that getpos() will consistently report the position the user is in.

Version of Vim

VIM - Vi IMproved 9.1 (2024 Jan 02, compiled Nov 15 2024 03:05:17) Included patches: 1-866 Compiled by Arch Linux

Environment

OS: Arch Linux
Kernel: Linux 6.12.4-arch1-1
Terminal: Wezterm
$TERM: wezterm
shell: zsh 5.9 (x86_64-pc-linux-gnu)

Logs and stack traces

No response


Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/issues/16276@github.com>

zeertzjq

unread,
Dec 22, 2024, 7:30:15 AM12/22/24
to vim/vim, Subscribed

This is unrelated to getregionpos(). The same happens without the autocommands.


Reply to this email directly, view it on GitHub.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/issues/16276/2558439791@github.com>

zeertzjq

unread,
Dec 22, 2024, 7:46:06 AM12/22/24
to vim/vim, Subscribed

Hmm, the bug doesn't happen if 'virtualedit' contains both all and onemore. It seems that onemore disables some cursor adjustments that are unaffected by all, but the help for 'virtualedit' says:

	It doesn't make sense to combine "all" with "onemore", but you will
	not get a warning for it.


Reply to this email directly, view it on GitHub.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/issues/16276/2558444391@github.com>

mcauley-penney

unread,
Dec 21, 2025, 2:44:31 PM (17 hours ago) Dec 21
to vim/vim, Subscribed
mcauley-penney left a comment (vim/vim#16276)

I've found some time and energy to work on this.

The culprit

The issue comes from adjust_cursor_eol() in src/ops.c, particularly this conditional:

    unsigned int cur_ve_flags = get_ve_flags();

    int adj_cursor = (curwin->w_cursor.col > 0
				&& gchar_cursor() == NUL
				&& (cur_ve_flags & VE_ONEMORE) == 0
				&& !(restart_edit || (State & MODE_INSERT)));
    if (!adj_cursor)
	return; 

What is going on

adjust_cursor_eol() seems intended to fix the cursor when it sits past the end of the line by moving it back onto the last real (printable?) character. Applying the cursor position change under the VE_ALL condition causes the cursor’s internal position to change even though the cursor hasn’t moved. So, we then see output from getpos('.') where, in certain situations, the cursor doens't go anywhere but its internal position is encoded differently. This change is not applied in the VE_ONEMORE case, so including onemore prevents it from happening.

What I think should be happening

With virtualedit=all, we expect that the cursor can sit in the virtual space past the end of the line and that this position will not simply change across mode changes. I think that skipping the adjustment when VE_ALL is set gives us the behavior that users (at least me) expect out of virtualedit=all.

The fix

With the following change, the errant behavior disappears:

    unsigned int cur_ve_flags = get_ve_flags();

    int adj_cursor = (curwin->w_cursor.col > 0
				&& gchar_cursor() == NUL
				&& (cur_ve_flags & VE_ONEMORE) == 0
+               && (cur_ve_flags & VE_ALL) == 0
				&& !(restart_edit || (State & MODE_INSERT)));
    if (!adj_cursor)
	return; 

To test to make sure that this didn't break anything, I tried to find and run all of the relevant tests:

cd src/testdir
rg -l "virtualedit|VE_ALL|VE_ONEMORE|coladd|adjust_cursor_eol" . \
  | sed -n 's#^\./\(test_[^/]*\)\.vim$#\1.res#p' \
  | sort -u \
  | xargs make

Important

The comments associated with the virtualedit options enum found in src/option.h indicate that both VE_BLOCK and VE_INSERT "include 'all'". I haven't done anything to assess whether these options should be accounted for in the conditional that I changed. The only checking I've done that may be related is just running the tests.


Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/issues/16276/3679337855@github.com>

Reply all
Reply to author
Forward
0 new messages