[vim/vim] Undo works differently when buffer is populated by job_start() (Issue #15025)

16 views
Skip to first unread message

Lifepillar

unread,
Jun 16, 2024, 1:24:20 PM (10 days ago) Jun 16
to vim/vim, Subscribed

Steps to reproduce

If I populate a buffer with setline() and then I make some changes, each change becomes an undo step as expected.

If the buffer is populated from the output of a command spawn by job_start(), however, the same changes are not recorded in the undotree.

To reproduce:

  1. vim --clean test.vim, where test.vim is the following script:
vim9script

var input = ''

def Filter()
  while true
    redraw
    var c = getcharstr()

    if char2nr(c) == 0x80 || char2nr(c) < 0x20
      break
    endif

    input ..= c

    execute $'g!:\m{input}:norm "_dd'

    echomsg $'Char={c} seq_cur={undotree()["seq_cur"]}'
  endwhile
enddef

def FilterItems()
  new
  setline(1, ['abcdf', 'abcee', 'abdde', 'accde', 'bbcde'])
  Filter()
enddef

def FilterCmdOutput()
  new
  const nr = bufnr()

  job_start(['echo', "abcdf\nabcee\nabdde\naccde\nbbcde"], {
    'close_cb': (ch) => Filter(),
    'out_io':   'buffer',
    'out_buf':  nr,
  })
enddef

# FilterItems()
FilterCmdOutput()
  1. :source %
  2. Type, in sequence: abcde, then Esc. Each key press removes a line from the buffer.
  3. :messages.

The output in messages is:

Char=a seq_cur=5
Char=b seq_cur=5
Char=c seq_cur=5
Char=d seq_cur=5
Char=e seq_cur=5

Note how seq_cur does not increase. If you run the same script, but uncomment FilterItems() instead of FilterCmdOutput(), the output is:

Char=a seq_cur=2
Char=b seq_cur=3
Char=c seq_cur=4
Char=d seq_cur=5
Char=e seq_cur=6

Now seq_cur was increased after each key press.

Expected behaviour

I would expect seq_cur to increase every time Filter() is called, regardless of how the text was added to the buffer. In particular, I would expect the script above to record the following output:

Char=a seq_cur=5
Char=b seq_cur=6
Char=c seq_cur=7
Char=d seq_cur=8
Char=e seq_cur=9

Version of Vim

9.1.494

Environment

macOS 14.5
Apple Terminal
xterm-256color
ZSH 5.9

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/15025@github.com>

Lifepillar

unread,
Jun 16, 2024, 1:40:51 PM (10 days ago) Jun 16
to vim/vim, Subscribed

According to :help undojoin, calling getchar() should start a new change. That does not seem to be the case if the code using getchar() is in a callback.


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/15025/2171783796@github.com>

zeertzjq

unread,
Jun 16, 2024, 6:23:51 PM (10 days ago) Jun 16
to vim/vim, Subscribed

According to :help undojoin, calling getchar() should start a new change. That does not seem to be the case if the code using getchar() is in a callback.

I don't think that's the case. It just recommends that you use :undojoin before calling getchar().


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/15025/2171911351@github.com>

Christian Brabandt

unread,
Jun 16, 2024, 11:07:50 PM (10 days ago) Jun 16
to vim/vim, Subscribed

I agree with @zeertzjq reading of the help.


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/15025/2172089285@github.com>

Lifepillar

unread,
Jun 18, 2024, 3:00:50 AM (8 days ago) Jun 18
to vim/vim, Subscribed

I beg to disagree. The help describes a case where undojoin is necessary to merge two changes with a getchar() in between exactly because

the next key press will start a new change

In fact, that is what happens:

vim9script

def Foo()
  normal aX
  getchar()
  normal aY
  undo
enddef

Foo()

X remains in the buffer after the undo. But if the function is used as a callback, it behaves as if undojoin has been added. That is, the following script:

vim9script

def Foo()
  normal aX
  getchar()
  normal aY
  undo
enddef

job_start(['ls'], {'close_cb': (_) => Foo()})

behaves the same as:

vim9script

def Foo()
  normal aX
  getchar()
  undojoin
  normal aY
  undo
enddef

Foo()

I would like the function invoked as a job_start()'s callback to behave the same as if invoked directly.


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/15025/2175297532@github.com>

Lifepillar

unread,
Jun 21, 2024, 4:41:39 PM (5 days ago) Jun 21
to vim/vim, Subscribed

A workaround for this issue is to force a new change:

def Foo()
  normal aX
  getchar
()
  execute "normal" "i\<c-g>u"
  normal aY
  undo
enddef

Similarly, my original example is fixed by adding execute "normal" "i\<c-g>u" before (or after) getcharstr().


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/15025/2183426106@github.com>

Reply all
Reply to author
Forward
0 new messages