let s:SCHEME = gina#command#scheme(expand('')) function! gina#command#diff#call(range, args, mods) abort call gina#core#options#help_if_necessary(a:args, s:get_options()) let git = gina#core#get_or_fail() let args = s:build_args(git, a:args) let bufname = gina#core#buffer#bufname(git, s:SCHEME, { \ 'treeish': args.params.treeish, \ 'params': [ \ args.params.cached ? 'cached' : '', \ args.params.R ? 'R' : '', \ args.params.partial ? '--' : '', \ ], \ 'noautocmd': !empty(args.params.path), \}) call gina#core#buffer#open(bufname, { \ 'mods': a:mods, \ 'group': args.params.group, \ 'opener': args.params.opener, \ 'cmdarg': args.params.cmdarg, \ 'callback': { \ 'fn': function('s:init'), \ 'args': [args], \ } \}) endfunction function! gina#command#diff#complete(arglead, cmdline, cursorpos) abort let args = gina#core#args#new(matchstr(a:cmdline, '^.*\ze .*')) if a:arglead =~# '^--opener=' return gina#complete#common#opener(a:arglead, a:cmdline, a:cursorpos) elseif a:arglead =~# '^\%(--diff-algorithm=\)' let leading = matchstr(a:arglead, '^--diff-algorithm=') return gina#util#filter(a:arglead, map( \ ['patience', 'minimal', 'histogram', 'myers'], \ 'leading . v:val' \)) elseif a:arglead =~# '^\%(--dirstat=\)' let leading = matchstr(a:arglead, '^--dirstat=') let dirstat = matchstr(a:arglead, '^--dirstat=\zs\%([^,]\+,\)*[^,]*') let candidates = filter( \ ['changes', 'lines', 'files', 'cumulative'], \ 'dirstat !~# ''\<'' . v:val . ''\>''', \) return gina#util#filter(a:arglead, map( \ candidates, 'leading . dirstat . v:val' \)) elseif a:arglead =~# '^\%(--submodule=\)' let leading = matchstr(a:arglead, '^--submodule=') return gina#util#filter(a:arglead, map( \ ['short', 'log', 'diff'], \ 'leading . v:val' \)) elseif a:arglead =~# '^\%(--diff-filter=\)' let leading = matchstr(a:arglead, '^--diff-filter=[ACDMRTUXB]*') return gina#util#filter(a:arglead, map( \ split('ACDMRTUXB', '\zs'), \ 'leading . v:val' \)) elseif a:arglead =~# '^\%(--ignore-submodules=\)' let leading = matchstr(a:arglead, '^--ignore-submodules=') return gina#util#filter(a:arglead, map( \ ['none', 'untracked', 'dirty', 'all'], \ 'leading . v:val' \)) elseif a:cmdline =~# '\s--\s' return gina#complete#filename#any(a:arglead, a:cmdline, a:cursorpos) elseif a:arglead[0] ==# '-' let options = s:get_options() return options.complete(a:arglead, a:cmdline, a:cursorpos) endif return gina#complete#common#treeish(a:arglead, a:cmdline, a:cursorpos) endfunction " Private -------------------------------------------------------------------- function! s:get_options() abort let options = gina#core#options#new() call options.define( \ '-h|--help', \ 'Show this help.', \) call options.define( \ '--opener=', \ 'A Vim command to open a new buffer.', \ ['edit', 'split', 'vsplit', 'tabedit', 'pedit'], \) call options.define( \ '--cached', \ 'Compare to the index rather than the working tree', \) call options.define( \ '-U|--unified=', \ 'Generate diffs with lines of context', \) call options.define( \ '--raw', \ 'Generate the diff in raw format', \) call options.define( \ '--patch-with-raw', \ 'Synonym for -p --raw', \) call options.define( \ '--minimal', \ 'Spend extra time to make sure the smallest possible diff is produced', \) call options.define( \ '--patience', \ 'Generate a diff using the "patience diff" algorithm', \) call options.define( \ '--histogram', \ 'Generate a diff using the "histogram diff" algorithm', \) call options.define( \ '--diff-algorithm', \ 'Choose a diff algorithm', \) call options.define( \ '--stat', \ 'Generate a diffstat', \) call options.define( \ '--numstat', \ 'Similar to --stat, but with more machine friendly output', \) call options.define( \ '--shortstat', \ 'Output only the last line of the --stat format', \) call options.define( \ '--dirstat', \ 'Output the dirstat numbers', \) call options.define( \ '--summary', \ 'Output a condensed summary of extended header information', \) call options.define( \ '--patch-with-stat', \ 'Synonym for -p --stat', \) call options.define( \ '--name-only', \ 'Show only names of changed files', \) call options.define( \ '--name-status', \ 'Show only names and status of changed files', \) call options.define( \ '--submodule', \ 'Specify how differences in submodules are shown', \) call options.define( \ '--submodule', \ 'Specify how differences in submodules are shown', \) call options.define( \ '--no-renames', \ 'Turn off rename detection', \) call options.define( \ '--check', \ 'Warn if changes introduce conflict markers or whitespace errors', \) call options.define( \ '--full-index', \ 'Instead of the first handful of characters,show the full blob', \) call options.define( \ '--binary', \ 'In addition to --full-index, output a binary diff', \) call options.define( \ '--abbrev', \ 'Show only a particular prefix hexadecimal object name', \) call options.define( \ '-B|--break-rewrites', \ 'Break complete rewrite changes into pair of delete and create', \) call options.define( \ '-M|--find-renames', \ 'Detect renames. -M to specify the threshold.', \) call options.define( \ '-C|--find-copies', \ 'Detect copies as well as renames. -C to specify the threshold', \) call options.define( \ '--find-copies-harder', \ 'Detect copies more harder than -C/--find-copies', \) call options.define( \ '-D|--irreversible-delete', \ 'Omit the preimage for deletes', \) call options.define( \ '-l', \ 'Specify the threshold of -M or -C', \) call options.define( \ '--diff-filter=', join([ \ 'Specify the Select only files that are Added(A), Copied(C), ', \ 'Deleted (D), Modified (M), Renamed (R), ', \ 'have their type changed (T), are Unmerged (U), ', \ 'are Unknown (X), or have had their pairing Broken (B)', \ ]) \) call options.define( \ '-S', join([ \ 'Look for differences that change the number of occurrences of ', \ 'the specified string in a file.' \ ]) \) call options.define( \ '-G', join([ \ 'Look for differences whose patch text contains added/removed ', \ 'line that match ' \ ]) \) call options.define( \ '--pickaxe-all', join([ \ 'Wnen -S or -G finds a change, show all the changes in that ', \ 'changeset, not just the files and contain the change' \ ]) \) call options.define( \ '--pickaxe-regex', join([ \ 'Treat the given to -S as an extended POSIX regular ', \ 'expression to match' \ ]) \) call options.define( \ '-O', \ 'Output the patch in the order specified in the ', \) call options.define( \ '-R', \ 'Swap two inputs', \) call options.define( \ '--relative', \ 'Show pathnames relative to', \) call options.define( \ '-a|--text', \ 'Treat all files as text', \) call options.define( \ '--ignore-space-at-eol', \ 'Ignore changes in whitespace at EOL', \) call options.define( \ '-b|--ignore-space-change', \ 'Ignore changes in amount of whitespace', \) call options.define( \ '-w|--ignore-all-space', \ 'Ignore whitespace when comparing lines', \) call options.define( \ '--ignore-blank-lines', \ 'Ignore changes whose line are all blank', \) call options.define( \ '--inter-hunk-context=', \ 'Show the context between diff hunks', \) call options.define( \ '-W|--function-context', \ 'Show whole surrounding functions of changes', \) call options.define( \ '--ext-diff', \ 'Allow an external diff helper to be executed', \) call options.define( \ '--no-ext-diff', \ 'Disallow external diff drivers', \) call options.define( \ '--textconv', \ 'Allow an external text conversion filters', \) call options.define( \ '--no-textconv', \ 'Disallow an external text conversion filters', \) call options.define( \ '--ignore-submodules', \ 'Ignore changes to submodules in the diff generation', \) call options.define( \ '--src-prefix=', \ 'Show the given source prefix instead of "a/"', \) call options.define( \ '--dst-prefix=', \ 'Show the given destination prefix instead of "a/"', \) call options.define( \ '--no-prefix', \ 'Do not show any source or destination prefix', \) call options.define( \ '--line-prefix=', \ 'Prepend an additional prefix to every line of output', \) return options endfunction function! s:build_args(git, args) abort let args = a:args.clone() let args.params.group = args.pop('--group', '') let args.params.opener = args.pop('--opener', '') let args.params.cached = args.get('--cached') let args.params.R = args.get('-R') let args.params.partial = !empty(args.residual()) " Remove unsupported options call args.pop('-z') call args.pop('--color') call args.pop('--word-diff') call args.pop('--word-diff-regex') call args.pop('--color-words') call args.pop('--ws-error-highlight') " Force --no-color call args.set('--no-color', 1) call gina#core#args#extend_treeish(a:git, args, args.pop(1)) call gina#core#args#extend_diff(a:git, args, args.params.rev) call args.set(1, args.params.rev) if args.params.path isnot# v:null call args.residual([args.params.path] + args.residual()) endif return args.lock() endfunction function! s:init(args) abort call gina#core#meta#set('args', a:args) if exists('b:gina_initialized') return endif let b:gina_initialized = 1 setlocal nomodeline setlocal buftype=nowrite setlocal noswapfile setlocal nomodifiable if a:args.params.partial setlocal bufhidden=wipe else setlocal bufhidden& endif augroup gina_command_diff_internal autocmd! * autocmd BufReadCmd \ call gina#core#revelator#call(function('s:BufReadCmd'), []) autocmd BufWinEnter call setbufvar(str2nr(expand('')), '&buflisted', 1) autocmd BufWinLeave call setbufvar(str2nr(expand('')), '&buflisted', 0) augroup END nnoremap (gina-diff-jump) \ :call gina#core#diffjump#jump() nnoremap (gina-diff-jump-split) \ :call gina#core#diffjump#jump('split') nnoremap (gina-diff-jump-vsplit) \ :call gina#core#diffjump#jump('vsplit') if g:gina#command#diff#use_default_mappings nmap (gina-diff-jump) endif endfunction function! s:BufReadCmd() abort let git = gina#core#get_or_fail() let args = gina#core#meta#get_or_fail('args') let pipe = gina#process#pipe#stream(s:writer) call gina#core#buffer#assign_cmdarg() call gina#process#open(git, args, pipe) setlocal filetype=diff endfunction " Writer --------------------------------------------------------------------- function! s:_writer_on_exit() abort dict call call(s:original_writer.on_exit, [], self) call gina#core#emitter#emit('command:called', s:SCHEME) endfunction let s:original_writer = gina#process#pipe#stream_writer() let s:writer = extend(deepcopy(s:original_writer), { \ 'on_exit': function('s:_writer_on_exit'), \}) " Config --------------------------------------------------------------------- call gina#config(expand(''), { \ 'use_default_mappings': 1, \})