Unfortunately there is nothing like that.
In my library plugin, I've implemented a generic feature that takes care of restoring options, variables (where restoring may means :unlet
), mappings, or trigger some other actions at the end of a sequence of operations. But it's not meant to be used on the fly. It's too verbose for that.
let cleanup = lh#on#exit()
\.restore('&l:statusline')
\.restore('&isk')
\.restore('b:varname')
\.register('au! SomeAuGroup')
\.restore_cursor()
\.restore_buffer_mapping('<c-y>', 'i')
.....
try
actions...
finally
call cleanup.finalize()
endtry
Back to your concern, I guess you'd like something simple to use. We could imagine something like :MyExec -protect:path=tmp\ value :the old ex-command
. The problem I see is parsing the temporary value sent to path
. I've tried several times to have commands able to reconstruct strings and take other parameters, it's a plain headache. At best we need some unique separator. Something like --
maybe ?
This could give
command! -nargs=1 ProtectExec call s:ProtectExec(<f-args>)
function! s:ProtectExec(cmdline) abort
" Only accepts a single -override parameter
let parts = split(a:cmdline, '\s\+--\s\+')
if len(parts) < 2
throw "ProtectExec: Don't forget to separate the ex-command from the overriden variable/option with '--'"
endif
let cleanup = lh#on#exit()
try
if parts[0] =~ '\v-over%[ride]:'
let [all, var, val; dummy] = matchlist(parts[0], '\v-over%[ride]:([^=]+)\=(.*)')
call cleanup.restore(var)
if empty(var)
throw "ProtectExec: Cannot extract variable name and value from `".parts[0]."`"
endif
" If only options and only one option would be supported,
" then varname could be used in the the finally clause instead
" of the generic on#exit() object.
let varname = lh#option#is_set_locally(var) ? '&l:'.var[1:] : var
exe 'let '.varname.' = '.val
endif
exe parts[1]
finally
call cleanup.finalize()
endtry
endfunction
That is used with:
:ProtectExec -override:&path='toto' -- echo "tmp:".&path
Note that lh#on#exit().restore()
relies on another function: lh#option#is_set_locally()
that I just had to fix in order to correctly recognize global-local options that haven't been locally overridden. This part has been a little tricky (we need to know how the option shall be restored: with :set
or :setlocal
).
let s:k_option_fullname = {
\ 'ai': 'autoindent',
\ 'bs': 'backspace',
\ 'efm': 'errorformat',
\ 'et': 'expandtab',
\ 'ft': 'filetype',
\ 'isk': 'iskeyword',
\ 'rtp': 'runtimepath',
\ 'sw': 'shiftwidth',
\ 'ts': 'tabstop',
\ 'tw': 'textwidth'
\ }
function! lh#option#is_set_locally(option_name, ...) abort
let bufid = get(a:, 1, '%')
if a:option_name =~ '^&\(\([lg]:\)\@!.\)*$'
" options with no explicit scope
let options = getbufvar(bufid, '&')
" Before 7.4.434, getbufvar() returns an empty string instead of an
" empty dict when nothing is found
" Also, older version of vim don't return local options with
" getbufvar(bid, '&')
" In global-local options case, an empty local option is an option that
" hasn't been overriden
let option_name = get(s:k_option_fullname, a:option_name[1:], a:option_name[1:])
if !empty(options) && has_key(options, option_name)
if type(options[option_name]) == type('')
return !empty(getbufvar(bufid, '&l:'.option_name))
else
return getbufvar(bufid, '&l:'.a:option_name[1:]) != getbufvar(bufid, '&g:'.option_name)
endif
endif
endif
return 0
endfunction
IMO, unless you have many cases of commands and options you'd like to override, you'd better off with a dedicated command for searching with a temporary path -- but don't forget to check whether 'path'
is currently a local or a global option.
unlet
. If this sounds OK, I could write a more detailed answerverbose
,hide
and such which essentially do a very similar thing. Not applicable to user stuff though.