Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I get E127 from this vimscript?

Tags:

python

vim

I have the following vimscript .vim/ftplugin dir:

" change to header file from c file or vice versa
function! CppAlter()
python << endpy
import vim
import os
bufferNames = [os.path.basename(b.name) for b in vim.buffers]
currentBufName = vim.eval("expand('%:p:t')")
currentBufStem, currentBufExt = os.path.splitext(currentBufName)
if currentBufExt == ".cpp" or currentBufExt == ".c" or currentBufExt == ".cc":
    altBufName1 = currentBufStem + ".h"
    altBufName2 = currentBufStem + ".hpp"
    if altBufName1 in bufferNames:
        vim.command("b " + altBufName1)
    elif altBufName2 in bufferNames:
        vim.command("b " + altBufName2)
    else:
        raise ValueError("No header file corresponding to this c file")
elif currentBufExt == ".h" or currentBufExt == ".hpp":
    altBufName1 = currentBufStem + ".cpp"
    altBufName2 = currentBufStem + ".c"
    altBufName3 = currentBufStem + ".cc"
    if altBufName1 in bufferNames:
        vim.command("b " + altBufName1)
    elif altBufName2 in bufferNames:
        vim.command("b " + altBufName2)
    elif altBufName3 in bufferNames:
        vim.command("b " + altBufName3)
    else:
        raise ValueError("No c file corresponding to this header file")
else:
    raise ValueError("This is not a c type file")
endpy
endfunction

nnoremap <leader>vc :call CppAlter()<cr>
inoremap <leader>vc <esc>:call CppAlter()<cr>

When I open vim I get an error:

" vim.error: Vim(function):E127: Cannot redefine function CppAlter: It is in use

But if I save it in /tmp and explicitly :so /tmp/x.vim, there is no error msg. Wondering what is wrong here.

like image 696
qed Avatar asked Mar 25 '14 11:03

qed


2 Answers

Inside your function, you're loading another buffer (e.g. vim.command("b " + altBufName1)). When that buffer has the same filetype, the current ftplugin script is sourced again as part of the filetype plugin handling, but the original function hasn't returned yet, so you get the E127.

Solution

I recommend putting the function itself into an autoload script, e.g. in ~/.vim/autoload/ft/cppalter.vim:

function! ft#cppalter#CppAlter()
    ...

Your ftplugin script becomes much smaller and efficient, as the function is only sourced once:

nnoremap <leader>vc :call ft#cppalter#CppAlter()<cr>
...

(You should probably use :nnoremap <buffer> here to limit the mapping's scope.)

Alternative

If you don't want to break this up, move the function definition(s) to the bottom and add a guard, like:

nnoremap <leader>vc :...

if exists('*CppAlter')
    finish
endif
function! CppAlter()
    ...
like image 52
Ingo Karkat Avatar answered Nov 08 '22 17:11

Ingo Karkat


I encountered an interesting case of E127, which pretty much sums up why it would occur in almost any situation. Let me explain.

First, let's look at what the docs say.

                                                                E127 E122
                        When a function by this name already exists and [!] is
                        not used an error message is given.  There is one
                        exception: When sourcing a script again, a function
                        that was previously defined in that script will be
                        silently replaced.
                        When [!] is used, an existing function is silently
                        replaced. **Unless it is currently being executed, that
                        is an error.**

For the next part notice what the last line has to say.

Let's understand this by an example. Below is a function that guesses and sources the current script based on what filetype it has. Notice how the exec command will initiate an endless recursive sourcing of the current file on calling this function.

function! s:SourceScriptImplicit()
  if !&readonly
    w
  endif
  let l:bin=system("which " . &filetype)[:-2]
  let l:sourcecommand=
        \ #{
        \ vim:         "source %",
        \ sh:          "!source %",
        \ javascript:  "!node %",
        \ python:      "!python3 %"
        \ }
  exec l:sourcecommand[split(l:bin, "/")[-1]]
endfunction

To fix this, simply remove the recursive part out of the function.

function! s:SourceScriptImplicit()
  if !&readonly
    w
  endif
  let l:bin=system("which " . &filetype)[:-2]
  let l:sourcecommand=
        \ #{
        \ vim:         "source %",
        \ sh:          "!source %",
        \ javascript:  "!node %",
        \ python:      "!python3 %"
        \ }
  return l:sourcecommand[split(l:bin, "/")[-1]]
endfunction

nn <leader>so :exec <SID>SourceScriptImplicit()<cr>

Now it works perfectly!

like image 39
rexagod Avatar answered Nov 08 '22 17:11

rexagod