"============================================================================= " FILE: installer.vim " AUTHOR: Shougo Matsushita " License: MIT license {{{ " Permission is hereby granted, free of charge, to any person obtaining " a copy of this software and associated documentation files (the " "Software"), to deal in the Software without restriction, including " without limitation the rights to use, copy, modify, merge, publish, " distribute, sublicense, and/or sell copies of the Software, and to " permit persons to whom the Software is furnished to do so, subject to " the following conditions: " " The above copyright notice and this permission notice shall be included " in all copies or substantial portions of the Software. " " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS " OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF " MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. " IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY " CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, " TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE " SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. " }}} "============================================================================= let s:save_cpo = &cpo set cpo&vim let s:install_info_version = '3.0' let s:log = [] let s:updates_log = [] function! neobundle#installer#update(bundles) abort "{{{ if neobundle#util#is_sudo() call neobundle#util#print_error( \ '"sudo vim" is detected. This feature is disabled.') return endif let all_bundles = neobundle#config#get_enabled_bundles() call neobundle#commands#helptags(all_bundles) call s:reload(filter(copy(a:bundles), \ "v:val.sourced && !v:val.disabled && v:val.rtp != ''")) call s:save_install_info(all_bundles) let lazy_bundles = filter(copy(all_bundles), 'v:val.lazy') call neobundle#util#merge_bundle_files( \ lazy_bundles, 'ftdetect') call neobundle#util#merge_bundle_files( \ lazy_bundles, 'after/ftdetect') " For neovim remote plugins NeoBundleRemotePlugins endfunction"}}} function! neobundle#installer#build(bundle) abort "{{{ if !empty(a:bundle.build_commands) \ && neobundle#config#check_commands(a:bundle.build_commands) call neobundle#installer#log( \ printf('|%s| ' . \ 'Build dependencies not met. Skipped', a:bundle.name)) return 0 endif " Environment check. let build = get(a:bundle, 'build', {}) if type(build) == type('') let cmd = build elseif neobundle#util#is_windows() && has_key(build, 'windows') let cmd = build.windows elseif neobundle#util#is_mac() && has_key(build, 'mac') let cmd = build.mac elseif neobundle#util#is_cygwin() && has_key(build, 'cygwin') let cmd = build.cygwin elseif !neobundle#util#is_windows() && has_key(build, 'linux') \ && !executable('gmake') let cmd = build.linux elseif !neobundle#util#is_windows() && has_key(build, 'unix') let cmd = build.unix elseif has_key(build, 'others') let cmd = build.others else return 0 endif call neobundle#installer#log('Building...') let cwd = getcwd() try call neobundle#util#cd(a:bundle.path) if !neobundle#util#has_vimproc() let result = neobundle#util#system(cmd) if neobundle#util#get_last_status() call neobundle#installer#error(result) else call neobundle#installer#log(result) endif else call s:async_system(cmd) endif catch " Build error from vimproc. let message = (v:exception !~# '^Vim:')? \ v:exception : v:exception . ' ' . v:throwpoint call neobundle#installer#error(message) return 1 finally call neobundle#util#cd(cwd) endtry return neobundle#util#get_last_status() endfunction"}}} function! neobundle#installer#reinstall(bundles) abort "{{{ let bundles = neobundle#util#uniq(a:bundles) for bundle in bundles if bundle.type ==# 'none' \ || bundle.local \ || bundle.normalized_name ==# 'neobundle' \ || (bundle.sourced && \ index(['vimproc', 'unite'], bundle.normalized_name) >= 0) call neobundle#installer#error( \ printf('|%s| Cannot reinstall the plugin!', bundle.name)) continue endif " Reinstall. call neobundle#installer#log( \ printf('|%s| Reinstalling...', bundle.name)) " Save info. let arg = copy(bundle.orig_arg) " Remove. call neobundle#commands#clean(1, bundle.name) call call('neobundle#parser#bundle', [arg]) endfor call s:save_install_info(neobundle#config#get_neobundles()) " Install. call neobundle#commands#install(0, \ join(map(copy(bundles), 'v:val.name'))) call neobundle#installer#update(bundles) endfunction"}}} function! neobundle#installer#get_reinstall_bundles(bundles) abort "{{{ call neobundle#installer#_load_install_info(a:bundles) let reinstall_bundles = filter(copy(a:bundles), \ "neobundle#config#is_installed(v:val.name) \ && v:val.type !=# 'none' \ && !v:val.local \ && v:val.path ==# v:val.installed_path \ && v:val.uri !=# v:val.installed_uri") if !empty(reinstall_bundles) call neobundle#util#print_error( \ 'Reinstall bundles are detected!') for bundle in reinstall_bundles echomsg printf('%s: %s -> %s', \ bundle.name, bundle.installed_uri, bundle.uri) endfor let cwd = neobundle#util#substitute_path_separator(getcwd()) let warning_bundles = map(filter(copy(reinstall_bundles), \ 'v:val.path ==# cwd'), 'v:val.path') if !empty(warning_bundles) call neobundle#util#print_error( \ 'Warning: current directory is the \ reinstall bundles directory! ' . string(warning_bundles)) endif let ret = confirm('Reinstall bundles now?', "yes\nNo", 2) redraw if ret != 1 return [] endif endif return reinstall_bundles endfunction"}}} function! neobundle#installer#get_updated_bundles_message(bundles) abort "{{{ let msg = '' let installed_bundles = filter(copy(a:bundles), \ "v:val.old_rev == ''") if !empty(installed_bundles) let msg .= "\nInstalled bundles:\n". \ join(map(copy(installed_bundles), \ "' ' . v:val.name"), "\n") endif let updated_bundles = filter(copy(a:bundles), \ "v:val.old_rev != ''") if !empty(updated_bundles) let msg .= "\nUpdated bundles:\n". \ join(map(updated_bundles, \ "' ' . v:val.name . (v:val.commit_count == 0 ? '' \ : printf('(%d change%s)', \ v:val.commit_count, \ (v:val.commit_count == 1 ? '' : 's'))) \ . (v:val.uri =~ '^\\h\\w*://github.com/' ? \"\\n\" \ . printf(' %s/compare/%s...%s', \ substitute(substitute(v:val.uri, '\\.git$', '', ''), \ '^\\h\\w*:', 'https:', ''), \ v:val.old_rev, v:val.new_rev) : '')") \ , "\n") endif return msg endfunction"}}} function! neobundle#installer#get_errored_bundles_message(bundles) abort "{{{ if empty(a:bundles) return '' endif let msg = "\nError installing bundles:\n".join( \ map(copy(a:bundles), "' ' . v:val.name"), "\n") let msg .= "\n" let msg .= "Please read the error message log with the :message command.\n" return msg endfunction"}}} function! neobundle#installer#get_sync_command(bang, bundle, number, max) abort "{{{ let type = neobundle#config#get_types(a:bundle.type) if empty(type) return ['E: Unknown Type', ''] endif let is_directory = isdirectory(a:bundle.path) let cmd = type.get_sync_command(a:bundle) if cmd == '' return ['', 'Not supported sync action.'] elseif (is_directory && !a:bang \ && a:bundle.install_rev ==# \ neobundle#installer#get_revision_number(a:bundle)) return ['', 'Already installed.'] endif let message = printf('(%'.len(a:max).'d/%d): |%s| %s', \ a:number, a:max, a:bundle.name, cmd) return [cmd, message] endfunction"}}} function! neobundle#installer#get_revision_lock_command(bang, bundle, number, max) abort "{{{ let type = neobundle#config#get_types(a:bundle.type) if empty(type) return ['E: Unknown Type', ''] endif let cmd = type.get_revision_lock_command(a:bundle) if cmd == '' return ['', ''] endif return [cmd, ''] endfunction"}}} function! neobundle#installer#get_revision_number(bundle) abort "{{{ let cwd = getcwd() let type = neobundle#config#get_types(a:bundle.type) if !isdirectory(a:bundle.path) \ || !has_key(type, 'get_revision_number_command') return '' endif let cmd = type.get_revision_number_command(a:bundle) if cmd == '' return '' endif try call neobundle#util#cd(a:bundle.path) let rev = neobundle#util#system(cmd) if type.name ==# 'vba' || type.name ==# 'raw' " If rev is ok, the output is the checksum followed by the filename " separated by two spaces. let pat = '^[0-9a-f]\+ ' . a:bundle.path . '/' . \ fnamemodify(a:bundle.uri, ':t') . '$' return (rev =~# pat) ? matchstr(rev, '^[0-9a-f]\+') : '' else " If rev contains spaces, it is error message return (rev !~ '\s') ? rev : '' endif finally call neobundle#util#cd(cwd) endtry endfunction"}}} function! s:get_commit_date(bundle) abort "{{{ let cwd = getcwd() try let type = neobundle#config#get_types(a:bundle.type) if !isdirectory(a:bundle.path) || \ !has_key(type, 'get_commit_date_command') return 0 endif call neobundle#util#cd(a:bundle.path) return neobundle#util#system( \ type.get_commit_date_command(a:bundle)) finally call neobundle#util#cd(cwd) endtry endfunction"}}} function! neobundle#installer#get_updated_log_message(bundle, new_rev, old_rev) abort "{{{ let cwd = getcwd() try let type = neobundle#config#get_types(a:bundle.type) call neobundle#util#cd(a:bundle.path) let log_command = has_key(type, 'get_log_command') ? \ type.get_log_command(a:bundle, a:new_rev, a:old_rev) : '' let log = (log_command != '' ? \ neobundle#util#system(log_command) : '') return log != '' ? log : \ (a:old_rev == a:new_rev) ? '' \ : printf('%s -> %s', a:old_rev, a:new_rev) finally call neobundle#util#cd(cwd) endtry endfunction"}}} function! neobundle#installer#sync(bundle, context, is_unite) abort "{{{ let a:context.source__number += 1 let num = a:context.source__number let max = a:context.source__max_bundles let before_one_day = localtime() - 60 * 60 * 24 let before_one_week = localtime() - 60 * 60 * 24 * 7 if a:context.source__bang == 1 && \ a:bundle.frozen let [cmd, message] = ['', 'is frozen.'] elseif a:context.source__bang == 1 && \ a:bundle.uri ==# a:bundle.installed_uri && \ a:bundle.updated_time < before_one_week \ && a:bundle.checked_time >= before_one_day let [cmd, message] = ['', 'Outdated plugin.'] else let [cmd, message] = \ neobundle#installer#get_sync_command( \ a:context.source__bang, a:bundle, \ a:context.source__number, a:context.source__max_bundles) endif if cmd == '' " Skipped. call neobundle#installer#log(s:get_skipped_message( \ num, max, a:bundle, '', message), a:is_unite) return elseif cmd =~# '^E: ' " Errored. call neobundle#installer#update_log( \ printf('(%'.len(max).'d/%d): |%s| %s', \ num, max, a:bundle.name, 'Error'), a:is_unite) call neobundle#installer#error(cmd[3:]) call add(a:context.source__errored_bundles, \ a:bundle) return endif call neobundle#installer#log(message, a:is_unite) let cwd = getcwd() try let lang_save = $LANG let $LANG = 'C' " Cd to bundle path. call neobundle#util#cd(a:bundle.path) let rev = neobundle#installer#get_revision_number(a:bundle) let process = { \ 'number' : num, \ 'rev' : rev, \ 'bundle' : a:bundle, \ 'output' : '', \ 'status' : -1, \ 'eof' : 0, \ 'start_time' : localtime(), \ } if isdirectory(a:bundle.path) && !a:bundle.local let rev_save = a:bundle.rev try " Force checkout HEAD revision. " The repository may be checked out. let a:bundle.rev = '' call neobundle#installer#lock_revision( \ process, a:context, a:is_unite) finally let a:bundle.rev = rev_save endtry endif if has('nvim') && a:is_unite " Use neovim async jobs let process.proc = jobstart( \ iconv(cmd, &encoding, 'char'), { \ 'on_stdout' : function('s:job_handler'), \ 'on_stderr' : function('s:job_handler'), \ 'on_exit' : function('s:job_handler'), \ }) elseif neobundle#util#has_vimproc() let process.proc = vimproc#pgroup_open(vimproc#util#iconv( \ cmd, &encoding, 'char'), 0, 2) " Close handles. call process.proc.stdin.close() call process.proc.stderr.close() else let process.output = neobundle#util#system(cmd) let process.status = neobundle#util#get_last_status() endif finally let $LANG = lang_save call neobundle#util#cd(cwd) endtry call add(a:context.source__processes, process) endfunction"}}} function! neobundle#installer#check_output(context, process, is_unite) abort "{{{ if has('nvim') && a:is_unite && has_key(a:process, 'proc') let is_timeout = (localtime() - a:process.start_time) \ >= a:process.bundle.install_process_timeout if !has_key(s:job_info, a:process.proc) return endif let job = s:job_info[a:process.proc] if !job.eof && !is_timeout let output = join(job.candidates[: -2], "\n") if output != '' let a:process.output .= output call neobundle#util#redraw_echo(output) endif let job.candidates = job.candidates[-1:] return else if is_timeout call jobstop(a:process.proc) endif let output = join(job.candidates, "\n") if output != '' let a:process.output .= output call neobundle#util#redraw_echo(output) endif let job.candidates = [] endif let status = job.status elseif neobundle#util#has_vimproc() && has_key(a:process, 'proc') let is_timeout = (localtime() - a:process.start_time) \ >= a:process.bundle.install_process_timeout let output = vimproc#util#iconv( \ a:process.proc.stdout.read(-1, 300), 'char', &encoding) if output != '' let a:process.output .= output call neobundle#util#redraw_echo(output) endif if !a:process.proc.stdout.eof && !is_timeout return endif call a:process.proc.stdout.close() let status = a:process.proc.waitpid()[1] else let is_timeout = 0 let status = a:process.status endif let num = a:process.number let max = a:context.source__max_bundles let bundle = a:process.bundle if bundle.rev != '' || !a:context.source__bang " Restore revision. let rev_save = bundle.rev try if !a:context.source__bang && bundle.rev == '' " Checkout install_rev revision. let bundle.rev = bundle.install_rev endif call neobundle#installer#lock_revision( \ a:process, a:context, a:is_unite) finally let bundle.rev = rev_save endtry endif let rev = neobundle#installer#get_revision_number(bundle) let updated_time = s:get_commit_date(bundle) let bundle.checked_time = localtime() if is_timeout || status let message = printf('(%'.len(max).'d/%d): |%s| %s', \ num, max, bundle.name, 'Error') call neobundle#installer#update_log(message, a:is_unite) call neobundle#installer#error(bundle.path) call neobundle#installer#error( \ (is_timeout ? 'Process timeout.' : \ split(a:process.output, '\n'))) call add(a:context.source__errored_bundles, \ bundle) elseif a:process.rev ==# rev if updated_time != 0 let bundle.updated_time = updated_time endif call neobundle#installer#log(s:get_skipped_message( \ num, max, bundle, '', 'Same revision.'), a:is_unite) else call neobundle#installer#update_log( \ printf('(%'.len(max).'d/%d): |%s| %s', \ num, max, bundle.name, 'Updated'), a:is_unite) if a:process.rev != '' let log_messages = split( \ neobundle#installer#get_updated_log_message( \ bundle, rev, a:process.rev), '\n') let bundle.commit_count = len(log_messages) call call((!has('vim_starting') ? 'neobundle#installer#update_log' \ : 'neobundle#installer#log'), [ \ map(log_messages, "printf('|%s| ' . \ substitute(v:val, '%', '%%', 'g'), bundle.name)"), \ a:is_unite \ ]) else let bundle.commit_count = 0 endif if updated_time == 0 let updated_time = bundle.checked_time endif let bundle.updated_time = updated_time let bundle.installed_uri = bundle.uri let bundle.revisions[updated_time] = rev let bundle.old_rev = a:process.rev let bundle.new_rev = rev if neobundle#installer#build(bundle) \ && confirm('Build failed. Uninstall "' \ .bundle.name.'" now?', "yes\nNo", 2) == 1 " Remove. call neobundle#commands#clean(1, bundle.name) else call add(a:context.source__synced_bundles, bundle) endif endif let bundle.install_rev = rev let a:process.eof = 1 endfunction"}}} function! neobundle#installer#lock_revision(process, context, is_unite) abort "{{{ let num = a:process.number let max = a:context.source__max_bundles let bundle = a:process.bundle let bundle.new_rev = neobundle#installer#get_revision_number(bundle) let [cmd, message] = \ neobundle#installer#get_revision_lock_command( \ a:context.source__bang, bundle, num, max) if cmd == '' || bundle.new_rev ==# bundle.rev " Skipped. return 0 elseif cmd =~# '^E: ' " Errored. call neobundle#installer#error(bundle.path) call neobundle#installer#error(cmd[3:]) return -1 endif if bundle.rev != '' call neobundle#installer#log( \ printf('(%'.len(max).'d/%d): |%s| %s', \ num, max, bundle.name, 'Locked'), a:is_unite) call neobundle#installer#log(message, a:is_unite) endif let cwd = getcwd() try " Cd to bundle path. call neobundle#util#cd(bundle.path) let result = neobundle#util#system(cmd) let status = neobundle#util#get_last_status() finally call neobundle#util#cd(cwd) endtry if status call neobundle#installer#error(bundle.path) call neobundle#installer#error(result) return -1 endif endfunction"}}} function! neobundle#installer#get_release_revision(bundle, command) abort "{{{ let cwd = getcwd() let rev = '' try call neobundle#util#cd(a:bundle.path) let rev = get(neobundle#util#sort_human( \ split(neobundle#util#system(a:command), '\n')), -1, '') finally call neobundle#util#cd(cwd) endtry return rev endfunction"}}} function! s:save_install_info(bundles) abort "{{{ let s:install_info = {} for bundle in filter(copy(a:bundles), \ "!v:val.local && has_key(v:val, 'updated_time')") " Note: Don't save local repository. let s:install_info[bundle.name] = { \ 'checked_time' : bundle.checked_time, \ 'updated_time' : bundle.updated_time, \ 'installed_uri' : bundle.installed_uri, \ 'installed_path' : bundle.path, \ 'revisions' : bundle.revisions, \ } endfor call neobundle#util#writefile('install_info', \ [s:install_info_version, string(s:install_info)]) " Save lock file call s:save_lockfile(a:bundles) endfunction"}}} function! neobundle#installer#_load_install_info(bundles) abort "{{{ let install_info_path = \ neobundle#get_neobundle_dir() . '/.neobundle/install_info' if !exists('s:install_info') call s:source_lockfile() let s:install_info = {} if filereadable(install_info_path) try let list = readfile(install_info_path) let ver = list[0] sandbox let s:install_info = eval(list[1]) if ver !=# s:install_info_version \ || type(s:install_info) != type({}) let s:install_info = {} endif catch endtry endif endif call map(a:bundles, "extend(v:val, get(s:install_info, v:val.name, { \ 'checked_time' : localtime(), \ 'updated_time' : localtime(), \ 'installed_uri' : v:val.uri, \ 'installed_path' : v:val.path, \ 'revisions' : {}, \}))") return s:install_info endfunction"}}} function! s:get_skipped_message(number, max, bundle, prefix, message) abort "{{{ let messages = [a:prefix . printf('(%'.len(a:max).'d/%d): |%s| %s', \ a:number, a:max, a:bundle.name, 'Skipped')] if a:message != '' call add(messages, a:prefix . a:message) endif return messages endfunction"}}} function! neobundle#installer#log(msg, ...) abort "{{{ let msg = neobundle#util#convert2list(a:msg) if empty(msg) return endif call extend(s:log, msg) call s:append_log_file(msg) endfunction"}}} function! neobundle#installer#update_log(msg, ...) abort "{{{ let is_unite = get(a:000, 0, 0) if !(&filetype == 'unite' || is_unite) call neobundle#util#redraw_echo(a:msg) endif call neobundle#installer#log(a:msg) let s:updates_log += neobundle#util#convert2list(a:msg) endfunction"}}} function! neobundle#installer#echomsg(msg) abort "{{{ call neobundle#util#redraw_echomsg(a:msg) call neobundle#installer#log(a:msg) let s:updates_log += neobundle#util#convert2list(a:msg) endfunction"}}} function! neobundle#installer#error(msg) abort "{{{ let msgs = neobundle#util#convert2list(a:msg) if empty(msgs) return endif call extend(s:log, msgs) call extend(s:updates_log, msgs) call neobundle#util#print_error(msgs) call s:append_log_file(msgs) endfunction"}}} function! s:append_log_file(msg) abort "{{{ if g:neobundle#log_filename == '' return endif let msg = a:msg " Appends to log file. if filereadable(g:neobundle#log_filename) let msg = readfile(g:neobundle#log_filename) + msg endif let dir = fnamemodify(g:neobundle#log_filename, ':h') if !isdirectory(dir) call mkdir(dir, 'p') endif call writefile(msg, g:neobundle#log_filename) endfunction"}}} function! neobundle#installer#get_log() abort "{{{ return s:log endfunction"}}} function! neobundle#installer#get_updates_log() abort "{{{ return s:updates_log endfunction"}}} function! neobundle#installer#clear_log() abort "{{{ let s:log = [] let s:updates_log = [] endfunction"}}} function! neobundle#installer#get_progress_message(bundle, number, max) abort "{{{ return printf('(%'.len(a:max).'d/%d) [%-20s] %s', \ a:number, a:max, \ repeat('=', (a:number*20/a:max)), a:bundle.name) endfunction"}}} function! neobundle#installer#get_tags_info() abort "{{{ let path = neobundle#get_neobundle_dir() . '/.neobundle/tags_info' if !filereadable(path) return [] endif return readfile(path) endfunction"}}} function! s:save_lockfile(bundles) abort "{{{ let path = neobundle#get_neobundle_dir() . '/NeoBundle.lock' let dir = fnamemodify(path, ':h') if !isdirectory(dir) call mkdir(dir, 'p') endif return writefile(sort(map(filter(map(copy(a:bundles), \ '[v:val.name, neobundle#installer#get_revision_number(v:val)]'), \ "v:val[1] != '' && v:val[1] !~ '\s'"), \ "printf('NeoBundleLock %s %s', \ escape(v:val[0], ' \'), v:val[1])")), path) endfunction"}}} function! s:source_lockfile() abort "{{{ let path = neobundle#get_neobundle_dir() . '/NeoBundle.lock' if filereadable(path) execute 'source' fnameescape(path) endif endfunction"}}} function! s:reload(bundles) abort "{{{ if empty(a:bundles) return endif call filter(copy(a:bundles), 'neobundle#config#rtp_add(v:val)') silent! runtime! ftdetect/**/*.vim silent! runtime! after/ftdetect/**/*.vim silent! runtime! plugin/**/*.vim silent! runtime! after/plugin/**/*.vim " Call hooks. call neobundle#call_hook('on_post_source', a:bundles) endfunction"}}} let s:job_info = {} function! s:job_handler(job_id, data, event) abort "{{{ if !has_key(s:job_info, a:job_id) let s:job_info[a:job_id] = { \ 'candidates' : [], \ 'eof' : 0, \ 'status' : -1, \ } endif let job = s:job_info[a:job_id] if a:event ==# 'exit' let job.eof = 1 let job.status = a:data return endif let lines = a:data let candidates = job.candidates if !empty(lines) && lines[0] != "\n" && !empty(job.candidates) " Join to the previous line let candidates[-1] .= lines[0] call remove(lines, 0) endif let candidates += map(lines, "iconv(v:val, 'char', &encoding)") endfunction"}}} function! s:async_system(cmd) abort "{{{ let proc = vimproc#pgroup_open(a:cmd) " Close handles. call proc.stdin.close() while !proc.stdout.eof if !proc.stderr.eof " Print error. call neobundle#installer#error(proc.stderr.read_lines(-1, 100)) endif call neobundle#util#redraw_echo(proc.stdout.read_lines(-1, 100)) endwhile if !proc.stderr.eof " Print error. call neobundle#installer#error(proc.stderr.read_lines(-1, 100)) endif call proc.waitpid() endfunction"}}} let &cpo = s:save_cpo unlet s:save_cpo