Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vim - Overwrite Plugin Scoped Function

Tags:

vim

I've a plugin in Vim and I don't like the behavior of a single function within it. But it isn't rly a case to open a pull request, but more an extension for it.
I know that overwriting a function ist possible by using a bang as postfix, as soon as the new definition comes after the previous one. But how can I do such thing, if this method is scoped to a script within a plugin?
I wasn't able to find a hint in _Vim_s help, nor by request a search engine. Anybody aware of this topic, at least if he can say that it is simply not possible.

A short example:
plugin/autoload/plugin.vim

  ...
  function! s:foo() {
       // behavior I would like to adjust
  }
  ...

~/.vimrc

  function! foo() {
       // the "correct" behavior
  }


Thanks for any help!

like image 419
weilbith Avatar asked Oct 17 '25 20:10

weilbith


2 Answers

Actually it is possible. But as @romainl said, you'd better suggest your patch to the plugin maintainer or ask for a variation point.

Regarding the how.

First, you'll need to identify the script number of this autoload plugin. Let's say that :scriptname says it's 210. In order to do that automatically I have a lh#askvim#scriptid() function in my library plugin that does the job -- see the current definition at the end of the answer.

Then, to override the s:foo() function, you'll need to provide a new definition for

function! <SNR>210_Foo()
   new definition
endfunction

(I've just tested it with vim 8.0-1157)

IOW, we can override a script-local function. However, I haven't found how to override a script-local variable directly without a reference to its s: dictionary. We could inject setter/getter functions to a specific variable or a function that returns the local s: dictionary.


lh#askvim#scriptid() current definition is the following

" Function: lh#askvim#execute(command) {{{3
" @since Version 4.0.0
if exists('*execute')
  function! lh#askvim#execute(command) abort
    return split(execute(a:command), "\n")
  endfunction
else
  function! lh#askvim#execute(command) abort
    return s:beware_running_through_client_server ? [] : split(lh#askvim#exe(a:command), "\n")
  endfunction
endif

" Function: lh#askvim#scriptnames() {{{3
function! lh#askvim#scriptnames() abort
  let scripts = lh#askvim#execute('scriptnames')
  let s:scripts = map(copy(scripts), 'split(v:val, "\\v:=\\s+")')
  call lh#list#map_on(s:scripts, 1, 'fnamemodify(v:val, ":p")')
  return s:scripts
endfunction

" Function: lh#askvim#scriptid(name) {{{3
function! lh#askvim#scriptid(name, ...) abort
  let last_change = get(a:, 1, 0)
  if last_change || !exists('s:scripts')
    call lh#askvim#scriptnames()
  endif
  let matches = filter(copy(s:scripts), 'v:val[1] =~ a:name')
  if len(matches) > 1
    throw "Too many scripts match `".a:name."`: ".string(matches)
  elseif empty(matches)
    if last_change
      throw "No script match `".a:name."`"
    else
      return lh#askvim#scriptid(a:name, 1)
    endif
  endif
  return matches[0][0]
endfunction
like image 87
Luc Hermitte Avatar answered Oct 20 '25 16:10

Luc Hermitte


That is not possible.

s:foo() is scoped to the script it belongs to (see :help s:) so it can't be accessed from anywhere else.

  1. Fork it.
  2. Make the desired changes to your fork.
  3. Use your fork instead of the original.
  4. Consider submitting a pull request.
like image 40
romainl Avatar answered Oct 20 '25 16:10

romainl