diff --git a/autoload/fugitive.vim b/autoload/fugitive.vim index d435878..1860dd0 100644 --- a/autoload/fugitive.vim +++ b/autoload/fugitive.vim @@ -2601,26 +2601,39 @@ function! s:Format(val) abort endif endfunction -function! s:AddHeader(key, value) abort +function! s:AddHeader(to, key, value) abort if empty(a:value) return endif - let before = 1 - while !empty(getline(before)) - let before += 1 - endwhile - call append(before - 1, [a:key . ':' . (len(a:value) ? ' ' . a:value : '')]) - if before == 1 && line('$') == 2 - silent keepjumps 2delete _ - endif + call add(a:to.lines, a:key . ':' . (len(a:value) ? ' ' . a:value : '')) endfunction -function! s:AddSection(label, lines, ...) abort +function! s:AddSection(to, label, lines, ...) abort let note = a:0 ? a:1 : '' if empty(a:lines) && empty(note) return endif - call append(line('$'), ['', a:label . (len(note) ? ': ' . note : ' (' . len(a:lines) . ')')] + s:Format(a:lines)) + call extend(a:to.lines, ['', a:label . (len(note) ? ': ' . note : ' (' . len(a:lines) . ')')] + s:Format(a:lines)) +endfunction + +function! s:AddDiffSection(to, stat, label, files) abort + if empty(a:files) + return + endif + let diff_section = a:stat.diff[a:label] + let expanded = a:stat.expanded[a:label] + let was_expanded = get(getbufvar(a:stat.bufnr, 'fugitive_expanded', {}), a:label, {}) + call extend(a:to.lines, ['', a:label . ' (' . len(a:files) . ')']) + for file in a:files + call add(a:to.lines, s:Format(file)) + if has_key(was_expanded, file.filename) + let [diff, start] = s:StageInlineGetDiff(diff_section, file) + if len(diff) + let expanded[file.filename] = [start] + call extend(a:to.lines, diff) + endif + endif + endfor endfunction function! s:QueryLog(refspec, limit, dir) abort @@ -2642,12 +2655,12 @@ function! s:QueryLogRange(old, new, dir) abort return s:QueryLog([a:old . '..' . a:new], 256, a:dir) endfunction -function! s:AddLogSection(label, log) abort +function! s:AddLogSection(to, label, log) abort if empty(a:log.entries) return endif let label = a:label . ' (' . len(a:log.entries) . (a:log.overflow ? '+' : '') . ')' - call append(line('$'), ['', label] + s:Format(a:log.entries)) + call extend(a:to.lines, ['', label] + s:Format(a:log.entries)) endfunction let s:rebase_abbrevs = { @@ -2720,10 +2733,11 @@ function! fugitive#BufReadStatus(cmdbang) abort unlet! b:fugitive_expanded endif unlet! b:fugitive_reltime b:fugitive_type + let dir = s:Dir() + let stat = {'bufnr': bufnr(''), 'reltime': reltime(), 'work_tree': s:Tree(dir)} try - let config = fugitive#Config() + let config = fugitive#Config(dir) - let dir = s:Dir() let cmd = [dir] if amatch !~# '^fugitive:' && s:cpath($GIT_INDEX_FILE !=# '' ? resolve(s:GitIndexFileEnv()) : fugitive#Find('.git/index', dir)) !=# s:cpath(amatch) let cmd += [{'env': {'GIT_INDEX_FILE': FugitiveGitPath(amatch)}}] @@ -2733,8 +2747,7 @@ function! fugitive#BufReadStatus(cmdbang) abort call add(cmd, '--no-optional-locks') endif - let tree = s:Tree(dir) - if !empty(tree) + if !empty(stat.work_tree) let status_cmd = cmd + ['status', '-bz'] call add(status_cmd, fugitive#GitVersion(2, 11) ? '--porcelain=v2' : '--porcelain') let status_exec = fugitive#Execute(status_cmd, function('len')) @@ -2842,20 +2855,20 @@ function! fugitive#BufReadStatus(cmdbang) abort endif let diff_cmd = cmd + ['-c', 'diff.suppressBlankEmpty=false', '-c', 'core.quotePath=false', 'diff', '--color=never', '--no-ext-diff', '--no-prefix'] - let diff = {'Staged': {'stdout': ['']}, 'Unstaged': {'stdout': ['']}} + let stat.diff = {'Staged': {'stdout': ['']}, 'Unstaged': {'stdout': ['']}} if len(staged) - let diff['Staged'] = fugitive#Execute(diff_cmd + ['--cached'], function('len')) + let stat.diff['Staged'] = fugitive#Execute(diff_cmd + ['--cached'], function('len')) endif if len(unstaged) - let diff['Unstaged'] = fugitive#Execute(diff_cmd + ['--'] + map(copy(unstaged), 'tree . "/" . v:val.relative[0]'), function('len')) + let stat.diff['Unstaged'] = fugitive#Execute(diff_cmd + ['--'] + map(copy(unstaged), 'stat.work_tree . "/" . v:val.relative[0]'), function('len')) endif - let section_files = {'Staged': {}, 'Unstaged': {}} + let stat.files = {'Staged': {}, 'Unstaged': {}} for dict in staged - let section_files['Staged'][dict.filename] = dict + let stat.files['Staged'][dict.filename] = dict endfor for dict in unstaged - let section_files['Unstaged'][dict.filename] = dict + let stat.files['Unstaged'][dict.filename] = dict endfor let fetch_remote = config.Get('branch.' . branch . '.remote', 'origin') @@ -2957,37 +2970,29 @@ function! fugitive#BufReadStatus(cmdbang) abort endif endif - let b:fugitive_files = section_files - let b:fugitive_diff = diff - let expanded = get(b:, 'fugitive_expanded', {'Staged': {}, 'Unstaged': {}}) - let b:fugitive_expanded = {'Staged': {}, 'Unstaged': {}} - - setlocal noreadonly modifiable - silent keepjumps %delete_ - - call s:AddHeader('Head', head) - call s:AddHeader(pull_type, pull_short) + let stat.expanded = {'Staged': {}, 'Unstaged': {}} + let to = {'lines': []} + call s:AddHeader(to, 'Head', head) + call s:AddHeader(to, pull_type, pull_short) if push_ref !=# pull_ref - call s:AddHeader('Push', push_short) + call s:AddHeader(to, 'Push', push_short) endif - if empty(tree) + if empty(stat.work_tree) if get(fugitive#ConfigGetAll('core.bare', config), 0, '') !~# '^\%(false\|no|off\|0\|\)$' - call s:AddHeader('Bare', 'yes') + call s:AddHeader(to, 'Bare', 'yes') else - call s:AddHeader('Error', s:worktree_error) + call s:AddHeader(to, 'Error', s:worktree_error) endif endif - if get(fugitive#ConfigGetAll('advice.statusHints', config), 0, 'true') !~# '^\%(false\|no|off\|0\|\)$' - call s:AddHeader('Help', 'g?') + if get(fugitive#ConfigGetAll('advice.tousHints', config), 0, 'true') !~# '^\%(false\|no|off\|0\|\)$' + call s:AddHeader(to, 'Help', 'g?') endif - call s:AddSection('Rebasing ' . rebasing_head, rebasing) - call s:AddSection(get(get(sequencing, 0, {}), 'status', '') ==# 'revert' ? 'Reverting' : 'Cherry Picking', sequencing) - call s:AddSection('Untracked', untracked) - call s:AddSection('Unstaged', unstaged) - let unstaged_end = len(unstaged) ? line('$') : 0 - call s:AddSection('Staged', staged) - let staged_end = len(staged) ? line('$') : 0 + call s:AddSection(to, 'Rebasing ' . rebasing_head, rebasing) + call s:AddSection(to, get(get(sequencing, 0, {}), 'tous', '') ==# 'revert' ? 'Reverting' : 'Cherry Picking', sequencing) + call s:AddSection(to, 'Untracked', untracked) + call s:AddDiffSection(to, stat, 'Unstaged', unstaged) + call s:AddDiffSection(to, stat, 'Staged', staged) let unique_push_ref = push_ref ==# pull_ref ? '' : push_ref let unpushed_push = s:QueryLogRange(unique_push_ref, head, dir) @@ -3001,27 +3006,27 @@ function! fugitive#BufReadStatus(cmdbang) abort if unpushed_push.error == 1 let unpushed_push = unpushed_pull endif - call s:AddLogSection('Unpushed to ' . push_short, unpushed_push) - call s:AddLogSection('Unpushed to ' . pull_short, unpushed_pull) + call s:AddLogSection(to, 'Unpushed to ' . push_short, unpushed_push) + call s:AddLogSection(to, 'Unpushed to ' . pull_short, unpushed_pull) if unpushed_push.error && unpushed_pull.error && empty(rebasing) && \ !empty(push_remote . fetch_remote) - call s:AddLogSection('Unpushed to *', s:QueryLog([head, '--not', '--remotes'], 256, dir)) + call s:AddLogSection(to, 'Unpushed to *', s:QueryLog([head, '--not', '--remotes'], 256, dir)) endif - call s:AddLogSection('Unpulled from ' . push_short, s:QueryLogRange(head, unique_push_ref, dir)) + call s:AddLogSection(to, 'Unpulled from ' . push_short, s:QueryLogRange(head, unique_push_ref, dir)) if len(pull_ref) && get(props, 'branch.ab') !~# ' -0$' - call s:AddLogSection('Unpulled from ' . pull_short, s:QueryLogRange(head, pull_ref, dir)) + call s:AddLogSection(to, 'Unpulled from ' . pull_short, s:QueryLogRange(head, pull_ref, dir)) endif + let b:fugitive_files = stat.files + let b:fugitive_diff = stat.diff + let b:fugitive_expanded = stat.expanded + let b:fugitive_reltime = stat.reltime + setlocal noreadonly modifiable + if len(to.lines) < line('$') + silent keepjumps execute (len(to.lines)+1) . ',$delete_' + endif + call setline(1, to.lines) setlocal nomodified readonly nomodifiable - for [lnum, section] in [[staged_end, 'Staged'], [unstaged_end, 'Unstaged']] - while len(getline(lnum)) - let filename = matchstr(getline(lnum), '^[A-Z?] \zs.*') - if has_key(expanded[section], filename) - call s:StageInline('show', lnum) - endif - let lnum -= 1 - endwhile - endfor doautocmd BufReadPost if &bufhidden ==# '' @@ -3032,7 +3037,6 @@ function! fugitive#BufReadStatus(cmdbang) abort endif setlocal filetype=fugitive - let b:fugitive_reltime = reltime() return s:DoAutocmd('User FugitiveIndex') finally let b:fugitive_type = 'index' @@ -4833,6 +4837,29 @@ function! s:PatchSearchExpr(reverse) abort return a:reverse ? '#' : '*' endfunction +function! s:StageInlineGetDiff(diff_section, info) abort + let diff = [] + if a:info.status ==# 'U' + let diff_header = 'diff --cc ' . s:Quote(a:info.relative[0]) + else + let diff_header = 'diff --git ' . s:Quote(a:info.relative[-1]) . ' ' . s:Quote(a:info.relative[0]) + endif + let stdout = fugitive#Wait(a:diff_section).stdout + let start = index(stdout, diff_header) + if start == -1 + return [[], -1] + endif + let index = start + 1 + while get(stdout, index, '@@') !~# '^@@\|^diff ' + let index += 1 + endwhile + while get(stdout, index, '') =~# '^[@ \+-]' + call add(diff, stdout[index]) + let index += 1 + endwhile + return [diff, start] +endfunction + function! s:StageInline(mode, ...) abort if &filetype !=# 'fugitive' return '' @@ -4877,26 +4904,7 @@ function! s:StageInline(mode, ...) abort if info.status !~# '^[ADMRU]$' || a:mode ==# 'hide' continue endif - let mode = '' - let diff = [] - if info.status ==# 'U' - let diff_header = 'diff --cc ' . s:Quote(info.relative[0]) - else - let diff_header = 'diff --git ' . s:Quote(info.relative[-1]) . ' ' . s:Quote(info.relative[0]) - endif - let stdout = fugitive#Wait(diff_section).stdout - let start = index(stdout, diff_header) - if start == -1 - continue - endif - let index = start + 1 - while get(stdout, index, '@@') !~# '^@@\|^diff ' - let index += 1 - endwhile - while get(stdout, index, '') =~# '^[@ \+-]' - call add(diff, stdout[index]) - let index += 1 - endwhile + let [diff, start] = s:StageInlineGetDiff(diff_section, info) if len(diff) setlocal modifiable noreadonly silent call append(lnum, diff)