[vim/vim] vim9 call closure from autocmd_add() (Issue #11870)

15 views
Skip to first unread message

monkoose

unread,
Jan 22, 2023, 5:08:13 PM1/22/23
to vim/vim, Subscribed

You can easily reference closure from autocmd_add() cmd key. As example

function! s:Foo() abort
  function! s:Bar() closure
    echom 'foobar'
  endfunction
  call autocmd_add([#{event: 'CursorMoved', pattern: '*', cmd: 'call s:Bar()'}])
endfunction

call s:Foo()

How to achieve the same with vim9? Tested a lot of the things without any success.

vim9script

def Foo()
  def Bar()
    echom 'foobar'
  enddef
  autocmd_add([{event: 'CursorMoved', pattern: '*', cmd: 'Bar()'}])
enddef

Foo()

Error detected while processing CursorMoved Autocommands for "*": E117: Unknown function: Bar


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

mityu

unread,
Jan 23, 2023, 6:07:19 AM1/23/23
to vim/vim, Subscribed

How about this? This worked at least in my environment.

Runned steps:

  • vim --clean
  • Put this script on the buffer
  • :%so
vim9script

var BarRef: func(): void
def Foo()
  const baz = 'baz'
  def Bar()
    echom 'foobar' .. baz
  enddef
  BarRef = Bar
  autocmd_add([{event: 'CursorMoved', pattern: '*', cmd: 'BarRef()'}])
enddef

Foo()
doautocmd CursorMoved
# => foobarbaz

Environment:

  • Vim 9.0.1222
  • Arch Linux


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

mityu

unread,
Jan 23, 2023, 6:17:15 AM1/23/23
to vim/vim, Subscribed

Maybe :h E1058 is related to this:

When using `:function` or `:def` to specify a nested function inside a `:def`
function and no namespace was given, this nested function is local to the code
block it is defined in.  It cannot be used in `function()` with a string
argument, pass the function reference itself: >
	def Outer()
	  def Inner()
	    echo 'inner'
	  enddef
	  var Fok = function(Inner)     # OK
	  var Fbad = function('Inner')  # does not work

Detail: this is because "Inner" will actually become a function reference to a
function with a generated name.

It is not possible to define a script-local function in a function.  You can
define a local function and assign it to a script-local funcref (it must have
been declared at the script level).  It is possible to define a global
function by using the "g:" prefix.


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

monkoose

unread,
Jan 23, 2023, 6:17:43 AM1/23/23
to vim/vim, Subscribed

And now Bar isn't local at all
I have simplified the example. This is working yes, but now try this

var BarRef: func(): void
def Foo(boo: string)
  const baz = 'baz'
  def Bar()
    echom $'foobar{baz}{boo}'
  enddef
  BarRef = Bar
  autocmd_add([{event: 'CursorMoved', pattern: '*', cmd: 'BarRef()'}])
enddef

Foo('moo')
Foo('boo')

moo is overriden by boo


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

monkoose

unread,
Jan 23, 2023, 6:19:45 AM1/23/23
to vim/vim, Subscribed

Ok i have tried legacy script version and i doesn't work too :(


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

monkoose

unread,
Jan 23, 2023, 6:21:22 AM1/23/23
to vim/vim, Subscribed

Any workaround to achieve what I want? Dynamically create 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/11870/1400184550@github.com>

mityu

unread,
Jan 23, 2023, 6:40:33 AM1/23/23
to vim/vim, Subscribed

I came up with two workarounds, but both of this is not good at managing added autocmds...

vim9script
var BarRefs: list<func(): void>
def Foo(boo: string)
  const baz = 'baz'
  def Bar()
    echom $'foobar{baz}{boo}'
  enddef
  BarRefs->add(Bar)
enddef

def OnCursorMoved()
  for F in BarRefs
    F()
  endfor
enddef
autocmd_add([{event: 'CursorMoved', pattern: '*', cmd: 'OnCursorMoved()'}])

Foo('moo')
Foo('boo')
vim9script

def Bar(baz: string, boo: string)
  
echom $'foobar{baz}{boo}'
enddef

def Foo(boo: string)
  const baz = 'baz'
  autocmd_add([{event: 'CursorMoved', pattern: '*', cmd: printf('call("Bar", %s)', [baz, boo])}])
enddef

Foo('moo')
Foo('boo')


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

monkoose

unread,
Jan 23, 2023, 6:44:16 AM1/23/23
to vim/vim, Subscribed

@mityu Thank You. Will test if any of those works for my needs and then will report.


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

monkoose

unread,
Jan 23, 2023, 2:00:00 PM1/23/23
to vim/vim, Subscribed

Ok I did something similar to you first suggestion and it works, too bad that there is no simpler solution, that do not require some dangling list/dict. I will leave this open for a few days, maybe someone would suggest something better. Thank You nevertheless.


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

Bram Moolenaar

unread,
Jan 23, 2023, 4:22:30 PM1/23/23
to vim/vim, Subscribed


> @mityu Thank You. Will test if any of those works for my needs and
> then will report.

The autocommand is executed outside of the script, but the script
context is set for it. Thus it can access script-local items. But not
function-call-local items.

There is no way to pass the function call context to the autocommand.
The Bar() function is created for each time the function is called. It
actually uses the same lambda, but it access the outer context, which in
this case gets it the function argument.

Only inside the function this reference to the Bar() function exists. I
don't really see any other way than to store it somewhere. Using a
list, as mityu suggested, works. I would use it differently though:

```

vim9script

var BarRefs: list<func(): void>

def Foo(boo: string)
const baz = 'baz'
def Bar()
echowin $'foobar{baz}{boo}'
enddef
var idx = len(BarRefs)
add(BarRefs, Bar)
autocmd_add([{event: 'CursorMoved', pattern: '*', cmd: $'BarRefs[{idx}]()'}])
enddef

Foo('moo')
Foo('BOO')
```

This works. Things are kept local to the script at least.


--
It was recently discovered that research causes cancer in rats.

/// Bram Moolenaar -- ***@***.*** -- http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///


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

monkoose

unread,
Jan 24, 2023, 5:37:46 AM1/24/23
to vim/vim, Subscribed

Thank you, Bram.


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

monkoose

unread,
Jan 24, 2023, 5:38:00 AM1/24/23
to vim/vim, Subscribed

Closed #11870 as completed.


Reply to this email directly, view it on GitHub.

You are receiving this because you are subscribed to this thread.Message ID: <vim/vim/issue/11870/issue_event/8342827032@github.com>

Geoff Reedy

unread,
Jan 24, 2023, 10:12:34 PM1/24/23
to vim/vim, Subscribed

I think it would be neat to allow a funcref in autocmd_add and add a callback_T cmd_cb field to the auto command struct for it. The same would be nice for options like omnifunc. Interestingly these are stored into the buffer structure as callback objects but only set as strings.

I started working on a patch to allow a funcref as the cmd option in autocmd_add but ran into a conceptual limitation: these places cannot be extended to hold funcrefs without slightly breaking compatibility. There are potentially scripts that rely on the fact that these values are accessed as strings. Now the common pattern of saving an option value and then restoring it should work the same (except if it is a vim9 script that is using the string type for the variable) and autocmd_get is only since vim9 so maybe this slight incompatibility is acceptable.


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

Yegappan Lakshmanan

unread,
Jan 24, 2023, 11:55:35 PM1/24/23
to vim...@googlegroups.com, reply+ACY5DGDP5R4QUXT5KR...@reply.github.com, vim/vim, Subscribed
Hi,

On Tue, Jan 24, 2023 at 7:12 PM Geoff Reedy <vim-dev...@256bit.org> wrote:

I think it would be neat to allow a funcref in autocmd_add and add a callback_T cmd_cb field to the auto command struct for it.

 

The same would be nice for options like omnifunc. Interestingly these are stored into the buffer structure as callback objects but only set as strings.


You can set 'omnifunc' and other similar '*func' options to a funcref.  You can refer to the following help

The following commit added this support:


- Yegappan

vim-dev ML

unread,
Jan 24, 2023, 11:55:50 PM1/24/23
to vim/vim, vim-dev ML, Your activity

Hi,

On Tue, Jan 24, 2023 at 7:12 PM Geoff Reedy ***@***.***>


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

Geoff Reedy

unread,
Jan 25, 2023, 2:51:52 AM1/25/23
to vim_dev
Yes, but this does not work for a nested def function. Setting one of these options to a funcref stringifies the funcref and then later unstringifies the value to get back to a callback. This process loses the environment bound in the closure causing an E1248.
Reply all
Reply to author
Forward
0 new messages