blob: 4cd2ba60a116cafb8b8c5a9ec16c7041b63cfb37 [file] [log] [blame]
" see README
" this file contains code which is always used
" code which is used for installing / updating etc should go into vam/install.vim
" don't need a plugin. If you want to use this plugin you call Activate once
" anyway
augroup VIM_ADDON_MANAGER
autocmd!
autocmd BufRead,BufNewFile *-addon-info.txt,addon-info.json
\ setlocal ft=addon-info
\ | setlocal syntax=json
\ | syn match Error "^\s*'"
autocmd BufWritePost *-addon-info.txt,addon-info.json call vam#ReadAddonInfo(expand('%'))
augroup end
fun! vam#DefineAndBind(local,global,default)
return 'if !exists('.string(a:global).') | let '.a:global.' = '.a:default.' | endif | let '.a:local.' = '.a:global
endf
" assign g:os
for os in split('amiga beos dos32 dos16 mac macunix os2 qnx unix vms win16 win32 win64 win32unix', ' ')
if has(os) | let g:os = os | break | endif
endfor
let g:is_win = g:os[:2] == 'win'
exec vam#DefineAndBind('s:c','g:vim_addon_manager','{}')
let s:c['auto_install'] = get(s:c,'auto_install', 0)
" repository locations:
let s:c['plugin_sources'] = get(s:c,'plugin_sources', {})
" if a plugin has an item here the dict value contents will be written as plugin info file
let s:c['activated_plugins'] = get(s:c,'activated_plugins', {})
" gentoo users may install VAM system wide. In that case s:d is not writeable.
" In the future this may be put into a gentoo specific patch.
let s:d = expand('<sfile>:h:h:h')
let s:c['plugin_root_dir'] = get(s:c, 'plugin_root_dir', filewritable(s:d) ? s:d : '~/.vim/vim-addons' )
unlet s:d
" ensure we have absolute paths (windows doesn't like ~/.. ) :
let s:c['plugin_root_dir'] = expand(s:c['plugin_root_dir'])
let s:c['dont_source'] = get(s:c, 'dont_source', 0)
let s:c['plugin_dir_by_name'] = get(s:c, 'plugin_dir_by_name', 'vam#DefaultPluginDirFromName')
" More options that are used for plugins installation are listed in
" autoload/vam/install.vim
" for testing it is necessary to avoid the "Press enter to continue lines"
" (cygwin?). Thus provide an option making all shell commands silent
" However don't set this to 1 by default. If something goes wrong I want users
" to see what went wrong. Not everybody knows how to debug VimL!
let s:c['silent_shell_commands'] = get(s:c,'silent_shell_commands', 0)
if g:is_win
" if binary-utils path exists then add it to PATH
let s:c['binary_utils'] = get(s:c,'binary_utils',s:c['plugin_root_dir'].'\binary-utils')
let s:c['binary_utils_bin'] = s:c['binary_utils'].'\dist\bin'
if isdirectory(s:c['binary_utils'])
let $PATH=$PATH.';'.s:c['binary_utils_bin']
endif
endif
" additional plugin sources should go into your .vimrc or into the repository
" called "vim-addon-manager-known-repositories" referenced here:
if executable('git')
let s:c['plugin_sources']["vim-addon-manager-known-repositories"] = { 'type' : 'git', 'url': 'git://github.com/MarcWeber/vim-addon-manager-known-repositories.git' }
else
let s:c['plugin_sources']["vim-addon-manager-known-repositories"] = { 'type' : 'archive', 'url': 'http://github.com/MarcWeber/vim-addon-manager-known-repositories/tarball/master', 'archive_name': 'vim-addon-manager-known-repositories-tip.tar.gz' }
endif
fun! vam#VerifyIsJSON(s)
" You must allow single-quoted strings in order for writefile([string()]) that
" adds missing addon information to work
let scalarless_body = substitute(a:s, '\v\"%(\\.|[^"\\])*\"|\''%(\''{2}|[^''])*\''|true|false|null|[+-]?\d+%(\.\d+%([Ee][+-]?\d+)?)?', '', 'g')
return scalarless_body !~# "[^,:{}[\\] \t]"
endf
" use join so that you can break the dict into multiple lines. This makes
" reading it much easier
fun! vam#ReadAddonInfo(path)
" don't add "b" because it'll read dos files as "\r\n" which will fail the
" check and evaluate in eval. \r\n is checked out by some msys git
" versions with strange settings
" using eval is evil!
let body = join(readfile(a:path),"")
if vam#VerifyIsJSON(body)
let true=1
let false=0
let null=''
" using eval is now safe!
return eval(body)
else
call vam#Log( "Invalid JSON in ".a:path."!")
return {}
endif
endf
fun! vam#DefaultPluginDirFromName(name)
" this function maps addon names to their storage location. \/: are replaced
" by - (See name rewriting)
return s:c.plugin_root_dir.'/'.substitute(a:name, '[\\/:]\+', '-', 'g')
endfun
fun! vam#PluginDirFromName(...)
return call(s:c.plugin_dir_by_name, a:000, {})
endf
fun! vam#PluginRuntimePath(name)
let info = vam#AddonInfo(a:name)
return vam#PluginDirFromName(a:name).(has_key(info, 'runtimepath') ? '/'.info['runtimepath'] : '')
endf
" doesn't check dependencies!
fun! vam#IsPluginInstalled(name)
let d = vam#PluginDirFromName(a:name)
" this will be dropped in about 12 months which is end of 2012
let old_path=s:c.plugin_root_dir.'/'.substitute(a:name, '[\\/:]\+', '', 'g')
if d != old_path && isdirectory(old_path)
if confirm("VAM has changed addon names policy for name rewriting. Rename ".old_path." to ".d."?", "&Ok") == 1
call rename(old_path, d)
endif
endif
" if dir exists and its not a failed download
" (empty archive directory)
return isdirectory(d)
\ && ( !isdirectory(d.'/archive')
\ || len(glob(d.'/archive/*')) > 0 )
endf
" {} if file doesn't exist
fun! vam#AddonInfo(name)
let infoFile = vam#AddonInfoFile(a:name)
return filereadable(infoFile)
\ ? vam#ReadAddonInfo(infoFile)
\ : {}
endf
" opts: {
" 'plugin_sources': additional sources (used when installing dependencies)
" 'auto_install': when 1 overrides global setting, so you can autoinstall
" trusted repositories only
" }
fun! vam#ActivateRecursively(list_of_names, ...)
let opts = a:0 == 0 ? {} : a:1
for name in a:list_of_names
if !has_key(s:c['activated_plugins'], name)
" break circular dependencies..
let s:c['activated_plugins'][name] = 0
let infoFile = vam#AddonInfoFile(name)
if !filereadable(infoFile) && !vam#IsPluginInstalled(name)
call vam#install#Install([name], opts)
endif
let info = vam#AddonInfo(name)
let dependencies = get(info,'dependencies', {})
" activate dependencies merging opts with given repository sources
" sources given in opts will win
call vam#ActivateAddons(keys(dependencies),
\ extend(copy(opts), {
\ 'plugin_sources' : extend(copy(dependencies), get(opts, 'plugin_sources',{})),
\ 'requested_by' : [name] + get(opts, 'requested_by', [])
\ }))
" source plugin/* files ?
let rtp = vam#PluginRuntimePath(name)
call add(opts['new_runtime_paths'], rtp)
let s:c['activated_plugins'][name] = 1
endif
endfor
endf
let s:top_level = 0
" see also ActivateRecursively
" Activate activates the plugins and their dependencies recursively.
" I sources both: plugin/*.vim and after/plugin/*.vim files when called after
" .vimrc has been sourced which happens when you activate plugins manually.
fun! vam#ActivateAddons(...) abort
let args = copy(a:000)
if a:0 == 0 | return | endif
if type(args[0])==type("")
" way of usage 1: pass addon names as function arguments
" Example: ActivateAddons("name1","name2")
" This way of calling has two flaws:
" - doesn't scale due to amount of args limitation
" - you can't pass autoinstall=1
" Therefore we should get rid of this way..
" verify that all args are strings only because errors are hard to debug
if !empty(filter(copy(args),'type(v:val) != type("")'))
throw "Bad argument to vam#ActivateAddons: only Strings are permitted. Use ActivateAddons(['n1','n2',..], {..}) to pass options dictionary"
endif
let args=[args, {}]
else
" way of usage 2: pass addon names as list optionally passing options
" Example: ActivateAddons(["name1","name2"], { options })
let args=[args[0], get(args,1,{})]
endif
" now opts should be defined
" args[0] = plugin names
" args[1] = options
let opts = args[1]
let topLevel = !has_key(opts, 'new_runtime_paths')
" add new_runtime_paths state if not present in opts yet
let new_runtime_paths = get(opts, 'new_runtime_paths',[])
let opts['new_runtime_paths'] = new_runtime_paths
call call('vam#ActivateRecursively', args)
if topLevel
" deferred tasks:
" - add addons to runtimepath
" - add source plugin/**/*.vim files in case Activate was called long
" after .vimrc has been sourced
" add paths after ~/.vim but before $VIMRUNTIME
" don't miss the after directories if they exist and
" put them last! (Thanks to Oliver Teuliere)
let rtp = split(&runtimepath, '\v(\\@<!(\\.)*\\)@<!\,')
let escapeComma = 'escape(v:val, '','')'
let after = filter(map(copy(new_runtime_paths), 'v:val."/after"'), 'isdirectory(v:val)')
if !s:c.dont_source
let &runtimepath=join(rtp[:0] + map(copy(new_runtime_paths), escapeComma)
\ + rtp[1:]
\ + map(after, escapeComma),
\ ",")
endif
unlet rtp
if !has('vim_starting')
for rtp in new_runtime_paths
call vam#GlobThenSource(rtp.'/plugin/**/*.vim')
call vam#GlobThenSource(rtp.'/after/plugin/**/*.vim')
endfor
endif
for rtp in new_runtime_paths
" filetype off/on would do the same ?
call vam#GlobThenSource(rtp.'/ftdetect/*.vim')
endfor
endif
endfun
fun! vam#DisplayAddonInfo(name)
let plugin = get(g:vim_addon_manager['plugin_sources'], a:name, {})
let name = a:name
if empty(plugin) && a:name =~ '^\d\+$'
" try to find by script id
let dict = filter(copy(g:vim_addon_manager['plugin_sources']), 'get(v:val,"vim_script_nr","")."" == '.string(1*a:name))
if (empty(dict))
throw "unkown script ".a:name
else
let plugin = get(values(dict), 0, {})
let name = keys(dict)[0]
endif
end
if empty(plugin)
echo "Invalid plugin name: " . a:name
return
endif
echo '========================'
echo 'VAM name: '.name
for key in keys(plugin)
echo key . ': ' . plugin[key]
endfor
endfun
fun! vam#DisplayAddonsInfo(names)
call vam#install#LoadPool()
for name in a:names
call vam#DisplayAddonInfo(name)
endfor
endfun
fun! vam#GlobThenSource(glob)
if s:c.dont_source | return | endif
for file in split(glob(a:glob),"\n")
exec 'source '.fnameescape(file)
endfor
endf
augroup VIM_PLUGIN_MANAGER
autocmd VimEnter * call vam#Hack()
augroup end
" hack: Vim sources plugin files after sourcing .vimrc
" Vim doesn't source the after/plugin/*.vim files in other runtime
" paths. So do this *after* plugin/* files have been sourced
fun! vam#Hack()
" now source after/plugin/**/*.vim files explicitly. Vim doesn't do it (hack!)
for p in keys(s:c['activated_plugins'])
call vam#GlobThenSource(vam#PluginDirFromName(p).'/after/plugin/**/*.vim')
endfor
endf
fun! vam#AddonInfoFile(name)
" history:
" 1) plugin-info.txt was the first name (deprecated)
" 2) a:name-addon-info.txt was the second recommended name (maybe deprecated - no hurry)
" 3) Now the recommended way is addon-info.json because:
" - you can rename a script without having to rename the file
" - json says all about its contents (Let's hope all browsers still render
" it in a readable way
let p = vam#PluginDirFromName(a:name)
let default = p.'/addon-info.json'
let choices = [ default , p.'/plugin-info.txt', p.'/'.a:name.'-addon-info.txt']
for f in choices
if filereadable(f)
return f
endif
endfor
return default
endfun
" looks like an error but is not. Catches users attention. Logs to :messages
fun! vam#Log(s, ...)
let hi = a:0 > 0 ? a:1 : 'WarningMsg'
exec 'echohl '. hi
for l in split(a:s, "\n", 1)
if empty(l)
echom ' '
else
echom l
endif
endfor
echohl None
endfun
" If you want these commands witohut activating plugins call
" vam#ActivateAddons([]) with empty list. Not moving them into plugin/vam.vim
" to prevent additional IO seeks.
" its likely that the command names change introducing nice naming sheme
" Not sure which is best. Options:
" 1) *VAM 2) Addon* 3) VAM*
" 3 seems to be best but is more to type.
" Using 1) you can still show all commands by :*VAM<c-d> but this scheme is
" less common. So 2) is my favorite right now. I'm too lazy to break things at
command! -nargs=* -complete=customlist,vam#install#NotInstalledAddonCompletion InstallAddons :call vam#install#Install([<f-args>])
command! -nargs=* -complete=customlist,vam#install#AddonCompletion ActivateAddons :call vam#ActivateAddons([<f-args>])
command! -nargs=* -complete=customlist,vam#install#AddonCompletion AddonsInfo :call vam#DisplayAddonsInfo([<f-args>])
command! -nargs=* -complete=customlist,vam#install#InstalledAddonCompletion ActivateInstalledAddons :call vam#ActivateAddons([<f-args>])
command! -nargs=* -complete=customlist,vam#install#UpdateCompletion UpdateAddons :call vam#install#Update([<f-args>])
command! -nargs=0 UpdateActivatedAddons exec 'UpdateAddons '.join(keys(g:vim_addon_manager['activated_plugins']),' ')
command! -nargs=* -complete=customlist,vam#install#UninstallCompletion UninstallNotLoadedAddons :call vam#install#UninstallAddons([<f-args>])
" plugin name completion function:
augroup VAM
" yes, this overwrites omnifunc set by vim-dev plugin for instance. I don't
" care. You install plugins, then you usually restart anyway.
autocmd BufRead,BufNewFile *.vim,*vimrc inoremap <buffer> <C-x><c-p> <c-o>:setlocal omnifunc=vam#install#CompleteAddonName<cr><c-x><c-o>
augroup end
" plugin completion:
" vim: et ts=8 sts=2 sw=2