[vim/vim] Support a lambda or a partial for the 'operatorfunc' option (#8775)

101 views
Skip to first unread message

Yegappan Lakshmanan

unread,
Aug 19, 2021, 9:06:58 PM8/19/21
to vim/vim, Subscribed

You can view, comment on, or merge this pull request online at:

  https://github.com/vim/vim/pull/8775

Commit Summary

  • Support a lambda or a partial for the 'operatorfunc' option

File Changes

Patch Links:


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or unsubscribe.
Triage notifications on the go with GitHub Mobile for iOS or Android.

codecov[bot]

unread,
Aug 19, 2021, 9:14:49 PM8/19/21
to vim/vim, Subscribed

Codecov Report

Merging #8775 (763fdab) into master (5aec755) will decrease coverage by 1.88%.
The diff coverage is 82.75%.

Current head 763fdab differs from pull request most recent head e71b389. Consider uploading reports for the commit e71b389 to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@

##           master    #8775      +/-   ##

==========================================

- Coverage   90.15%   88.26%   -1.89%     

==========================================

  Files         151      146       -5     

  Lines      170691   166364    -4327     

==========================================

- Hits       153882   146839    -7043     

- Misses      16809    19525    +2716     
Flag Coverage Δ
huge-clang-none ?
huge-gcc-none ?
huge-gcc-testgui 88.26% <82.75%> (-0.01%) ⬇️
huge-gcc-unittests ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
src/optionstr.c 94.08% <66.66%> (-1.09%) ⬇️
src/option.c 93.56% <80.95%> (-0.63%) ⬇️
src/ops.c 95.18% <100.00%> (-0.82%) ⬇️
src/quickfix.c 94.81% <100.00%> (-0.13%) ⬇️
src/libvterm/src/rect.h 0.00% <0.00%> (-96.56%) ⬇️
src/libvterm/src/mouse.c 0.00% <0.00%> (-48.34%) ⬇️
src/libvterm/src/state.c 48.87% <0.00%> (-41.13%) ⬇️
src/libvterm/include/vterm.h 0.00% <0.00%> (-37.50%) ⬇️
src/libvterm/src/pen.c 49.85% <0.00%> (-34.81%) ⬇️
src/libvterm/src/keyboard.c 54.73% <0.00%> (-33.69%) ⬇️
... and 137 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 5aec755...e71b389. Read the comment docs.

Yegappan Lakshmanan

unread,
Aug 19, 2021, 9:15:39 PM8/19/21
to vim/vim, Push

@yegappan pushed 1 commit.

  • b1c338f Fix failure in tiny build


You are receiving this because you are subscribed to this thread.

View it on GitHub or unsubscribe.

Bram Moolenaar

unread,
Aug 20, 2021, 4:35:59 PM8/20/21
to vim/vim, Subscribed

This deserves a good example, or perhaps two, for how the lambda can be useful.

lacygoill

unread,
Aug 21, 2021, 12:49:00 PM8/21/21
to vim/vim, Subscribed

This could be handy for when the opfunc is a script-local function. We can't write this:

set operatorfunc=s:MyOpfunc

Because it raises E120:

E120: Using <SID> not in a script context: s:MyOpfunc

So, we have to write this:

let &operatorfunc = expand('<SID>') .. 'MyOpfunc'

Which looks a bit weird.

But with a lambda, we could write this instead:

set operatorfunc={type\ ->\ s:MyOpfunc(type)}

One could argue that it still looks weird. I still prefer reading a lambda which can be used in many other contexts, rather than expand('<SID>'), which is less often used.


Just 2 remarks. It works with :set but not with :let. That is, we cannot write this:

let &operatorfunc = {type -> s:MyOpfunc(type)}

Because it raises E729:

E729: Using a Funcref as a String

It still doesn't work if we manually coerce the lambda into a string:

let &operatorfunc = {type -> s:MyOpfunc(type)}->string()
                                              ^--------^

Because this time, it raises E129:

E129: Function name required

It would be nice if could work so that we don't have to escape spaces:

# ugly
set operatorfunc={type\ ->\ s:MyOpfunc(type)}
                      ^   ^

# nicer
let &operatorfunc = {type -> s:MyOpfunc(type)}
                         ^  ^

Also, it doesn't work with a Vim9 lambda. That is, in a Vim9 script/function, we cannot write this:

set operatorfunc=(type)\ =>\ MyOpfunc(type)

Because it raises E117:

E117: Unknown function: (type) => MyOpfunc(type)

I suspect that's because the option is wrongly evaluated in the legacy context. Possibly relevant todo item:

Use the location where the option was set for deciding whether it's to be
evaluated in Vim9 script context.

As a workaround, we have to use the :legacy modifier:

legacy set operatorfunc={type\ ->\ MyOpfunc(type)}
^----^

Using the example given at :help g@:

vim9script
nnoremap <expr> <F4> <SID>CountSpaces()
xnoremap <expr> <F4> <SID>CountSpaces()
nnoremap <expr> <F4><F4> <SID>CountSpaces() .. '_'

def CountSpaces(type = ''): string
  if type == ''
    legacy set operatorfunc={type\ ->\ CountSpaces(type)}
    return 'g@'
  endif

  var sel_save: string = &selection
  var reg_save: dict<any> = getreginfo('"')
  var cb_save: string = &clipboard
  var visual_marks_save: list<list<number>> = [getpos("'<"), getpos("'>")]

  try
    set clipboard= selection=inclusive
    var commands: dict<string> = {line: "'[V']y", char: "`[v`]y", block: "`[\<c-v>`]y"}
    silent execute 'noautocmd keepjumps normal! ' .. get(commands, type, '"')
    echom getreg('"')->count(' ')
  finally
    setreg('"', reg_save)
    setpos("'<", visual_marks_save[0])
    setpos("'>", visual_marks_save[1])
    &clipboard = cb_save
    &selection = sel_save
  endtry
  return ''
enddef

'a b c d e'->setline(1)
feedkeys("\<F4>\<F4>")

What's weird is that we don't have to specify the s: prefix in the legacy lambda in front of the script-local function name. Usually, in the legacy context, s: is mandatory; it can only be dropped in the Vim9 context.

Yegappan Lakshmanan

unread,
Aug 21, 2021, 2:42:52 PM8/21/21
to vim_dev, reply+ACY5DGBQT7F7WWYEFH...@reply.github.com, vim/vim, Subscribed
Hi,

Thanks for trying out the patch and the detailed report. See below for some
comments.

On Sat, Aug 21, 2021 at 9:48 AM lacygoill <vim-dev...@256bit.org> wrote:

This could be handy for when the opfunc is a script-local function. We can't write this:

set operatorfunc=s:MyOpfunc

Because it raises E120:

E120: Using <SID> not in a script context: s:MyOpfunc

So, we have to write this:

let &operatorfunc = expand('<SID>') .. 'MyOpfunc'

Which looks a bit weird.

But with a lambda, we could write this instead:

set operatorfunc={type\ ->\ s:MyOpfunc(type)}

One could argue that it still looks weird. I still prefer reading a lambda which can be used in many other contexts, rather than expand('<SID>'), which is less often used.


Just 2 remarks. It works with :set but not with :let. That is, we cannot write this:

let &operatorfunc = {type -> s:MyOpfunc(type)}

Because it raises E729:

E729: Using a Funcref as a String

It still doesn't work if we manually coerce the lambda into a string:


Yes. Currently this option only supports a string value. So to set the
option using 'let' you need to quote the lambda:

let &operatorfunc = '{type -> s:MyOpfunc(type)}'
 
let &operatorfunc = {type -> s:MyOpfunc(type)}->string()
                                              ^--------^

Because this time, it raises E129:

E129: Function name required

It would be nice if could work so that we don't have to escape spaces:

# ugly
set operatorfunc={type\ ->\ s:MyOpfunc(type)}
                      ^   ^

# nicer
let &operatorfunc = {type -> s:MyOpfunc(type)}
                         ^  ^


Agreed. But supporting this syntax is a lot more work. We need
to add a new option type that can accept function names (string)
or lambdas or funcrefs.
 

Also, it doesn't work with a Vim9 lambda. That is, in a Vim9 script/function, we cannot write this:

set operatorfunc=(type)\ =>\ MyOpfunc(type)


Yes. I will take a look at this.
 

Because it raises E117:

E117: Unknown function: (type) => MyOpfunc(type)

I suspect that's because the option is wrongly evaluated in the legacy context. Possibly relevant todo item:

Use the location where the option was set for deciding whether it's to be
evaluated in Vim9 script context.

As a workaround, we have to use the :legacy modifier:

legacy set operatorfunc={type\ ->\ MyOpfunc(type)}
^----^

Using the example given at :help g@:


Thanks for enhancing the example with the new syntax.

Regards,
Yegappan

vim-dev ML

unread,
Aug 21, 2021, 2:43:12 PM8/21/21
to vim/vim, vim-dev ML, Your activity

Hi,

Thanks for trying out the patch and the detailed report. See below for some
comments.

On Sat, Aug 21, 2021 at 9:48 AM lacygoill ***@***.***> wrote:

> This could be handy for when the opfunc is a script-local function. We
> can't write this:
>
> set operatorfunc=s:MyOpfunc
>
> Because it raises E120:
>
> E120: Using <SID> not in a script context: s:MyOpfunc
>
> So, we have to write this:
>
> let &operatorfunc = expand('<SID>') .. 'MyOpfunc'
>
> Which looks a bit weird.
>
> But with a lambda, we could write this instead:
>
> set operatorfunc={type\ ->\ s:MyOpfunc(type)}
>
> One could argue that it still looks weird. I still prefer reading a lambda
> which can be used in many other contexts, rather than expand('<SID>'),
> which is less often used.
> ------------------------------
> vim9scriptnnoremap <expr> <F4> <SID>CountSpaces()xnoremap <expr> <F4> <SID>CountSpaces()nnoremap <expr> <F4><F4> <SID>CountSpaces() .. '_'

> def CountSpaces(type = ''): string
> if type == ''
> legacy set operatorfunc={type\ ->\ CountSpaces(type)}
> return 'g@'
> endif
>
> var sel_save: string = &selection
> var reg_save: dict<any> = getreginfo('"')
> var cb_save: string = &clipboard
> var visual_marks_save: list<list<number>> = [getpos("'<"), getpos("'>")]
>
> try
> set clipboard= selection=inclusive
> var commands: dict<string> = {line: "'[V']y", char: "`[v`]y", block: "`[\<c-v>`]y"}
> silent execute 'noautocmd keepjumps normal! ' .. get(commands, type, '"')
> echom getreg('"')->count(' ')
> finally
> setreg('"', reg_save)
> setpos("'<", visual_marks_save[0])
> setpos("'>", visual_marks_save[1])
> &clipboard = cb_save
> &selection = sel_save
> endtry
> return ''enddef
> 'a b c d e'->setline(1)feedkeys("\<F4>\<F4>")

>
> What's weird is that we don't have to specify the s: prefix in the legacy
> lambda in front of the script-local function name. Usually, in the legacy
> context, s: is mandatory; it can only be dropped in the Vim9 context.
>
>
>

Bram Moolenaar

unread,
Oct 23, 2021, 4:44:55 PM10/23/21
to vim/vim, vim-dev ML, Comment

There are several options that use a string value which is the name of a function.
Most functions, also lambdas, have a string name, some with a non-alpha prefix.
That allows using any function, so long as we can get the name as a string.
Then we could allow using:

let &operatorfunc = {type -> [some expression]}

And also:

let &operatorfunc = s:SomeFunc

Keeping the option type a string avoids having to deal with a different option value in very many places.
We just need to add a flag to the options that use a function name, and covert the function reference to a name for these.


You are receiving this because you commented.
Reply to this email directly, view it on GitHub.

Bram Moolenaar

unread,
Nov 15, 2021, 2:40:07 PM11/15/21
to vim/vim, vim-dev ML, Comment

Finally had time to look into the patch. It does make sense, I think we can use his for all options that take the name of a function as an argument.
The use of function() and funcref() is not documented. We should make a separate section in the help, with some examples, and link to it from the options that support this format.
We have to make sure that the callback is cleared when exiting. The asan build doesn't report a leak, but that's probably because the tests end with making the option empty.


You are receiving this because you commented.
Reply to this email directly, view it on GitHub.

lacygoill

unread,
Nov 15, 2021, 10:21:25 PM11/15/21
to vim/vim, vim-dev ML, Comment

It seems that we can use a partial in a legacy function:

vim9script
nnoremap <expr> zd <SID>ZdSetup('zd')
function ZdSetup(cmd)
    set operatorfunc=function('s:Zd',\ [a:cmd])
    return 'g@l'
endfunction
def Zd(cmd: string, type: string)
    execute 'normal! ' .. cmd
enddef
var lines =<< trim END
    some folded lines {{{
    some folded lines
    some folded lines }}}
END
lines->setline(1)
&foldmethod = 'marker'
normal zd
the fold is correctly deleted

But not in a Vim9 function:

vim9script
nnoremap <expr> zd <SID>ZdSetup('zd')
def ZdSetup(cmd: string): string
    set operatorfunc=function(Zd,\ [cmd])
    return 'g@l'
enddef
def Zd(cmd: string, type: string)
    execute 'normal! ' .. cmd
enddef
var lines =<< trim END
    some folded lines {{{
    some folded lines
    some folded lines }}}
END
lines->setline(1)
&foldmethod = 'marker'
normal zd
E121: Undefined variable: cmd

Am I missing something? Shouldn't Vim be able to find the cmd argument in a :def function:

set operatorfunc=function(Zd,\ [cmd])
                                ^^^

just like it is able to in a legacy function:

set operatorfunc=function('s:Zd',\ [a:cmd])
                                    ^---^

Note that the issue is not caused by the omission of the s: scope in front of Zd, nor by the omission of the quotes around. None of them are required in the Vim9 context. And it doesn't suppress the error here anyway.


You are receiving this because you commented.
Reply to this email directly, view it on GitHub.

lacygoill

unread,
Nov 15, 2021, 11:09:47 PM11/15/21
to vim/vim, vim-dev ML, Comment

Sorry, I think it's the same issue as the one reported earlier about the lambdas:

Also, it doesn't work with a Vim9 lambda. That is, in a Vim9 script/function, we cannot write this:

set operatorfunc=(type)\ =>\ MyOpfunc(type)


You are receiving this because you commented.
Reply to this email directly, view it on GitHub.

Yegappan Lakshmanan

unread,
Nov 16, 2021, 1:01:27 AM11/16/21
to vim_dev, reply+ACY5DGHOC7L6QD75UM...@reply.github.com, vim/vim, vim-dev ML, Comment
Hi,
The current implementation only supports legacy functions and
funcrefs. It doesn't support
Vim9 functions.

Regards,
Yegappan

vim-dev ML

unread,
Nov 16, 2021, 1:01:44 AM11/16/21
to vim/vim, vim-dev ML, Your activity

Hi,


On Mon, Nov 15, 2021 at 7:21 PM lacygoill ***@***.***> wrote:
>
> It seems that we can use a partial in a legacy function:
>
> vim9script
> nnoremap <expr> zd <SID>ZdSetup('zd')
> function ZdSetup(cmd)
> set operatorfunc=function('s:Zd',\ [a:cmd])
> return ***@***.***'

> endfunction
> def Zd(cmd: string, type: string)
> execute 'normal! ' .. cmd
> enddef
> var lines =<< trim END
> some folded lines {{{
> some folded lines
> some folded lines }}}
> END
> lines->setline(1)
> &foldmethod = 'marker'
> normal zd
>
> the fold is correctly deleted
>
> But not in a Vim9 function:
>
> vim9script
> nnoremap <expr> zd <SID>ZdSetup('zd')
> def ZdSetup(cmd: string): string
> set operatorfunc=function(Zd,\ [cmd])
> return ***@***.***'


You are receiving this because you are subscribed to this thread.

Reply to this email directly, view it on GitHub.

Yegappan Lakshmanan

unread,
Nov 16, 2021, 1:40:37 AM11/16/21
to vim_dev, reply+ACY5DGBX2YSJASHCXD...@reply.github.com, vim/vim, vim-dev ML, Comment
Hi Bram,

On Mon, Nov 15, 2021 at 11:40 AM Bram Moolenaar <vim-dev...@256bit.org> wrote:

Finally had time to look into the patch. It does make sense, I think we can use his for all options that take the name of a function as an argument.
The use of function() and funcref() is not documented. We should make a separate section in the help, with some examples, and link to it from the options that support this format.
We have to make sure that the callback is cleared when exiting. The asan build doesn't report a leak, but that's probably because the tests end with making the option empty.



The current implementation only supports legacy functions and doesn't support compiled
Vim9 functions and lambdas.

Regards,
Yegappan 

vim-dev ML

unread,
Nov 16, 2021, 1:40:55 AM11/16/21
to vim/vim, vim-dev ML, Your activity

Hi Bram,

On Mon, Nov 15, 2021 at 11:40 AM Bram Moolenaar ***@***.***>

wrote:

> Finally had time to look into the patch. It does make sense, I think we
> can use his for all options that take the name of a function as an argument.
> The use of function() and funcref() is not documented. We should make a
> separate section in the help, with some examples, and link to it from the
> options that support this format.
> We have to make sure that the callback is cleared when exiting. The asan
> build doesn't report a leak, but that's probably because the tests end with
> making the option empty.
>
>
>
The current implementation only supports legacy functions and doesn't
support compiled
Vim9 functions and lambdas.

Regards,
Yegappan


You are receiving this because you are subscribed to this thread.

Reply to this email directly, view it on GitHub.

Bram Moolenaar

unread,
Nov 16, 2021, 12:20:09 PM11/16/21
to vim...@googlegroups.com, lacygoill

> It seems that we can use a partial in a legacy function:
> ```vim
> vim9script
> nnoremap <expr> zd <SID>ZdSetup('zd')
> function ZdSetup(cmd)
> set operatorfunc=function('s:Zd',\ [a:cmd])
> return 'g@l'
> endfunction
> def Zd(cmd: string, type: string)
> execute 'normal! ' .. cmd
> enddef
> var lines =<< trim END
> some folded lines {{{
> some folded lines
> some folded lines }}}
> END
> lines->setline(1)
> &foldmethod = 'marker'
> normal zd
> ```
> the fold is correctly deleted
>
> But not in a Vim9 function:
> ```vim
> vim9script
> nnoremap <expr> zd <SID>ZdSetup('zd')
> def ZdSetup(cmd: string): string
> set operatorfunc=function(Zd,\ [cmd])
> return 'g@l'
> enddef
> def Zd(cmd: string, type: string)
> execute 'normal! ' .. cmd
> enddef
> var lines =<< trim END
> some folded lines {{{
> some folded lines
> some folded lines }}}
> END
> lines->setline(1)
> &foldmethod = 'marker'
> normal zd
> ```
> E121: Undefined variable: cmd
>
> Am I missing something? Shouldn't Vim be able to find the `cmd`
> argument in a `:def` function:

The :def function is compiled and turns the "set" command into an EXEC
instruction. The argument is a string, when executed the function
context where "cmd" is defined is not accessible.

It might work to do (untested):

&operatorfunc = 'function(Zd, [' .. cmd .. '])'


--
f y cn rd ths thn y cn hv grt jb n cmptr prgrmmng

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

Yegappan Lakshmanan

unread,
Nov 18, 2021, 1:15:10 AM11/18/21
to vim/vim, vim-dev ML, Push

@yegappan pushed 1 commit.

  • 8ada2d5 Update doc and free function callback on exit


You are receiving this because you are subscribed to this thread.

View it on GitHub.

Yegappan Lakshmanan

unread,
Nov 18, 2021, 1:16:39 AM11/18/21
to vim/vim, vim-dev ML, Push

@yegappan pushed 2 commits.

  • a8701ce Support a lambda or a partial for the 'operatorfunc' option
  • 9499dab Update doc and free function callback on exit


You are receiving this because you are subscribed to this thread.

View it on GitHub.

Yegappan Lakshmanan

unread,
Nov 18, 2021, 1:27:57 AM11/18/21
to vim/vim, vim-dev ML, Push

@yegappan pushed 1 commit.


You are receiving this because you are subscribed to this thread.

View it on GitHub.

Yegappan Lakshmanan

unread,
Nov 18, 2021, 1:46:08 AM11/18/21
to vim_dev, reply+ACY5DGBX2YSJASHCXD...@reply.github.com, vim/vim, vim-dev ML, Comment
Hi Bram,

On Mon, Nov 15, 2021 at 11:40 AM Bram Moolenaar <vim-dev...@256bit.org> wrote:

Finally had time to look into the patch. It does make sense, I think we can use his for all options that take the name of a function as an argument.
The use of function() and funcref() is not documented. We should make a separate section in the help, with some examples, and link to it from the options that support this format.
We have to make sure that the callback is cleared when exiting. The asan build doesn't report a leak, but that's probably because the tests end with making the option empty.



I have updated the PR to incorporate your comments.

Regards,
Yegappan

vim-dev ML

unread,
Nov 18, 2021, 1:46:24 AM11/18/21
to vim/vim, vim-dev ML, Your activity

Hi Bram,

On Mon, Nov 15, 2021 at 11:40 AM Bram Moolenaar ***@***.***>

wrote:

> Finally had time to look into the patch. It does make sense, I think we
> can use his for all options that take the name of a function as an argument.
> The use of function() and funcref() is not documented. We should make a
> separate section in the help, with some examples, and link to it from the
> options that support this format.
> We have to make sure that the callback is cleared when exiting. The asan
> build doesn't report a leak, but that's probably because the tests end with
> making the option empty.
>
>
>
I have updated the PR to incorporate your comments.

Regards,
Yegappan


You are receiving this because you are subscribed to this thread.

Reply to this email directly, view it on GitHub.

Bram Moolenaar

unread,
Nov 18, 2021, 5:09:21 PM11/18/21
to vim/vim, vim-dev ML, Comment

Closed #8775 via 777175b.


You are receiving this because you commented.
Reply to this email directly, view it on GitHub.

Yegappan Lakshmanan

unread,
Dec 6, 2021, 10:39:01 AM12/6/21
to vim_dev, reply+ACY5DGBQT7F7WWYEFH...@reply.github.com, vim/vim, Subscribed
Hi,

See below for some comments (after patch 8.2.3751)

On Sat, Aug 21, 2021 at 9:48 AM lacygoill <vim-dev...@256bit.org> wrote:

This could be handy for when the opfunc is a script-local function. We can't write this:

set operatorfunc=s:MyOpfunc

This should work now.
 

Because it raises E120:

E120: Using <SID> not in a script context: s:MyOpfunc

So, we have to write this:

let &operatorfunc = expand('<SID>') .. 'MyOpfunc'

Which looks a bit weird.

But with a lambda, we could write this instead:

set operatorfunc={type\ ->\ s:MyOpfunc(type)}

One could argue that it still looks weird. I still prefer reading a lambda which can be used in many other contexts, rather than expand('<SID>'), which is less often used.


Just 2 remarks. It works with :set but not with :let. That is, we cannot write this:

let &operatorfunc = {type -> s:MyOpfunc(type)}

This should work now. 

Because it raises E729:

E729: Using a Funcref as a String

It still doesn't work if we manually coerce the lambda into a string:

let &operatorfunc = {type -> s:MyOpfunc(type)}->string()
                                              ^--------^
This also should work now. 

Because this time, it raises E129:

E129: Function name required

It would be nice if could work so that we don't have to escape spaces:

# ugly
set operatorfunc={type\ ->\ s:MyOpfunc(type)}
                      ^   ^

# nicer
let &operatorfunc = {type -> s:MyOpfunc(type)}
                         ^  ^

Also, it doesn't work with a Vim9 lambda. That is, in a Vim9 script/function, we cannot write this:

set operatorfunc=(type)\ =>\ MyOpfunc(type)

This should work now. 

Because it raises E117:

E117: Unknown function: (type) => MyOpfunc(type)

I suspect that's because the option is wrongly evaluated in the legacy context. Possibly relevant todo item:

Use the location where the option was set for deciding whether it's to be
evaluated in Vim9 script context.

As a workaround, we have to use the :legacy modifier:

legacy set operatorfunc={type\ ->\ MyOpfunc(type)}
^----^



Can you try the latest version and let us know if you see any problems?

Thanks,
Yegappan
 

vim-dev ML

unread,
Dec 6, 2021, 10:39:19 AM12/6/21
to vim/vim, vim-dev ML, Your activity

Hi,

See below for some comments (after patch 8.2.3751)

On Sat, Aug 21, 2021 at 9:48 AM lacygoill ***@***.***> wrote:

> This could be handy for when the opfunc is a script-local function. We
> can't write this:
>
> set operatorfunc=s:MyOpfunc
>
>
This should work now.


> Because it raises E120:
>
> E120: Using <SID> not in a script context: s:MyOpfunc
>
> So, we have to write this:
>
> let &operatorfunc = expand('<SID>') .. 'MyOpfunc'
>
> Which looks a bit weird.
>
> But with a lambda, we could write this instead:
>
> set operatorfunc={type\ ->\ s:MyOpfunc(type)}
>
> One could argue that it still looks weird. I still prefer reading a lambda
> which can be used in many other contexts, rather than expand('<SID>'),
> which is less often used.
> ------------------------------


You are receiving this because you are subscribed to this thread.

Reply to this email directly, view it on GitHub.

lacygoill

unread,
Dec 6, 2021, 10:36:56 PM12/6/21
to vim/vim, vim-dev ML, Comment

Thank you very much for all your work on this. I don't know whether it's planned at some point, but we can still not set an opfunc with a simple assignment in Vim9:

vim9script
nnoremap <expr> <F4><F4> <SID>CountSpaces() .. '_'
def CountSpaces(type = ''): string
  if type == ''
    &operatorfunc = (t) => CountSpaces(t)
    return 'g@'
  endif
  normal! '[V']y
  echomsg getreg('"')->count(' ')
  return ''
enddef
'a b c d e'->setline(1)
feedkeys("\<F4>\<F4>")
E1012: Type mismatch; expected string but got func(any): string

The issue comes from this line:

&operatorfunc = (t) => CountSpaces(t)

Which works in legacy:

legacy let &operatorfunc = {t -> CountSpaces(t)}

Test:

vim9script
nnoremap <expr> <F4><F4> <SID>CountSpaces() .. '_'
def CountSpaces(type = ''): string
  if type == ''
    legacy let &operatorfunc = {t -> CountSpaces(t)}
    return 'g@'
  endif
  normal! '[V']y
  echomsg getreg('"')->count(' ')
  return ''
enddef
'a b c d e'->setline(1)
feedkeys("\<F4>\<F4>")
4

Also, still in Vim9, we can not use a partial; neither with :set:

set operatorfunc=function(Zd,\ [cmd])

Test:

vim9script
nnoremap <expr> zd <SID>ZdSetup('zd')
def ZdSetup(cmd: string): string
    set operatorfunc=function(Zd,\ [cmd])
    return 'g@l'
enddef
def Zd(cmd: string, type: string)
    execute 'normal! ' .. cmd
enddef
var lines =<< trim END
    some folded lines {{{
    some folded lines
    some folded lines }}}
END
lines->setline(1)
&foldmethod = 'marker'
normal zd
E121: Undefined variable: cmd

nor with an assignment:

&operatorfunc = function(Zd, [cmd])

Test:

vim9script
nnoremap <expr> zd <SID>ZdSetup('zd')
def ZdSetup(cmd: string): string
    &operatorfunc = function(Zd, [cmd])
    
return 'g@l'
enddef
def Zd(cmd: string, type: string)
    execute 'normal! ' .. cmd
enddef
var lines =<< trim END
    some folded lines {{{
    some folded lines
    some folded lines }}}
END
lines->setline(1)
&foldmethod = 'marker'
normal zd
E1012: Type mismatch; expected string but got func(...): unknown


You are receiving this because you commented.
Reply to this email directly, view it on GitHub.

Yegappan Lakshmanan

unread,
Dec 8, 2021, 1:05:25 AM12/8/21
to vim_dev, reply+ACY5DGAW77N3ZVZEO5...@reply.github.com, vim/vim, vim-dev ML, Comment
Hi,

On Mon, Dec 6, 2021 at 7:36 PM lacygoill <vim-dev...@256bit.org> wrote:

Thank you very much for all your work on this. I don't know whether it's planned at some point, but we can still not set an opfunc with a simple assignment in Vim9:


Thanks for testing this. Setting the 'opfunc', 'completefunc', etc. options to a funcref
variable in a compiled Vim9 function is currently not supported.

Regards,
Yegappan

vim-dev ML

unread,
Dec 8, 2021, 1:05:43 AM12/8/21
to vim/vim, vim-dev ML, Your activity

Hi,


On Mon, Dec 6, 2021 at 7:36 PM lacygoill ***@***.***> wrote:

> Thank you very much for all your work on this. I don't know whether it's
> planned at some point, but we can still not set an opfunc with a simple
> assignment in Vim9:
>

Thanks for testing this. Setting the 'opfunc', 'completefunc', etc. options
to a funcref
variable in a compiled Vim9 function is currently not supported.

Regards,
Yegappan


> vim9scriptnnoremap <expr> <F4><F4> <SID>CountSpaces() .. '_'def CountSpaces(type = ''): string

> if type == ''
> &operatorfunc = (t) => CountSpaces(t)
> return 'g@'
> endif
> normal! '[V']y
> echomsg getreg('"')->count(' ')
> return ''enddef'a b c d e'->setline(1)feedkeys("\<F4>\<F4>")

>
> E1012: Type mismatch; expected string but got func(any): string
>
> The issue comes from this line:
>
> &operatorfunc = (t) => CountSpaces(t)
>
> Which works in legacy:
>
> legacy let &operatorfunc = {t -> CountSpaces(t)}
>
> Test:
>
> vim9scriptnnoremap <expr> <F4><F4> <SID>CountSpaces() .. '_'def CountSpaces(type = ''): string

> if type == ''
> legacy let &operatorfunc = {t -> CountSpaces(t)}
> return 'g@'
> endif
> normal! '[V']y
> echomsg getreg('"')->count(' ')
> return ''enddef'a b c d e'->setline(1)feedkeys("\<F4>\<F4>")
>
> 4
>
> ------------------------------

>
> Also, still in Vim9, we can not use a partial; neither with :set:
>
> set operatorfunc=function(Zd,\ [cmd])
>
> Test:
>
> vim9scriptnnoremap <expr> zd <SID>ZdSetup('zd')def ZdSetup(cmd: string): string
> set operatorfunc=function(Zd,\ [cmd])
> return ***@***.***'enddefdef Zd(cmd: string, type: string)
> execute 'normal! ' .. cmdenddefvar lines =<< trim END

> some folded lines {{{
> some folded lines
> some folded lines }}}
> ENDlines->setline(1)

> &foldmethod = 'marker'normal zd
>
> E121: Undefined variable: cmd
>
> nor with an assignment:
>
> &operatorfunc = function(Zd, [cmd])
>
> Test:
>
> vim9scriptnnoremap <expr> zd <SID>ZdSetup('zd')def ZdSetup(cmd: string): string

> &operatorfunc = function(Zd, [cmd])
> return ***@***.***'enddefdef Zd(cmd: string, type: string)
> execute 'normal! ' .. cmdenddefvar lines =<< trim END

> some folded lines {{{
> some folded lines
> some folded lines }}}
> ENDlines->setline(1)

> &foldmethod = 'marker'normal zd
>
> E1012: Type mismatch; expected string but got func(...): unknown
>
>
>


You are receiving this because you are subscribed to this thread.

Bram Moolenaar

unread,
Dec 9, 2021, 10:50:45 AM12/9/21
to vim/vim, vim-dev ML, Comment

The option assignment to a lambda in a :def function was implemented with 8.2.3765. I hope it works for you.

The "set" command is not compiled, thus it cannot use variables from its context. You can see this with ":disas":

 set operatorfunc=function(Zd,\ [cmd])
   0 EXEC     set operatorfunc=function(Zd,\ [cmd])

The last example fails because the 'operatorfunc' option is set to the lambda, not the partial.
Perhaps we could make this work but since using a lambda is easier and already works, I don't think we need it.


You are receiving this because you commented.

Bram Moolenaar

unread,
Dec 9, 2021, 10:56:14 AM12/9/21
to vim/vim, vim-dev ML, Comment

Actually, using a lambda doesn't work, because the 'operatorfunc' calls the function without the context of where it was defined.

Reply all
Reply to author
Forward
0 new messages