Vim - Pydoc plugin not working with operator mapping - vim

I have a small operator mapping for use with the Pydoc plugin. The code for it is below:
nnoremap <buffer> <localleader>d :set operatorfunc=<SID>PydocOperator<cr>g#
vnoremap <buffer> <localleader>d :<c-u>call <SID>PydocOperator(visualmode())<cr>
function! s:PydocOperator(type)
let l:orig_register = ##
if a:type ==# 'v'
normal! `<v`>y
elseif a:type ==# 'char'
normal! `[v`]y
else
return
endif
execute 'Pydoc ' . shellescape(##)
let ## = l:orig_register
endfunction
However, vim throws an error:
E116: Invalid arguments for function <SNR>117_ShowPyDoc
The same error happens if I copy some text manually and run this command:
execute 'Pydoc ' . shellescape(##)
This is very odd, considering that the :Pydoc should work as a normal command, taking one argument as its input. I looked at the code where the :Pydoc command is defined, (that line of code is here) and discovered that passing an argument to the :Pydoc command that is in quotes might be causing an issue. So I ran :Pydoc 'sys' to see if it would throw the same error as the operator mapping, which it did. So if it is having an issue with the quotes around the argument, how do I format the execute command so that it doesn't give an invalid argument?

The shellescape() function is not necessary for the :Pydoc command. shellescape includes quotes in the returned string, which causes :Pydoc to self destruct. However, if the command were :grep, for instance, shellescape would need to be used.
Relevant help topics:
:help shellescape()
:help 'operatorfunc'
:help :map-operator

Related

How to pass Vim function argument to a command?

I'm using the following function + command to invoke the debugger in Vim:
function! TermDebugArm(executable)
packadd termdebug
let g:termdebugger="arm-none-eabi-gdb"
Termdebug a:executable
endfunction
command! -complete=file -nargs=1 TermDebugArm :call TermDebugArm(<f-args>)
Unfortunately, the Termdebug command gets the literal argument "a:executable" and not the actual value it should represent (i.e. the filename passed to the command that called the function).
What am I doing wrong?
You need to use the :execute command to build a command from strings, which will allow you to use the value of a:executable as a literal:
execute "Termdebug ".a:executable
Or you can use the feature of :execute that will join multiple arguments with a space, so you don't need an explicit concatenation:
execute "Termdebug" a:executable
See :help :execute.

How to execute a command from cnoremap?

I want to map <M-o> to quickly look up buflist from command-mode.
Here's an example normal-mode mapping:
nnoremap <M-o> :ls<CR>
Here's an attempt to do the same for command mode using execute():
cnoremap <M-o> <C-r>=execute(':ls')<CR>
This however simply appends whitespace-separated :ls results to the command line, with no side effect of the :ls table actually showing.
Is there a way to execute a command without leaving command-mode?
Update:
Closest I've come to cracking this using Vimscript is the following:
function! RunCmdFromCmdline(cmd)
let l:cmd_line = getcmdline()
echo execute(a:cmd)
return l:cmd_line
endfunction
cnoremap <M-o> <C-\>eRunCmdFromCmdline(':ls')<CR>
The echo makes it so the results of a:cmd do pop on the screen, however in invisible font. I tried playing with :redraw <bar> in different combinations with no success.
Exiting from the command mode from within a <C-r>= or <C-\>e prompt does not seem possible.
You can save the contents of the current command line in a register, then run ls and paste the contents of the register:
cnoremap <esc>o <home>let #x="<end>"<cr>:ls<cr>:<c-r>x
So if command line looks like this:
:echo 'some'
type <M-o> to get:
:ls
1 %a + "filename" line 1
:echo 'some'
This works. It uses global variables, but arguably this is an acceptable use for them, since there's only a single command-line you can be in at a time.
function! SaveCmdline()
let g:save_cmdline = getcmdline()
let g:save_cmdpos = getcmdpos()
return ''
endfunction
function! RestoreCmdline()
call setcmdpos(g:save_cmdpos)
return g:save_cmdline
endfunction
cnoremap <M-o> <C-\>eSaveCmdline()<CR>ls<CR>:<C-\>eRestoreCmdline()<CR>
This works with whatever quotes or unfinished syntax you have in your command-line, restores the full command-line and the cursor to the position it was before running the command, doesn't mess with any registers (only touches the two global variables) and doesn't mess with command-line history either (see q: command for history.)
It does have one tiny bug, in that cnoremap actually also works when you're typing a search expression (after / or ?) among a few other sub-modes (see getcmdtype() for a list of those.) If you use the mapping there, it will search for "ls" and then restore your search term as an Ex command, oops...
You can probably use getcmdtype() to detect that and possibly only perform the actions while you're typing an Ex command and not in the other modes.
UPDATE: This fixes the last part, only executing the command while in the Ex command-line, not in search or other context. It also makes the interjected command parameterizable.
function! CmdlineExecute(cmd)
if getcmdtype() != ':'
return ''
endif
return "\<C-\>eSaveCmdline()\r".a:cmd.
\ "\r:\<C-\>eRestoreCmdline()\r"
endfunction
cnoremap <expr> <M-o> CmdlineExecute('ls')
(Note this still needs the definition of the two functions above.)

Getting 'E488: Trailing characters' With the following function

Does anyone know why I'm getting the following error? I'm mapping the following function
function! ToggleCompileErrors()
:if w:syntastic_is_open == 1
:call SyntasticReset()<CR>
let w:syntastic_is_open = 0
:else
:call SyntasticCheck()<CR>
let w:syntastic_is_open = 1
:endif
endfunction
With this command
command ToggleCompileErrors :call ToggleCompileErrors()
and it is getting called by the following keymapping
nnoremap <Leader>b :ToggleCompileErrors<CR>
And I don't know if it makes a difference but I am using neovim 0.2.2
Watch your modes. That <CR> :help key-notation is necessary for mappings, but not inside functions, which use Ex commands.
Likewise, you don't need to prefix commands inside a function with : (and this is rather odd here, especially because you're not even consistent about it). : is a normal mode command that enters command-line mode. As commands in a function already are Ex commands, the : is not needed.

How to let and execute in the same line?

I was trying to make a simple setup for Perl in Vim and used this:
function! PerlEnvSetup()
nnoremap <F10> :let f=expand("%")|vnew|execute '.!perl "' . f . '"'<CR>
endf
autocmd FileType perl :call PerlEnvSetup()<CR>
But when I do vim XX.pl, the error in the bottom saying:
Error detected while processing function PerlEnvSetup:
line 1:
E121: Undefined variable: f
E15: Invalid expression: '.!perl "' . f . '"'<CR>
Error detected while processing FileType Auto commands for "perl":
E488: Trailing characters
And the window splits into two (which should not happen unless I press F10). What am I doing wrong here?
What you are seeing, is that the | is used to separate commands (and therefore explicitly ends your :nnoremap command.
You therefore want to use the special idiom <Bar> as described by the help below :h map_bar
Here's another option:
function! PerlExecuteCurrentFile()
let f=expand("%")
vnew
execute '.!perl "' . f . '"'
endf
function! PerlEnvSetup()
nnoremap <F10> :call PerlExecuteCurrentFile()<CR>
endf

How to get a function's return value after the :e command in Vim

I wrote a function to get the full path of the current file under the cursor
nmap <F12> :echo GetFullPath()<cr>
function! GetFullPath()
let currentFile=expand("<cfile>")
let afterChangeSlash=substitute(currentFile,"/","\\","g")
let fullPath="e:\\Test\\".afterChangeSlash
return fullPath
endfunction
When I call the function after the :echo command, I get the expected result,like:
:echo GetFullPath()
e:\Test\test.h
However,When I call it after the :e(edit) command:
:e GetFullPath()
Vim just create a new file named GetFullPath()
Why the command :e will treat a function call literally while the command :echo won't?
You can use :execute to build your ex command string and execute it:
:exe "e ".GetFullPath()
Or use the `=` syntax to expand a Vim expression:
:e `=GetFullPath()`
If you check the help for :edit and :echo, you'll notice that the former expects its argument to be the file name (literally), while :echo expects an expression which will be evaluated.
Some ex commands expect to be given an expression, while some others expect to be given a string. For your case to make it work use exec:
nmap <F12> :exec 'e ' . GetFullPath()

Resources