From af89938633897782ae9148e679ffa4e23d1d4ac0 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Sun, 13 Jul 2014 03:54:53 +0900 Subject: [PATCH] Real-time progress report This commit is almost identical to the reverted 60b907c87f693e3e4ce2cc91aa10d9125cfe441e, but it tries to avoid high CPU load by not printing every line. --- plug.vim | 106 ++++++++++++++++++++++++++++++++++---------- test/workflow.vader | 2 +- 2 files changed, 84 insertions(+), 24 deletions(-) diff --git a/plug.vim b/plug.vim index 38f9c6b..bef33cf 100644 --- a/plug.vim +++ b/plug.vim @@ -323,7 +323,11 @@ function! s:syntax() syn match plugBracket /[[\]]/ contained syn match plugX /x/ contained syn match plugDash /^-/ + syn match plugPlus /^+/ + syn match plugStar /^*/ syn match plugName /\(^- \)\@<=[^:]*/ + syn match plugInstall /\(^+ \)\@<=[^:]*/ + syn match plugUpdate /\(^* \)\@<=[^:]*/ syn match plugCommit /^ [0-9a-z]\{7} .*/ contains=plugRelDate,plugSha syn match plugSha /\(^ \)\@<=[0-9a-z]\{7}/ contained syn match plugRelDate /([^)]*)$/ contained @@ -334,8 +338,15 @@ function! s:syntax() hi def link plugX Exception hi def link plugBracket Structure hi def link plugNumber Number + hi def link plugDash Special + hi def link plugPlus Constant + hi def link plugStar Boolean + hi def link plugName Label + hi def link plugInstall Function + hi def link plugUpdate Type + hi def link plugError Error hi def link plugRelDate Comment hi def link plugSha Identifier @@ -429,7 +440,21 @@ function! s:update_impl(pull, args) abort let len = len(g:plugs) if has('ruby') && threads > 1 - call s:update_parallel(a:pull, todo, threads) + try + call s:update_parallel(a:pull, todo, threads) + catch + let lines = getline(4, '$') + let printed = {} + silent 4,$d + for line in lines + let name = get(matchlist(line, '^. \([^:]\+\):'), 1, '') + if empty(name) || !has_key(printed, name) + let printed[name] = 1 + call append('$', line) + endif + endfor + echoerr v:exception + endtry else call s:update_serial(a:pull, todo) endif @@ -491,7 +516,6 @@ function! s:update_serial(pull, todo) if !isdirectory(base) call mkdir(base, 'p') endif - execute 'cd '.base let result = s:system( \ printf('git clone --recursive %s -b %s %s 2>&1 && cd %s && git submodule update --init --recursive 2>&1', \ s:shellesc(spec.uri), @@ -520,6 +544,23 @@ endfunction function! s:update_parallel(pull, todo, threads) ruby << EOF + module PlugStream + SEP = ["\r", "\n", nil] + def get_line + buffer = '' + loop do + char = readchar rescue return + if SEP.include? char + buffer << $/ + break + else + buffer << char + end + end + buffer + end + end unless defined?(PlugStream) + def esc arg %["#{arg.gsub('"', '\"')}"] end @@ -535,55 +576,70 @@ function! s:update_parallel(pull, todo, threads) all = VIM::evaluate('copy(a:todo)') limit = VIM::evaluate('get(g:, "plug_timeout", 60)') nthr = VIM::evaluate('a:threads').to_i + maxy = VIM::evaluate('winheight(".")').to_i cd = iswin ? 'cd /d' : 'cd' - done = {} tot = 0 bar = '' skip = 'Already installed' mtx = Mutex.new take1 = proc { mtx.synchronize { running && all.shift } } logh = proc { - cnt = done.length + cnt = $curbuf[2][1...-1].strip.length tot = VIM::evaluate('len(a:todo)') || tot $curbuf[1] = "#{pull ? 'Updating' : 'Installing'} plugins (#{cnt}/#{tot})" $curbuf[2] = '[' + bar.ljust(tot) + ']' VIM::command('normal! 2G') VIM::command('redraw') unless iswin } - log = proc { |name, result, ok| + where = proc { |name| (1..($curbuf.length)).find { |l| $curbuf[l] =~ /^[-+x*] #{name}:/ } } + log = proc { |name, result, type| mtx.synchronize do - bar += ok ? '=' : 'x' - done[name] = true + ing = ![true, false].include?(type) + bar += type ? '=' : 'x' unless ing + b = case type + when :install then '+' when :update then '*' + when true, nil then '-' else 'x' end result = - if ok - ["- #{name}: #{result.lines.to_a.last.strip}"] + if type || type.nil? + ["#{b} #{name}: #{result.lines.to_a.last}"] elsif result =~ /^Interrupted|^Timeout/ - ["x #{name}: #{result}"] + ["#{b} #{name}: #{result}"] else - ["x #{name}"] + result.lines.map { |l| " " << l } + ["#{b} #{name}"] + result.lines.map { |l| " " << l } end + if lnum = where.call(name) + $curbuf.delete lnum + lnum = 4 if ing && lnum > maxy + end result.each_with_index do |line, offset| - $curbuf.append 3 + offset, line.chomp + $curbuf.append (lnum || 4) - 1 + offset, line.gsub(/\e\[./, '').chomp end logh.call end } - bt = proc { |cmd| + bt = proc { |cmd, name, type| begin fd = nil - Timeout::timeout(limit) do - if iswin + data = '' + if iswin + Timeout::timeout(limit) do tmp = VIM::evaluate('tempname()') system("#{cmd} > #{tmp}") data = File.read(tmp).chomp File.unlink tmp rescue nil - else - fd = IO.popen(cmd) - data = fd.read.chomp - fd.close end - [$? == 0, data] + else + fd = IO.popen(cmd).extend(PlugStream) + first_line = true + log_prob = 1.0 / nthr + while line = Timeout::timeout(limit) { fd.get_line } + data << line + log.call name, line.chomp, type if name && (first_line || rand < log_prob) + first_line = false + end + fd.close end + [$? == 0, data.chomp] rescue Timeout::Error, Interrupt => e if fd && !fd.closed? pids = [fd.pid] @@ -616,6 +672,7 @@ function! s:update_parallel(pull, todo, threads) main.kill } + progress = iswin ? '' : '--progress' until all.empty? names = all.keys [names.length, nthr].min.times do @@ -625,10 +682,11 @@ function! s:update_parallel(pull, todo, threads) name = pair.first dir, uri, branch = pair.last.values_at *%w[dir uri branch] branch = esc branch + subm = "git submodule update --init --recursive 2>&1" ok, result = if File.directory? dir dir = esc dir - ret, data = bt.call "#{cd} #{dir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url" + ret, data = bt.call "#{cd} #{dir} && git rev-parse --abbrev-ref HEAD 2>&1 && git config remote.origin.url", nil, nil current_uri = data.lines.to_a.last if !ret if data =~ /^Interrupted|^Timeout/ @@ -642,7 +700,8 @@ function! s:update_parallel(pull, todo, threads) "PlugClean required."].join($/)] else if pull - bt.call "#{cd} #{dir} && git checkout -q #{branch} 2>&1 && git pull origin #{branch} 2>&1 && git submodule update --init --recursive 2>&1" + log.call name, 'Updating ...', :update + bt.call "#{cd} #{dir} && git checkout -q #{branch} 2>&1 && (git pull origin #{branch} #{progress} 2>&1 && #{subm})", name, :update else [true, skip] end @@ -650,7 +709,8 @@ function! s:update_parallel(pull, todo, threads) else FileUtils.mkdir_p(base) d = esc dir.sub(%r{[\\/]+$}, '') - bt.call "#{cd} #{base} && git clone --recursive #{uri} -b #{branch} #{d} 2>&1 && cd #{esc dir} && git submodule update --init --recursive 2>&1" + log.call name, 'Installing ...', :install + bt.call "(git clone #{progress} --recursive #{uri} -b #{branch} #{d} 2>&1 && cd #{esc dir} && #{subm})", name, :install end log.call name, result, ok end diff --git a/test/workflow.vader b/test/workflow.vader index 11b07c1..55c89cc 100644 --- a/test/workflow.vader +++ b/test/workflow.vader @@ -239,7 +239,7 @@ Execute (PlugClean! to remove vim-emoji): Execute (PlugUpdate to install both again): PlugUpdate - AssertEqual 2, len(filter(getline(1, line('$')), 'v:val =~ "Cloning into"')) + AssertEqual 2, len(filter(getline(1, line('$')), 'v:val =~ "^- [^:]*:"')) AssertEqual 3, g:vimrc_reloaded Assert !empty(globpath(&rtp, 'colors/seoul256.vim')), 'seoul256.vim should be found' Assert !empty(globpath(&rtp, 'autoload/emoji.vim')), 'vim-emoji should be found'