Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

4 choices; YONV variant #8

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,25 @@ Features:
- When Vim starts up, every directory from root to the directory of the file
is traversed and special files such as .(local-)vimrc files are sourced

- Because you don't want to run untrusted code by accident, this plugin
calculates a hash before sourcing. If its unknown you must confirm sourcing
the file. The hash is updated automatically if you write a local vimrc file.
- Because you don't want to run untrusted code by accident, this plugin asks
you before sourcing any given file. Your answer can optionally be "sticky",
i.e. saved for use in the future. The possible answers (the abbreviation
is captitalized) are:

Answer | Read now? | Sticky?
:-----:|:---------:|:------:
Yes | Yes | Yes
Once | Yes | No
No | No | No
neVer | No | Yes

- The plugin also stores a hash of the file's contents; if the file changes,
you'll be asked again, i.e. any sticky answer you gave previously will be
ignored

- If you use vim to edit a local vimrc file, a sticky "yes" is automatically
saved for (that version of) the file, on the assumption that you trust any
edits you yourself have made

- if you change a directory and edit a file the local vimrc files are resourced

Expand Down Expand Up @@ -56,3 +72,4 @@ unless the file belongs to a different owner..
contributors
============
Thiago de Arruda (github.com/tarruba)
Eric Siegerman (github.com/esiegerman)
54 changes: 50 additions & 4 deletions plugin/localvimrc.vim
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ let s:c.hash_fun = get(s:c,'hash_fun','LVRHashOfFile')
let s:c.cache_file = get(s:c,'cache_file', $HOME.'/.vim_local_rc_cache')
let s:c.resource_on_cwd_change = get(s:c, 'resource_on_cwd_change', 1)
let s:last_cwd = ''
let s:cache_format_version=2 " Increment this on any format change

" Map return values from confirm() into our answer codes.
" If confirm() can't provide a good answer, it returns 0; hence we set
" [0] to a safe default.
"
" Besides these, there's also ANS_ASK, which is only used internally;
" it's written to the cache if the user gives a non-sticky answer.
let s:answer_map = ['ANS_NO', 'ANS_YES', 'ANS_ONCE', 'ANS_NO', 'ANS_NEVER']

" very simple hash function using md5 falling back to VimL implementation
fun! LVRHashOfFile(file, seed)
Expand All @@ -26,6 +35,27 @@ fun! LVRHashOfFile(file, seed)
endif
endfun

" Ask the user; return one of the ANS_* strings.
" If they don't provide a useful answer, return dflt.
fun! LVRAsk(prompt, dflt)
let ans = confirm(a:prompt,"&Yes\n&Once\n&No\nne&Ver", a:dflt)
if 0 < ans
return s:answer_map[ans]
else
return dflt
endif
endfun

fun! LVRSaveAnswer(cache, key, hash, ans)
if 'ANS_YES' == a:ans || 'ANS_NEVER' == a:ans
let l:ans = a:ans
else
let l:ans = 'ANS_ASK'
endif
let ce = {'hash': a:hash, 'ans': l:ans}
let a:cache[a:key] = ce
endf

" source local vimrc, ask user for confirmation if file contents change
fun! LVRSource(file, cache)
" always ignore user global .vimrc which Vim sources on startup:
Expand All @@ -34,19 +64,34 @@ fun! LVRSource(file, cache)
let p = expand(a:file)
let h = call(function(s:c.hash_fun), [a:file, a:cache.seed])
" if hash doesn't match or no hash exists ask user to confirm sourcing this file
if get(a:cache, p, 'no-hash') == h || 1 == confirm('source '.p,"&Y\n&n",2)
let a:cache[p] = h
let ce = get(a:cache, p, {'hash':'no-hash', 'ans':'ANS_NO'})
if ce['hash'] == h && ce['ans'] != 'ANS_ASK'
let ans = ce['ans']
else
let ans = LVRAsk('source '.p,'ANS_NO')
endif
" source the file if so requested
if 'ANS_YES' == ans || 'ANS_ONCE' == ans
exec 'source '.fnameescape(p)
endif
call LVRSaveAnswer(a:cache, p, h, ans)
endf

fun! LVRWithCache(F, args)
" for each computer use different unique seed based on time so that its
" harder to find collisions
let cache = filereadable(s:c.cache_file)
\ ? eval(readfile(s:c.cache_file)[0])
\ : {'seed':localtime()}
\ : {}
let c = copy(cache)
" if the cache isn't in the format we understand, just blow it away;
" it's not valuable enough to be worth converting.
" note that we do this whether the file's version is too low *or* too high;
" in either case, we assume that we don't know how to interpret the contents.
let ver = get(cache, 'format_version', -1) " default should never match any real version number
if ver != s:cache_format_version
let cache = {'seed' : localtime(), 'format_version' : s:cache_format_version}
endif
let r = call(a:F, [cache]+a:args)
if c != cache | call writefile([string(cache)], s:c.cache_file) | endif
return r
Expand Down Expand Up @@ -85,7 +130,8 @@ SourceLocalVimrcOnce
" if its you writing a file update hash automatically
fun! LVRUpdateCache(cache)
let f = expand('%:p')
let a:cache[f] = call(function(s:c.hash_fun), [f, a:cache.seed])
call LVRSaveAnswer(a:cache, f, call(function(s:c.hash_fun), [f, a:cache.seed]),
\ 'ANS_ALWAYS')
endf

augroup LOCAL_VIMRC
Expand Down