[vim/vim] Vim9: cannot use backtick expansion in :folddoopen command in :def function (#7621)

4 views
Skip to first unread message

lacygoill

unread,
Jan 4, 2021, 11:42:26 PM1/4/21
to vim/vim, Subscribed

Describe the bug

In Vim9 script, we cannot use a backtick expansion in a :folddoopen command, nor in a :folddoclosed command.

To Reproduce

Run this shell command:

vim -Nu NONE -S <(cat <<'EOF'
    vim9
    def Func()
        var name = 'test'
        folddoopen echo '`=name`'
    enddef
    Func()
EOF
)

This is echo'ed:

`=name`

Expected behavior

test is echo'ed.

Environment

  • Vim version: 8.2 Included patches: 1-2301
  • OS: Ubuntu 16.04.7 LTS
  • Terminal: xterm(363)

Additional context

The same backtick expansion works fine in a global command:

vim9
def Func()
    var name = 'test'
    g/^/echo '`=name`'
enddef
Func()
test


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or unsubscribe.

Bram Moolenaar

unread,
Jan 5, 2021, 1:02:44 PM1/5/21
to vim/vim, Subscribed


> **Describe the bug**

>
> In Vim9 script, we cannot use a backtick expansion in a `:folddoopen` command, nor in a `:folddoclosed` command.
>
> **To Reproduce**

>
> Run this shell command:
>
> vim -Nu NONE -S <(cat <<'EOF'
> vim9
> def Func()
> var name = 'test'
> folddoopen echo '`=name`'
> enddef
> Func()
> EOF
> )
>
> This is echo'ed:
>
> `=name`
>
> **Expected behavior**
>
> `test` is echo'ed.

I don't think `=expr` is evaluated for an :echo command. But the
problem does reproduce when using :edit, where it is supposed to be
evaluated. This works:

def Edit()
var name = 'xxx'
edit `=name`
enddef

This does not work:

def Edit()
var name = 'xxx'
folddoopen edit `=name`
enddef


--
From "know your smileys":
[:-) Frankenstein's monster

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

Bram Moolenaar

unread,
Jan 5, 2021, 1:24:22 PM1/5/21
to vim/vim, Subscribed

Closed #7621 via ecac591.

Bram Moolenaar

unread,
Jan 5, 2021, 1:24:58 PM1/5/21
to vim...@googlegroups.com, Bram Moolenaar
Ironically it's a lot simpler to expand `=expr` for any command.
That's probably fine in most cases.

--
From "know your smileys":
:-E Has major dental problems

lacygoill

unread,
Jan 6, 2021, 12:22:32 AM1/6/21
to vim/vim, Subscribed

I don't think =expr is evaluated for an :echo command.

Yes, in Vim script legacy, I think a backtick expansion is only evaluated for a command which expects a filename, like :edit and :vimgrep. :echo does not expect a filename but an expression, therefore a backtick expansion should not work after :echo.

Nevertheless, I still thought it would work for several reasons.

First, it already works in a global command:

vim9
def Func()
    var name = 'test'
    g/^/echo '`=name`'
enddef
Func()
test

Second, it was mentioned in this comment:

The value of the variable is inserted in place, thus if you want to use
it as a string you need to add quotes:


def Func()
var name = 'test'
g/^/echo "=name"
enddef

Third, it is documented as working in the replacement field of a substitution command:

https://github.com/vim/vim/blob/b23279d7a2d28de5df942924b77cf23672fc684f/runtime/doc/vim9.txt#L800-L805

And indeed, it works as documented:

vim9
def Replace()
  setline(1, 'pattern')
  var newText = 'blah'
  g/pattern/s/^/`=newText`/
enddef
Replace()
echo getline(1)
blahpattern

But this doesn't work in Vim script legacy:

fu Replace()
  call setline(1, 'pattern')
  let newText = 'blah'
  g/pattern/s/^/`=newText`/
endfu
call Replace()
echo getline(1)
`=newText`pattern

Which means that the syntax has been made valid in more contexts than it was initially designed for. As I didn't know which contexts it was valid in, I assumed it was valid everywhere.

Fourth, we can no longer access a function-local variable in a command which is executed in the global context like :g or :s. For those cases, the backtick expansion could be useful.

That being said, I can understand that we don't allow the syntax to work in too many places, to avoid unexpected expansions. And I don't mind if it's limited to commands expecting filenames as arguments and the replacement field of :s. That's because it doesn't always work anyway. For example, the last time I was tempted to use it, I had to execute an assignment in a :folddoclosed and a :folddoopen command; but the assignment involved a function-local dictionary variable:

" inside a function
let state = {'open': [], 'closed': []}
folddoclosed let state.closed += s:get_state('closed')
folddoopen let state.open += s:get_state('open')

The code worked fine in Vim script legacy, but not in Vim9. I wasn't able to use a backtick expansion either. So, in the end, I moved the function-local variable to the script-local namespace:

# at the script level
var state: dict<list<number>>

# inside a function
...
state = {open: [], closed: []}
folddoclosed state.closed += GetState('closed')
folddoopen state.open += GetState('open')
...

I think this is the general solution for this kind of issue. I have yet to find a single case where it didn't work.

Reply all
Reply to author
Forward
0 new messages