0

So I was doing some testing on some function I made, let's call it Test_mode,

"let g:Test_mode = 0 " commented to show that it works without defining it first
function! Test_mode()
if g:Test_mode == "off"
  nnoremap <buffer> <Down> <Down>
  nnoremap <buffer> <Up> <Up>
elseif g:Test_mode == "on"
  noremap <buffer><expr> <Down> Another_function()
  noremap <buffer><expr> <Up> Yet_another_function()
endif
endfunction

function! Togglemode(argument)
  redir >> event.log
  echo execute(a:argument)
" where the weird behavior occur
  redir END
endfun

noremap <buffer> <expr> c Togglemode("g:Test_mode")

Again, the above was just me playing around with some vimscript features.

Anyway, it seems to me like the execute + echo combination above make it so that the output is actually the Test_mode variable location (where/line where it's defined) and the content of the function of the same name...

I don't get why execute would do this since my initial attempt was to see if I could turn off/on a variable by passing it as an argument, but that resulted in seeing the function's content echoed on the screen + the variable using the same name.

By the way, this only happen if I use g:Test_mode, so if I do it without passing g: in front, it won't work. And yes, as mentioned in the commented code, it works the same even if I comment the part of the code where the variable is defined...

Is this normal behavior? a bug? I'm just curious if this is really a weird behavior.

P.S: I don't think I was clear enough when I said "it output the content of the function", so I'd like to clarify this point.

Basically, if I use the keybinding set above, which is c in normal mode, I get this as output:

"let g:Test_mode = 0 " commented to show that it works without defining it first
function! Test_mode()
if g:Test_mode == "off"
elseif g:Test_mode == "on"
noremap <buffer> <expr> c Togglemode("g:Test_mode")
Press ENTER or type command to continue

Which is literally the code of the function above. That's the weird behavior I mentioned above.

It would make sense if I had this as output if I was trying to capture whatever execute is executing, but that doesn't look like that to me. It kind of look like more of a string matching of the global variable I passed to execute.

5
  • 1
    I think you're mixing some stuff here. Togglemode("g:Test_mode"): you are calling your function with the string "g:Test_mode" which has nothing to do with the variable if you want to pass the variable as parameter remove the quotes. execute(a:argument): since the argument is a variable holding a boolean it doesn't make sense to execute it, what would make sense would be to have a string representing an actual command to execute.
    – statox
    Mar 15, 2022 at 13:13
  • 1
    Also you are using the same name for a variable and a function: First read :h variable-scope and remember that in most programming language it's usually a bad idea to use non unique names. I know these comments don't really answer your questions (and I'm sure someone else will) but still I think they are good advice to keep in mind.
    – statox
    Mar 15, 2022 at 13:14
  • ah, I never said any of what I shown here was good practices/good idea, since, I did said this was just me playing around or testing things :) also, please try the code I posted with the keybinding and you'll see what I meant by "it output the content of the function"...@statox Mar 15, 2022 at 13:15
  • also, I'm aware I should have tried to put it without quotes, but beside the fact it's beside the point of this question, I did mention I notice this weird behavior when I tried to toggle some global variable, so, if I try and pass it to the Togglemode function, it will only return the content of the variable, not the variable's name as string, which is what I tried to do (although that's as I mentioned, beside the point of this question) @statox Mar 15, 2022 at 13:18
  • feel free to check the question again -- I think I clarified what I meant better :D @statox Mar 15, 2022 at 13:27

1 Answer 1

2

The seemingly odd behavior you're seeing is because the execute() function actually runs its argument as an Ex command.

So when you pass it "g:Test_mode", it's actually running the :g or :global command, using : as a separator, then the Test_mode regex and finally an implicit :p to print the lines matching the regex (which is the default command if :g doesn't get a command explicitly.)

So the output you're seeing is that of:

:g/Test_mode/p

Which you can also get with:

:g:Test_mode

The execute() function is capturing that output as a string, then passing it to echo, so you end up seeing it that way. You'd get the same result if you used:

:echo execute('g/Test_mode/p')

If you want to pass the function a string containing the name of a variable and then get the contents of that variable, you can use eval() instead. For example, for the contents of g:Test_mode:

:echo eval("g:Test_mode")

If you're specifically interested in a global variable and have its name passed as a string, you can also use get() in the g: namespace:

:echo get(g:, 'Test_mode')

In that case, you can also pass get() a third argument to use in case g:Test_mode is not actually defined.

2
  • 1
    Thanks a lot! I thought this was too odd of an issue to be answered/known :) I wonder, is this even documented in some way? Mar 16, 2022 at 13:26
  • 1
    nevermind, it's documented (looked into the inline links on your answer). Cheers Mar 16, 2022 at 13:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.