(I originally posted this question on vi.stackexchange, and I'm posting it again here upon D. Ben Knoble's suggestion.)
Put this content in ~/.vim/plugin/messingaround.vim
vim9script noclear
import autoload 'messingaround.vim' as xyz
if exists("g:loaded_messingaround")
finish
endif
g:loaded_messingaround = 1
var save_cpo = &cpo
set cpo&vim
if !hasmapto('<Plug>MessingAround;')
map <unique> <Leader>a <Plug>MessingAround;
endif
noremap <unique> <script> <Plug>MessingAround; :echo <ScriptCmd>xyz.Say(expand("<cword>"))<CR>
&cpo = save_cpo
and this content in ~/.vim/autoload/messingaround.vim
vim9script noclear
export def Say(str: string): string
return str
enddef
Temporarly empty your ~/.vimrc (I couldn't reproduce the problem with -u NONE, but I haven't looked much into it) and open a new Vim session, type a word into it, and hit Leadera.
You'll see the command line being populated with :echo (trailing space included), but no trace of Say being run.
The word under the cursor should be echoed.
Notice that if I don't use the autoload mechanism, i.e. if I
Say in the ~/.vim/plugin/messingaround.vim file (and delete the file in autoload),export<ScriptCmd>xyz. to <SID>the function behaves as I expect.
Not sure if in the original case I expect the wrong thing, though.
8.2.4651
Operating system: Archlinux (up-to-date)
Terminal: URxvt
Value of $TERM: rxvt-unicode-256color
Shell: GNU bash 5.1.16(1)
No response
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
You'll see the command line being populated with :echo (trailing space included), but no trace of Say being run.
:echo expects an expression as argument, but <ScriptCmd>...<CR> is not an expression. <ScriptCmd>...<CR> just asks Vim to execute an arbitrary Ex command regardless of the current mode.
In contrast, <SID>Say(...) is an expression, so it works.
I suspect that you want to refer to an autoload function in a mapping using the script.func syntax, but you can't. There are still indeed a few contexts where it's not possible:
:help i_CTRL-R_=:help c_CTRL-R_=:help c_CTRL-\_e:help stl-%{:help 'statusline' and :help 'tabline' (%!Func()):help input() (3rd {completion} argument)Not sure if we need yet another pseudo-key, some other mechanism, or just more documentation on this pitfall.
In any case, if you need to refer to an autoload function in a context where script.func is not valid, you can still use the legacy name script#func.
Unrelated, but you don't need to save and restore 'cpoptions'; it's done automatically:
A side effect of
:vim9scriptis that the 'cpoptions' option is set to the
Vim default value, like with: >
:set cpo&vim
One of the effects is that |line-continuation| is always enabled.
The original value of 'cpoptions' is restored at the end of the script, while
flags added or removed in the script are also added to or removed from the
original value to get the same effect. The order of flags may change.
In the |vimrc| file sourced on startup this does not happen.
See :help vim9-namespace.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
In any case, if you need to refer to an autoload function in a context where script.func is not valid, you can still use the legacy name script#func.
Now that I think about it, that will be an issue when we start to refactor import autoload into import lazy. Then, there will no longer be any legacy name to fall back on. So, maybe we do need some new syntax/mechanism; I don't know.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
In any case, if you need to refer to an autoload function in a context where script.func is not valid, you can still use the legacy name script#func.
Now that I think about it, that will be an issue when we start to refactor
import autoloadintoimport lazy. Then, there will no longer be any legacy name to fall back on. So, maybe we do need some new syntax/mechanism; I don't know.
Do you mean I shouldn't follow your original suggestion if I want my code to be compatible with wherever Vim9 will get to?
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
Do you mean I shouldn't follow your original suggestion if I want my code to be compatible with wherever Vim9 will get to?
import lazy will not deprecate import autoload, so if you keep using the latter, then you will be able to use the legacy name script#func anywhere an autoload function name is accepted.
The issue will arise if you later decide to refactor import autoload into import lazy.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
Regarding
:echoexpects an expression as argument, but<ScriptCmd>...<CR>is not an expression.<ScriptCmd>...<CR>just asks Vim to execute an arbitrary Ex command regardless of the current mode.In contrast,
<SID>Say(...)is an expression, so it works.
and
I suspect that you want to refer to an autoload function in a mapping using the
script.funcsyntax, but you can't. There are still indeed a few contexts where it's not possible:
would you mind quoting the bits of the documentation from which I'd have deduced the 3 phrases I've styled in bold above?
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
... is not an expression
From :help <ScriptCmd>:
<ScriptCmd> is like <Cmd>
Then from :help <Cmd>:
The special text <Cmd> begins a "command mapping", it executes the command
directly without changing modes.
This states that a command is executed. Not that an expression is evaluated. At no point in the rest of this help tag is the word "expression" ever used.
<SID>Say(...) is an expression
<SID> is irrelevant here. What matters is Say() which is a function call. And a function call is an expression.
From :help expression-syntax or :help expr10:
function(expr1, ...) function call
but you can't
To be clear: you can use script.func if and only if you're in a Vim9 context. The issue is that a mapping is run in the global context where only the legacy syntax is allowed. That's why <ScriptCmd> was introduced: to let you use the Vim9 syntax in a mapping. In practice, this covers most cases, but not all. For example, in your original mapping:
noremap <unique> <script> <Plug>MessingAround; :echo <ScriptCmd>xyz.Say(expand("<cword>"))<CR>
^---------^
I guess you originally wanted to write something like this:
noremap <unique> <script> <Plug>MessingAround; :echo <C-R>=xyz.Say(expand("<cword>"))<CR>
^----^
Then found out that it didn't work. I don't think this limitation is currently documented, which is why I wrote:
Not sure if we need yet another pseudo-key, some other mechanism, or just more documentation on this pitfall.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
It was mentioned before that a limitation of a mapping is that it just replaces typeahead by other text.
No script context can be used when the mapping expands. Thus the relation with the script must be included when
the mapping is defined. For that works fine.
Since you cannot use for a function like importName.Func() you need to split it up:
def LocalFunc(): string
return xyz.Say(expand("<cword>"))
enddef
noremap <unique> <script> <Plug>MessingAround; :echo <SID>LocalFunc()<CR>
Disclaimer: I haven't tried it!
Perhaps it's possible to add something like for calling an imported function, but is it worth it?
Moving as much as possible into a compiled function is more useful.
Although, if the only thing that LocalFunc() would do is call the imported function, it does add some overhead.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
Hmm, perhaps we can make this work:
:echo <SID>xyz.Say(expand("<cword>"))<CR>
Need to recognize "{name}{dot}", replace with "{scriptnr}_" and when called load that script if needed, before calling the function.
Seems a bit like magic, but I think we have all the information needed.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
Perhaps it's possible to add something like for calling an imported function, but is it worth it?
I don't know, but at least we might want to improve the help with regards to how a user is expected to refer to a function in a mapping. In particular:
<ScriptCmd> and <CR>, and for <expr> mappings, which are run in the script context; this lets you use the Vim9 syntax if your mapping is installed in a Vim9 scriptvim9script import 'script.vim' def Wrapper(): string return script.func() enddef ioremap <key> <C-R>=<SID>Wrapper()<CR>
All of this might already be documented here and there, but maybe we should have 1 help tag somewhere at :help vim9 which centralizes all of this information.
Hmm, perhaps we can make this work:
Yes, I think this would fix most, if not all the remaining cases.
Seems a bit like magic, but I think we have all the information needed.
It's not really magic. From a mapping, we can refer to a script-local function via <SID>. Why couldn't we do the same for a variable? I already mentioned this pitfall here.
That's yet another pitfall which has always bothered me. We can access a script-local function from the global context, provided we know its ID (via the pseudo-key ), but we can't do the same for a script-local variable.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
It's not really magic. From a mapping, we can refer to a script-local function via . Why couldn't we do the same for a variable?
Ah, I guess script is not technically a variable. Still, it looks like one.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
With patch 8.2.4747 this should work.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
There actually is another solution, which was mentioned before and is used in a test: Use a funcref variable:
import './XsomeExport.vim' as some
var Funcy = some.Funcx
nnoremap <F3> :call <sid>Funcy(42)<cr>
But the straigtforward way of using <SID>some.Funcx() is much nicer.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
With patch 8.2.4747 this should work.
It works for a function defined under import/:
vim9script var dir = '/tmp/.vim' dir->delete('rf') &runtimepath = dir dir ..= '/import' dir->mkdir('p') var lines =<< trim END vim9script export def Func() echomsg 'from Func()' enddef END lines->writefile(dir .. '/script.vim') import 'script.vim' nnoremap <F3> <Cmd>call <SID>script.Func()<CR> feedkeys("\<F3>")
from Func()
But not for a function defined under autoload/:
vim9script var dir = '/tmp/.vim' dir->delete('rf') &runtimepath = dir dir ..= '/autoload' dir->mkdir('p') var lines =<< trim END vim9script export def Func() echomsg 'from Func()' enddef END lines->writefile(dir .. '/script.vim') import autoload 'script.vim' nnoremap <F3> <Cmd>call <SID>script.Func()<CR> feedkeys("\<F3>")
E117: Unknown function: <SNR>2_Func
Could it work?
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
Oh yes, a function from an actual autoload script isn't defined with but with # in the name.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
To be clear: you can use
script.funcif and only if you're in a Vim9 context. The issue is that a mapping is run in the global context where only the legacy syntax is allowed. That's why<ScriptCmd>was introduced: to let you use the Vim9 syntax in a mapping. In practice, this covers most cases, but not all. For example, in your original mapping:
I think I have a deep misunderstanding here. Does the above mean that using :*map commands means using non-Vim9 syntax?
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
Does the above mean that using :*map commands means using non-Vim9 syntax?
In a mapping, the Vim9 syntax is allowed if and only if you're in a Vim9 script, and the code is:
<ScriptCmd> and the next <CR><expr> mappingHere are 2 examples:
vim9script nnoremap <F3> <ScriptCmd>echo (() => 'this is a Vim9 lambda')()<CR> feedkeys("\<F3>")
this is a Vim9 lambda
vim9script nnoremap <expr> <F3> (() => ':echomsg "this is a Vim9 lambda"<CR>')() feedkeys("\<F3>")
this is a Vim9 lambda
But still, the above looks a bit like the mappings will always be legacy.
I think you expect a mapping to let you use the Vim9 syntax just because it was installed from a Vim9 script. That would be possible if mappings were running in the context of the script where they were installed (just like user-defined Ex commands and autocmds). But unfortunately, they do not.
Mappings run in the global context. This means that when the mapping is actually processed, Vim has no clue about its script (let alone whether it's a Vim9 script or a legacy script). That's why we need this weird <SID> pseudo-key. This tells Vim that when it installs a mapping, it should remember the script ID, so that later, when the mapping is finally used, Vim knows in which script it can find the function to call.
It was annoying/confusing to have to use the legacy syntax in a mapping installed in a Vim9 script, which is why I opened the issue #9499
In response, Vim improved the mappings in 2 ways:
<expr> mapping: this allows you to use the Vim9 syntax in the rhs of an <expr> mapping<ScriptCmd> which again makes Vim remember the script context: this allows you to use the Vim9 syntax between <ScriptCmd> and the next <CR>Besides, thanks to your report, Vim has improved in 2 other ways:
<SID>script.Func()Maybe I just don't understand how legacy and vim9 script are meant to overlap,
Ideally, there would never be the need to use the legacy syntax in a Vim9 script. But there are still a few cases where it's required. It all boils down to this issue: where you are when you write your code, and where Vim is when it runs this code, are not necessarily the same places. If Vim is in the global context when it runs your code, then the Vim9 syntax will not work.
In my experience, those cases are rare though.
for how long, and when (if) the latter will be a standalone language that needn't be combined with legacy to allow everything legacy allowed.
Vim9 will be released in a month or 2. What you can see right now will not be much different from the release.
That doesn't mean that the syntax can not be improved further. It can. I have a few ideas, but not enough time.
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()
Sorry for posting in a Closed issue, but I couldn't find the answer in the :h pages.
I read many interesting workaround (including a wrapper, a funcref, re-defining your function so that you just <ScriptCmd>:call xyz.Say(...), and Bram also proposed the actually nicer <SID>xyz.Say()).
My question is: as it is today (April 2023), which is the suggested method?
Does <SID>xyz.Myfunc() work in Vim9? If so, is there any plans to update the docs?
—
Reply to this email directly, view it on GitHub.
You are receiving this because you are subscribed to this thread.![]()