2 Commits

Author SHA1 Message Date
fab4100
a41ef0f9d6 Merge 28f17e5469 into d74a7cff4c 2025-01-21 16:51:34 +01:00
Fabian Wermelinger
28f17e5469 Add support for multi-file edits
Git send-email by default uses multi-file edit mode
(sendemail.multiEdit) when more than one patch file requires editing an
associated email message and/or cover-letters.  Multi edit mode spawns
one editor instance with multiple files in argument list.  This behavior
is supported by creating multiple splits for a running vim instance when
`:G send-email` is called.  When the user sets sendemail.multiEdit to
false in the local or global config, if multiple files need editing then
they are processed sequentially in a single split at a time.

Closes #2352
2024-12-02 21:41:01 +01:00

View File

@@ -4,6 +4,11 @@
" The functions contained within this file are for internal use only. For the " The functions contained within this file are for internal use only. For the
" official API, see the commented functions in plugin/fugitive.vim. " official API, see the commented functions in plugin/fugitive.vim.
if exists('g:autoloaded_fugitive')
finish
endif
let g:autoloaded_fugitive = 1
" Section: Utility " Section: Utility
function! s:function(name) abort function! s:function(name) abort
@@ -2066,8 +2071,6 @@ function! s:Expand(rev, ...) abort
endif endif
elseif s:Slash(a:rev) =~# '^\a\a\+://' elseif s:Slash(a:rev) =~# '^\a\a\+://'
let file = substitute(a:rev, '\\\@<!\%(#\a\|%\x\x\)', '\\&', 'g') let file = substitute(a:rev, '\\\@<!\%(#\a\|%\x\x\)', '\\&', 'g')
elseif a:rev =~# '^:[!#%$]'
let file = ':0' . a:rev
else else
let file = a:rev let file = a:rev
endif endif
@@ -2694,7 +2697,7 @@ function! s:MapStatus() abort
call s:MapMotion('gP', "exe <SID>StageJump(v:count, 'Unpulled')") call s:MapMotion('gP', "exe <SID>StageJump(v:count, 'Unpulled')")
call s:MapMotion('gr', "exe <SID>StageJump(v:count, 'Rebasing')") call s:MapMotion('gr', "exe <SID>StageJump(v:count, 'Rebasing')")
call s:Map('n', 'C', ":echoerr 'fugitive: C has been removed in favor of cc'<CR>", '<silent><unique>') call s:Map('n', 'C', ":echoerr 'fugitive: C has been removed in favor of cc'<CR>", '<silent><unique>')
call s:Map('n', 'a', ":echoerr 'fugitive: a has been removed in favor of s'<CR>", '<silent><unique>') call s:Map('n', 'a', ":<C-U>execute <SID>Do('Toggle',0)<CR>", '<silent>')
call s:Map('n', 'i', ":<C-U>execute <SID>NextExpandedHunk(v:count1)<CR>", '<silent>') call s:Map('n', 'i', ":<C-U>execute <SID>NextExpandedHunk(v:count1)<CR>", '<silent>')
call s:Map('n', "=", ":<C-U>execute <SID>StageInline('toggle',line('.'),v:count)<CR>", '<silent>') call s:Map('n', "=", ":<C-U>execute <SID>StageInline('toggle',line('.'),v:count)<CR>", '<silent>')
call s:Map('n', "<", ":<C-U>execute <SID>StageInline('hide', line('.'),v:count)<CR>", '<silent>') call s:Map('n', "<", ":<C-U>execute <SID>StageInline('hide', line('.'),v:count)<CR>", '<silent>')
@@ -2937,34 +2940,20 @@ function! s:StatusRender(stat) abort
endif endif
let sequencing = [] let sequencing = []
try if filereadable(fugitive#Find('.git/sequencer/todo', dir))
let sequencer_todo = reverse(readfile(fugitive#Find('.git/sequencer/todo', dir))) for line in reverse(readfile(fugitive#Find('.git/sequencer/todo', dir)))
catch
endtry
if exists('sequencer_todo')
for line in sequencer_todo
let match = matchlist(line, '^\(\l\+\)\s\+\(\x\{4,\}\)\s\+\(.*\)') let match = matchlist(line, '^\(\l\+\)\s\+\(\x\{4,\}\)\s\+\(.*\)')
if len(match) && match[1] !~# 'exec\|merge\|label' if len(match) && match[1] !~# 'exec\|merge\|label'
call add(sequencing, {'type': 'Rebase', 'status': get(s:rebase_abbrevs, match[1], match[1]), 'commit': match[2], 'subject': match[3]}) call add(sequencing, {'type': 'Rebase', 'status': get(s:rebase_abbrevs, match[1], match[1]), 'commit': match[2], 'subject': match[3]})
endif endif
endfor endfor
else elseif filereadable(fugitive#Find('.git/MERGE_MSG', dir))
try
let merge_msg = get(readfile(fugitive#Find('.git/MERGE_MSG', dir)), 0, '')
catch
endtry
endif
if exists('merge_msg')
if filereadable(fugitive#Find('.git/CHERRY_PICK_HEAD', dir)) if filereadable(fugitive#Find('.git/CHERRY_PICK_HEAD', dir))
let pick_head = fugitive#Execute(['rev-parse', '--short', 'CHERRY_PICK_HEAD', '--'], dir).stdout[0] let pick_head = fugitive#Execute(['rev-parse', '--short', 'CHERRY_PICK_HEAD', '--'], dir).stdout[0]
if !empty(pick_head) call add(sequencing, {'type': 'Rebase', 'status': 'pick', 'commit': pick_head, 'subject': get(readfile(fugitive#Find('.git/MERGE_MSG', dir)), 0, '')})
call add(sequencing, {'type': 'Rebase', 'status': 'pick', 'commit': pick_head, 'subject': merge_msg})
endif
elseif filereadable(fugitive#Find('.git/REVERT_HEAD', dir)) elseif filereadable(fugitive#Find('.git/REVERT_HEAD', dir))
let pick_head = fugitive#Execute(['rev-parse', '--short', 'REVERT_HEAD', '--'], dir).stdout[0] let pick_head = fugitive#Execute(['rev-parse', '--short', 'REVERT_HEAD', '--'], dir).stdout[0]
if !empty(pick_head) call add(sequencing, {'type': 'Rebase', 'status': 'revert', 'commit': pick_head, 'subject': get(readfile(fugitive#Find('.git/MERGE_MSG', dir)), 0, '')})
call add(sequencing, {'type': 'Rebase', 'status': 'revert', 'commit': pick_head, 'subject': merge_msg})
endif
endif endif
endif endif
@@ -2987,7 +2976,7 @@ function! s:StatusRender(stat) abort
endif endif
call s:AddSection(to, 'Rebasing ' . rebasing_head, rebasing) call s:AddSection(to, 'Rebasing ' . rebasing_head, rebasing)
call s:AddSection(to, get(get(sequencing, 0, {}), 'status', '') ==# 'revert' ? 'Reverting' : 'Cherry Picking', sequencing) call s:AddSection(to, get(get(sequencing, 0, {}), 'tous', '') ==# 'revert' ? 'Reverting' : 'Cherry Picking', sequencing)
call s:AddSection(to, 'Untracked', untracked) call s:AddSection(to, 'Untracked', untracked)
call s:AddDiffSection(to, stat, 'Unstaged', unstaged) call s:AddDiffSection(to, stat, 'Unstaged', unstaged)
call s:AddDiffSection(to, stat, 'Staged', staged) call s:AddDiffSection(to, stat, 'Staged', staged)
@@ -3454,33 +3443,37 @@ function! s:RunEdit(state, tmp, job) abort
endif endif
call remove(a:state, 'request') call remove(a:state, 'request')
let sentinel = a:state.file . '.edit' let sentinel = a:state.file . '.edit'
let file = FugitiveVimPath(readfile(sentinel, '', 1)[0]) let files = readfile(sentinel, '')
try call writefile([len(files)], sentinel)
if !&equalalways && a:state.mods !~# '\<\d*tab\>' && 3 > (a:state.mods =~# '\<vert' ? winwidth(0) : winheight(0)) for file in reverse(files)
let noequalalways = 1 let file = FugitiveVimPath(file)
setglobal equalalways
endif
let mods = s:Mods(a:state.mods, 'SpanOrigin')
exe substitute(mods, '\<tab\>', '-tab', 'g') 'keepalt split' s:fnameescape(file)
finally
if exists('l:noequalalways')
setglobal noequalalways
endif
endtry
set bufhidden=wipe
call s:InitializeBuffer(a:state)
let bufnr = bufnr('')
let s:edit_jobs[bufnr] = [a:state, a:tmp, a:job, sentinel]
call fugitive#DidChange(a:state.git_dir)
if bufnr == bufnr('') && !exists('g:fugitive_event')
try try
let g:fugitive_event = a:state.git_dir if !&equalalways && a:state.mods !~# '\<\d*tab\>' && 3 > (a:state.mods =~# '\<vert' ? winwidth(0) : winheight(0))
let g:fugitive_result = a:state let noequalalways = 1
exe s:DoAutocmd('User FugitiveEditor') setglobal equalalways
endif
let mods = s:Mods(a:state.mods, 'SpanOrigin')
exe substitute(mods, '\<tab\>', '-tab', 'g') 'keepalt split' s:fnameescape(file)
finally finally
unlet! g:fugitive_event g:fugitive_result if exists('l:noequalalways')
setglobal noequalalways
endif
endtry endtry
endif set bufhidden=wipe
call s:InitializeBuffer(a:state)
let bufnr = bufnr('')
let s:edit_jobs[bufnr] = [a:state, a:tmp, a:job, sentinel]
call fugitive#DidChange(a:state.git_dir)
if bufnr == bufnr('') && !exists('g:fugitive_event')
try
let g:fugitive_event = a:state.git_dir
let g:fugitive_result = a:state
exe s:DoAutocmd('User FugitiveEditor')
finally
unlet! g:fugitive_event g:fugitive_result
endtry
endif
endfor
return 1 return 1
endfunction endfunction
@@ -3634,6 +3627,9 @@ if !exists('s:edit_jobs')
endif endif
function! s:RunWait(state, tmp, job, ...) abort function! s:RunWait(state, tmp, job, ...) abort
if a:0 && filereadable(a:1) if a:0 && filereadable(a:1)
if a:0 > 1 && a:2 > 0
return ''
endif
call delete(a:1) call delete(a:1)
endif endif
try try
@@ -3736,8 +3732,16 @@ function! s:RunBufDelete(bufnr) abort
endif endif
if has_key(s:edit_jobs, a:bufnr) | if has_key(s:edit_jobs, a:bufnr) |
call add(s:resume_queue, remove(s:edit_jobs, a:bufnr)) call add(s:resume_queue, remove(s:edit_jobs, a:bufnr))
call feedkeys("\<C-\>\<C-N>:redraw!|call delete(" . string(s:resume_queue[-1][0].file . '.edit') . let sentinel = s:resume_queue[-1][0].file . '.edit'
\ ")|call fugitive#Resume()|checktime\r", 'n') let active_buffers = str2nr(readfile(sentinel, '', 1)[0]) - 1
call add(s:resume_queue[-1], active_buffers)
if active_buffers < 1
call feedkeys("\<C-\>\<C-N>:redraw!|call delete(" . string(sentinel) .
\ ")|call fugitive#Resume()|checktime\r", 'n')
else
call writefile([active_buffers], sentinel)
call fugitive#Resume()
endif
endif endif
endfunction endfunction
@@ -3951,7 +3955,7 @@ function! fugitive#Command(line1, line2, range, bang, mods, arg, ...) abort
let env.FUGITIVE = state.file let env.FUGITIVE = state.file
let editor = 'sh ' . s:TempScript( let editor = 'sh ' . s:TempScript(
\ '[ -f "$FUGITIVE.exit" ] && cat "$FUGITIVE.exit" >&2 && exit 1', \ '[ -f "$FUGITIVE.exit" ] && cat "$FUGITIVE.exit" >&2 && exit 1',
\ 'echo "$1" > "$FUGITIVE.edit"', \ 'for arg; do echo "$arg" >> "$FUGITIVE.edit"; done',
\ 'printf "\033]51;fugitive:edit\007" >&2', \ 'printf "\033]51;fugitive:edit\007" >&2',
\ 'while [ -f "$FUGITIVE.edit" -a ! -f "$FUGITIVE.exit" ]; do sleep 0.05 2>/dev/null || sleep 1; done', \ 'while [ -f "$FUGITIVE.edit" -a ! -f "$FUGITIVE.exit" ]; do sleep 0.05 2>/dev/null || sleep 1; done',
\ 'exit 0') \ 'exit 0')
@@ -7446,12 +7450,9 @@ function! s:BrowserOpen(url, mods, echo_copy) abort
else else
if !exists('g:loaded_netrw') if !exists('g:loaded_netrw')
runtime! autoload/netrw.vim runtime! autoload/netrw.vim
runtime! autoload/netrw/os.vim
endif endif
if exists('*netrw#Open') if exists('*netrw#Open')
return 'echo '.string(url).'|' . mods . 'call netrw#Open('.string(url).')' return 'echo '.string(url).'|' . mods . 'call netrw#Open('.string(url).')'
elseif exists('*netrw#os#Open')
return 'echo '.string(url).'|' . mods . 'call netrw#os#Open('.string(url).')'
elseif exists('*netrw#BrowseX') elseif exists('*netrw#BrowseX')
return 'echo '.string(url).'|' . mods . 'call netrw#BrowseX('.string(url).', 0)' return 'echo '.string(url).'|' . mods . 'call netrw#BrowseX('.string(url).', 0)'
elseif exists('*netrw#NetrwBrowseX') elseif exists('*netrw#NetrwBrowseX')
@@ -8044,8 +8045,8 @@ function! fugitive#MapJumps(...) abort
call s:MapMotion(']]', 'exe <SID>NextSection(v:count1)') call s:MapMotion(']]', 'exe <SID>NextSection(v:count1)')
call s:MapMotion('[]', 'exe <SID>PreviousSectionEnd(v:count1)') call s:MapMotion('[]', 'exe <SID>PreviousSectionEnd(v:count1)')
call s:MapMotion('][', 'exe <SID>NextSectionEnd(v:count1)') call s:MapMotion('][', 'exe <SID>NextSectionEnd(v:count1)')
call s:Map('no', '*', '<SID>PatchSearchExpr(0)', '<expr>') call s:Map('nxo', '*', '<SID>PatchSearchExpr(0)', '<expr>')
call s:Map('no', '#', '<SID>PatchSearchExpr(1)', '<expr>') call s:Map('nxo', '#', '<SID>PatchSearchExpr(1)', '<expr>')
endif endif
call s:Map('n', 'S', ':<C-U>echoerr "Use gO"<CR>', '<silent><unique>') call s:Map('n', 'S', ':<C-U>echoerr "Use gO"<CR>', '<silent><unique>')
call s:Map('n', 'dq', ":<C-U>call fugitive#DiffClose()<CR>", '<silent>') call s:Map('n', 'dq', ":<C-U>call fugitive#DiffClose()<CR>", '<silent>')