" Vimball Archiver by Charles E. Campbell UseVimball finish plugin/svnTools.vim [[[1 239 " Script Name: svnTools.vim "Description: " " Copyright: (C) 2017-2021 Javier Puigdevall " The VIM LICENSE applies to this script; see ':help copyright'. " " Maintainer: Javier Puigdevall " Contributors: " " Dependencies: jobs.vim, jpLib.vim(optional) " " NOTES: " " Version: 1.0.6 " Changes: " 1.0.6 Fry, 28 May 21. JPuigdevall " - New: Svndvdr command, when placed on an svn log and diff file (obtained from Svnr, Svnda, Svndd or Svndf), for each file " open the vimdiff of the current log's file revision. " - New configuration option g:svnTools_userAndPsswd to use non interactive snv and send user and password in the " same command: let g:svnTools_userAndPsswd = 1 " - New options to manage svn user and password: g:svnTools_svnUser, g:svnTool_storeSvnPsswd. " 1.0.5 Fry, 26 Feb 21. JPuigdevall " - New: Svnlr command, when placed on an svn log file, for each revision " number get its log and diff changes. " - Fix Svnm command, treat status ' C ' as file in conflict. " - Fix Svnm command, treat status !.*C as file in conflict. " - Fix Svnm command, do not try opening merge tool when file in confict is not found. " - Fix GetStatusFilesList function, file path is last column not second one. " - New Svnvmb command, development command to create a vimball release of the plugin. " 1.0.4 Wed, 16 Dec 20. JPuigdevall " - Fix bug on Svnvda, Svnvdd, Svnvdf, SvnvdA. " - New on Svnda, Svndd, Svndf: do not allow to modify or save the revision file on vimdiff. " - New Svndc command, show diff between files with changes on two different directories. " - New Svndc command, show diff between files with changes on two different directories, skip binary files. " - New Svnmh command: show merge layout help. " 1.0.3 Thu, 10 Dec 20. JPuigdevall " - New SvnvdA command. For every modifed file, allows user to get vimdiff, skip (this/all/none) file, " skip (this/all/none) binaries. " - New on Svnvdf/Svnvdd/Svnvda check if file is binary, ask user to skip binary files. " - New on Svnvdc will now show only files with differecnes, omitt any files that do not differ. " - New Svnvdca command to show all changes between directories. " - Fix GetStatusFilesString issue affecting svnTools#VimDiffCompareDirChanges. " 1.0.2 Fry, 10 Jul 20. JPuigdevall " - Add Svnm command to merge commit upon conflict. " - Add Svnrv command to resolve commit conflicts. " - Add Svnst commands to show svn status. " - Adapt to jobs.vim 0.1.0 " - Add Svnvdr command to show vimdiff on files modified on selected revision. " 1.0.1 Fry, 22 Jun 20. JPuigdevall " 1.0.0 Wed, 13 Feb 19. JPuigdevall " 0.0.1 Sun, 10 Feb 18. JPuigdevall " - Initial realease. if exists('g:loaded_svntools') finish endif let g:loaded_svntools = 1 let s:save_cpo = &cpo set cpo&vim let g:svnTools_version = "1.0.6" "- configuration -------------------------------------------------------------- let g:svnTools_svnCmd = get(g:, 'svnTools_svnCmd', "svn") let g:svnTools_userAndPsswd = get(g:, 'svnTools_userAndPsswd', 0) let g:svnTools_svnUser = get(g:, 'svnTools_svnUser', "") let g:svnTools_storeSvnPsswd = get(g:, 'svnTools_storeSvnPsswd', 1) let g:svnTools_runInBackground = get(g:, 'svnTools_runInBackground', 1) let g:svnTools_gotoWindowOnEnd = get(g:, 'svnTools_gotoWindowOnEnd', 1) let g:svnTools_lastCommits = get(g:, 'svnTools_lastCommits', 3000) let g:svnTools_mode = get(g:, 'svnTools_mode', 3) " Merge Layout: " Layout 2: " -------------------------- " | | | " | Current | Right | " | | | " -------------------------- " " Layout 3: " -------------------------- " | | | | " | Left | Current | Right | " | | | | " -------------------------- " " Layout 4: " -------------------------- " | | | | " | Left | Working | Right | " | | | | " -------------------------- " | Current | " -------------------------- " let g:svnTools_mergeLayout = get(g:, 'svnTools_mergeLayout', "4") let g:svnTools_mergeLayouts = get(g:, 'svnTools_mergeLayouts', "2 3 4") "- commands ------------------------------------------------------------------- " SVN INFO: command! -nargs=0 Svni call svnTools#Info(getcwd()) command! -nargs=0 Svnif call svnTools#Info(getcwd()) " SVN STATUS: command! -nargs=0 Svnst call svnTools#Status(getcwd(), "^[A-Z!] ", "^[?X] ") command! -nargs=0 Svnsta call svnTools#Status(getcwd(), "", "") command! -nargs=0 Svnstf call svnTools#Status(expand('%'), "", "") command! -nargs=0 Svnstd call svnTools#Status(expand('%:h'), "", "") command! -nargs=0 Svnsth call svnTools#StatusHelp() " SVN DIFF: command! -nargs=1 -complete=dir Svnd call svnTools#Diff() command! -nargs=0 Svndf call svnTools#Diff(expand('%')) command! -nargs=0 Svndd call svnTools#Diff(expand('%:h')) command! -nargs=0 Svnda call svnTools#Diff(getcwd()) " SVN DIFF WITH VIMDIF: command! -nargs=* -complete=dir Svnvd call svnTools#VimDiff(, "check_binaries") command! -nargs=0 Svnvdf call svnTools#VimDiff(expand('%'), "check_binaries") command! -nargs=0 Svnvdd call svnTools#VimDiff(expand('%:h'),"check_binaries") command! -nargs=0 Svnvda call svnTools#VimDiff(getcwd(), "check_binaries") command! -nargs=0 SvnvdA call svnTools#VimDiff(getcwd(), "check_all") " DIFF FILES BETWEEN DIRECTORIES: command! -nargs=* -complete=dir Svndc call svnTools#CompareDirChanges("diff", , "") command! -nargs=* -complete=dir SvndC call svnTools#CompareDirChanges("diff", , "omitt_equal_files omitt_binaries") command! -nargs=* -complete=dir Svnvdc call svnTools#CompareDirChanges("vimdiff", , "") command! -nargs=* -complete=dir SvnvdC call svnTools#CompareDirChanges("vimdiff", , "omitt_equal_files omitt_binaries") command! -nargs=0 Svndvdr call svnTools#DiffGetVimDiffChanges() " Open with vimdiff all files modified on a revsion or between two different " revisions " Svnvdr REV1 " Svnvdr REV1 REV2 " When no revision number provided as argument, try get word under cursor as the revision number. command! -nargs=* Svnvdr call svnTools#VimDiffRevision() " DIFF FILE TOOLS: " When placed on buffer with a diff file opened. " Show vimdiff of each modified file command! -nargs=* SvnDiffVdr call svnTools#DiffFileVimDiffRevision() " When placed on buffer with a diff file opened. And an line starting with 'Index' or '---' or " '+++' " Show vimdiff of current modified file command! -nargs=* SvnDiffVdrf call svnTools#DiffFileVimDiffRevisionFile() " SVN LOG: command! -nargs=? Svnl call svnTools#Log() command! -nargs=* Svnls call svnTools#LogSearchPattern() command! -nargs=* Svnlf call svnTools#LogFile(expand("%"), ) command! -nargs=* Svnld call svnTools#LogFile(getcwd(), ) command! -nargs=* Svnlp call svnTools#LogFile() command! -nargs=? Svnlr call svnTools#LogGetRevDiff("") "command! -nargs=0 Svndvdr call svnTools#LogVimDiffRevision() " SVN CAT: command! -nargs=1 Svncr call svnTools#GetRevision() " SVN REVISION: command! -nargs=? Svnr call svnTools#GetLogDiffRev() " SVN BLAME: command! -nargs=0 Svnbl call svnTools#Blame("") command! -nargs=0 Svnblv call svnTools#Blame("-v") " SVN CONFLICTS: command! -nargs=* Svnm call svnTools#Merge(getcwd(), ) command! -nargs=* Svnmf call svnTools#Merge(expand('%'), ) command! -nargs=* Svnmp call svnTools#Merge() command! -nargs=* Svnmh call svnTools#MergeLayoutHelp() command! -nargs=* Svnres call svnTools#Resolve(getcwd(), ) command! -nargs=* Svnresp call svnTools#Resolve() " Other: command! -nargs=0 Svnh call svnTools#Help() " Toogle background/foreground execution of the svn commands. command! -nargs=? Svnbg call svnTools#BackgroundMode("") command! -nargs=? Svnv call svnTools#Verbose("") " Release functions: command! -nargs=0 Svnvba call svnTools#NewVimballRelease() "- abbreviations ------------------------------------------------------------------- " DEBUG functions: reload plugin cnoreabbrev _svnrl =svnTools#Reload() "- menus ------------------------------------------------------------------- if has("gui_running") call svnTools#CreateMenus('cn' , '.&Info' , ':Svni' , 'Working dir info' , ':Svni') call svnTools#CreateMenus('cn' , '.&Info' , ':Svnif' , 'File info' , ':Svnif') call svnTools#CreateMenus('cn' , '.&Blame' , ':Svnbl' , 'Get file blame' , ':Svnbl') call svnTools#CreateMenus('cn' , '.&Blame' , ':Svnblv' , 'Get file blame verbose' , ':Svnblv') call svnTools#CreateMenus('cn' , '.&Log' , ':Svnl' , 'Show log (num of commits)' , ':Svnl [NUM]') call svnTools#CreateMenus('cn' , '.&Log' , ':Svnls' , 'Log search (num of commits)' , ':Svnls PATTERN [NUM]') call svnTools#CreateMenus('cn' , '.&Log' , ':Svnlr' , 'On svn log file, get each revision diff' , ':Svnlr') call svnTools#CreateMenus('cn' , '.&Log' , ':Svnlvdr' , 'On svn log and diff file, get each file vimdiff' , ':Svnlvdr') call svnTools#CreateMenus('cn' , '.&Diff' , ':Svndf' , 'Get file diff' , ':Svndf') call svnTools#CreateMenus('cn' , '.&Diff' , ':Svndd' , 'Get dir diff' , ':Svndd') call svnTools#CreateMenus('cn' , '.&Diff' , ':Svnda' , 'Get working dir diff' , ':Svnda') call svnTools#CreateMenus('cn' , '.&Vimdiff' , ':Svnvdf' , 'Show current file changes' , ':Svnvdf') call svnTools#CreateMenus('cn' , '.&Vimdiff' , ':Svnvdd' , 'Show current dir changes' , ':Svnvdd') call svnTools#CreateMenus('cn' , '.&Vimdiff' , ':Svnvda' , 'Show working dir with changes' , ':Svnvda') call svnTools#CreateMenus('cn' , '.&Vimdiff' , ':SvnvdA' , 'Show selected files with changes' , ':SvnvdA') call svnTools#CreateMenus('cn' , '.&Compare' , ':Svndc' , 'Show diff changes between directories' , ':Svndc') call svnTools#CreateMenus('cn' , '.&Compare' , ':SvndC' , 'Show diff changes between directories (skip bin files)' , ':SvndC') call svnTools#CreateMenus('cn' , '.&Compare' , ':Svnvdc' , 'Show vimdiff changes between directories' , ':Svnvdc') call svnTools#CreateMenus('cn' , '.&Compare' , ':SvnvdC' , 'Show vimdiff changes between directories (skip bin and not differ files)' , ':SvnvdC') call svnTools#CreateMenus('cn' , '.&Revision' , ':Svnr' , 'Get revision log and diff' , ':Svnr REV') call svnTools#CreateMenus('cn' , '.&Revision' , ':Svncr' , 'Get revision files' , ':Svncr REV') call svnTools#CreateMenus('cn' , '.&Revision' , ':Svnvdr' , 'Get vimdiff on revision' , ':Svnvdr REV') call svnTools#CreateMenus('cn' , '.&Conflicts', ':Svnm' , 'Merge all files in conflict' , ':Svnm [LAYOUT]') call svnTools#CreateMenus('cn' , '.&Conflicts', ':Svnmf' , 'Merge file in conflict' , ':Svnmf FILE [LAYOUT]') call svnTools#CreateMenus('cn' , '.&Conflicts', ':Svnmp' , 'Merge file in conflict' , ':Svnmf FILE [LAYOUT]') call svnTools#CreateMenus('cn' , '.&Conflicts', ':Svnres' , 'Resolve conflicts' , ':Svnres [all/file]') call svnTools#CreateMenus('cn' , '.&Conflicts', ':Svnmh' , 'Show merge layout help' , ':Svnmh') call svnTools#CreateMenus('cn' , '' , ':Svnbg' , 'Run foreground/background' , ':Svnbg') call svnTools#CreateMenus('cn' , '' , ':Svnh ' , 'Show command help' , ':Svnh') endif let &cpo = s:save_cpo unlet s:save_cpo autoload/svnTools.vim [[[1 2795 " Script Name: svnTools.vim "Description: " " Copyright: (C) 2017-2021 Javier Puigdevall " The VIM LICENSE applies to this script; see ':help copyright'. " " Maintainer: Javier Puigdevall " Contributors: " " Dependencies: jobs.vim, jpLib.vim(optional) " " NOTES: " "- functions ------------------------------------------------------------------- " Get the plugin reload command function! svnTools#Reload() let l:pluginPath = substitute(s:plugin_path, "autoload", "plugin", "") let l:autoloadFile = s:plugin_path."/".s:plugin_name let l:pluginFile = l:pluginPath."/".s:plugin_name return "silent! unlet g:loaded_svntools | so ".l:autoloadFile." | so ".l:pluginFile endfunction function! s:Initialize() let s:LogLevel = 0 let s:jobsRunningDict = {} endfunction function! s:CheckSvnUserAndPsswd() if g:svnTools_userAndPsswd == 0 return endif " Get svn user: if g:svnTools_svnUser == "" let g:svnTools_svnUser = input("Svn user: ") endif if g:svnTools_svnUser == "" call s:Error("Not valid svn user.") return endif " Get svn password: if !exists("g:svnTools_tmp") || g:svnTools_tmp == "" let l:tmp = inputsecret("Svn user: ".g:svnTools_svnUser.". Enter password: ") if g:svnTools_storeSvnPsswd == 1 let g:svnTools_tmp = l:tmp endif else let l:tmp = g:svnTools_tmp endif if l:tmp == "" call s:Error("Not valid svn password.") return endif return " --non-interactive --no-auth-cache --username ".g:svnTools_svnUser." --password ".l:tmp endfunction function! s:Error(mssg) echohl ErrorMsg | echom "[".s:plugin."] ".a:mssg | echohl None endfunction function! s:Warn(mssg) echohl WarningMsg | echom a:mssg | echohl None endfunction " Debug function. Log message function! s:LogLevel(level,func,mssg) if s:LogLevel >= a:level echom "[".s:plugin_name." : ".a:func."] ".a:mssg endif endfunction " Debug function. Log message and wait user key function! s:LogLevelStop(level,func,mssg) if s:LogLevel >= a:level call input("[".s:plugin_name." : ".a:func."] ".a:mssg." (press key)") endif endfunction func! svnTools#Verbose(level) if a:level == "" call s:LogLevel(0, expand(''), "Verbose level: ".s:LogLevel) return endif let s:LogLevel = a:level call s:LogLevel(0, expand(''), "Set verbose level: ".s:LogLevel) endfun function! s:SetSyntax(ext) if a:ext == "h" let l:ext = "cpp" elseif a:ext == "hpp" let l:ext = "cpp" else let l:ext = a:ext endif "silent exec("set syntax=".l:ext) silent exec("set ft=".l:ext) endfunction function! s:GetSyntax() let l:ext = expand("%:e") if l:ext == "h" return "cpp" elseif l:ext == "hpp" return "cpp" else return l:ext endif endfunction function! s:WindowSplitMenu(default) let w:winSize = winheight(0) "echo "w:winSize:".w:winSize | call input("continue") let text = "split hor&izontal\n&split vertical\nnew &tab\ncurrent &window" let w:split = confirm("", l:text, a:default) redraw call s:LogLevel(1, expand(''), "Choosed split:".w:split) endfunction function! s:WindowSplit() if !exists('w:split') return endif let l:split = w:split let l:winSize = w:winSize call s:LogLevel(1, expand(''), "New split:".w:split) if w:split == 1 silent exec("sp! | enew") elseif w:split == 2 silent exec("vnew") elseif w:split == 3 silent exec("tabnew") elseif w:split == 4 silent exec("enew") endif let w:split = l:split let w:winSize = l:winSize endfunction function! s:WindowSplitEnd() if exists('w:split') if w:split == 1 if exists('w:winSize') "echo "w:winSize:".w:winSize | call input("continue") let lines = line('$') + 2 if l:lines <= w:winSize "echo "resize:".l:lines | call input("continue") exe "resize ".l:lines else "echo "resize:".w:winSize | call input("continue") exe "resize ".w:winSize endif endif exe "normal! gg" endif endif silent! unlet w:winSize silent! unlet w:split endfunction function! s:PathToFile(path) if a:path == "" return "" endif let path = a:path let path = substitute(path, getcwd(), '', 'g') let path = substitute(path, '/', '_', 'g') let path = substitute(path, '-$', '', '') let path = substitute(path, '_-', '', '') let path = substitute(path, '-_', '', '') return l:path endfunction " Svn blame and Svn blame -v " Commands: SvnBlame, Svnbl, Svnblv. function! svnTools#Blame(opt) let file = expand("%") let name = expand("%:t") let path = expand("%:h") let pos = line('.') let ext = s:GetSyntax() let path = s:PathToFile(l:path) let name = "_svnBlame_".l:path.".".l:ext let l:svnCmd = g:svnTools_svnCmd let l:svnCmd .= s:CheckSvnUserAndPsswd() let command = l:svnCmd." blame ".a:opt." ".l:file "let command = g:svnTools_svnCmd." blame ".a:opt." ".l:file let callback = ["svnTools#SvnBlameEnd", l:pos, l:ext, l:name] call s:WindowSplitMenu(4) "call s:SystemCmd(command,callback,1) call s:SystemCmd0(l:command, l:callback, 1) endfunction function! svnTools#SvnBlameEnd(pos,ext,name,resfile) if exists('a:resfile') && !empty(glob(a:resfile)) " On vertical split synchronize scroll if exists('w:split') if w:split == 2 | set crb! | endif endif call s:WindowSplit() put = readfile(a:resfile) " Set syntax highlight silent exec("set ft=".a:ext) " Restore previous position silent exec("normal ".a:pos."G") silent exec("normal zz") " Rename buffer silent! exec("0file") silent! exec("bd! ".a:name) silent! exec("file! ".a:name) call s:WindowSplitEnd() " On vertical split synchronize scroll if exists('w:split') if w:split == 2 set crb! vertical resize 70 endif endif else call s:Warn("Svn blame empty") endif endfunction " Svn diff file/path " Command: SvnDiff, Svndf, Svnda, Svndd function! svnTools#Diff(path) let path = s:PathToFile(a:path) let name = "_svnDiff_".l:path.".diff" let l:svnCmd = g:svnTools_svnCmd let l:svnCmd .= s:CheckSvnUserAndPsswd() let command = l:svnCmd." --non-interactive diff --diff-cmd=diff ".a:path "let command = g:svnTools_svnCmd." --non-interactive diff --diff-cmd=diff ".a:path "let callback = "svnTools#SvnDiffEnd(\"".l:name."\"," let callback = ["svnTools#SvnDiffEnd", l:name] call s:WindowSplitMenu(4) "call s:SystemCmd(command,callback,1) call s:SystemCmd0(command,callback,1) endfunction function! svnTools#SvnDiffEnd(name,resfile) if !exists('a:resfile') || empty(glob(a:resfile)) call s:Warn("Svn diff empty") endif call s:WindowSplit() put = readfile(a:resfile) silent exec("set syntax=diff") " Rename buffer silent! exec("0file") silent! exec("bd! ".a:name) silent! exec("file! ".a:name) normal gg let @/ = '^+ \|^- ' call s:WindowSplitEnd() endfunction " Svn info " Command: SvnInfo, Svni function! svnTools#Info(path) let l:svnCmd = g:svnTools_svnCmd let l:svnCmd .= s:CheckSvnUserAndPsswd() let info = system(l:svnCmd." info ".a:path." \| egrep -i 'rev:|date' \| awk '{$1=$2=\"\";print}'") "let info = system("svn info ".a:path." \| egrep -i 'rev:|date' \| awk '{$1=$2=\"\";print}'") let list = split(l:info,"\n") "echo "".l:fileFullPath for file in l:list echo "".l:file endfor call input("") endfunction " Perform diff between same files on different directories/sandboxes " Arg1: file to check on both paths " Arg2: sandbox 1 path " Arg3: sandbox 2 path function! s:DiffFiles(file,path1,path2) let l:file1 = a:path1."/".a:file let l:file2 = a:path2."/".a:file return system("diff -ua ".l:file1." ".l:file2) endfunction " Perform vimdiff between same files on different directories/sandboxes " Arg1: file to check on both paths " Arg2: sandbox 1 path " Arg3: sandbox 2 path function! s:VimDiffFiles(file,path1,path2) let l:ext = fnamemodify(a:file, ":e") let l:file1 = a:path1."/".a:file let l:file2 = a:path2."/".a:file "Open new tab tabnew " Open file1 if !filereadable(l:file1) call s:Warn("File not found:".l:file1) silent exec("new ".l:file1) else silent exec("e ".l:file1) endif call s:SetSyntax(l:ext) silent exec("diffthis") " Visually show tabs and spaces silent exec("set invlist") silent exec("set listchars=tab:>.,trail:_,extends:\#,nbsp:_") " Open file2 " New vertical split if !filereadable(l:file2) call s:Warn("File not found:".l:file2) silent exec("vert new".l:file2) else silent exec("vert new") silent exec("e ".l:file2) endif silent exec("diffthis") call s:SetSyntax(l:ext) " Visually show tabs and spaces silent exec("set invlist") silent exec("set listchars=tab:>.,trail:_,extends:\#,nbsp:_") highlight DiffText cterm=BOLD ctermfg=Red ctermbg=DarkGrey endfunction " Perform vimdiff between two revisions or last svn revision and current one. " Arg1: rev0 to download from svn and compare, if empty use last revision. " Arg2: rev1 to download from svn and compare, if empty use current file on disk " Arg3: directory to save svn file into. function! s:VimDiffFileRev(file,rev0,rev1,saveDir) "Open new tab tabnew let path = fnamemodify(a:file,":p:h") let name = fnamemodify(a:file,":t:r") let ext = fnamemodify(a:file,":e") let nameExt = fnamemodify(a:file,":t") let file = a:file " Remove working directory from path let path1 = substitute(l:path, getcwd(), '', 'g') " Replace each / with _ let path1 = substitute(l:path1, '/', '_', 'g') " Replace duplicated _ let path1 = substitute(l:path1, "__", "_", "") let l:rev0 = "" let l:rev1 = "" if a:rev0 != "" let l:rev0 = "-r ".a:rev0." " endif if a:rev1 != "" let l:rev1 = "-r ".a:rev1." " endif " Get original file echo "This may take a while ..." if a:saveDir != "" let l:tmp = a:saveDir."/".l:path1."_".l:name."_r".a:rev0.".".l:ext else let l:tmp = l:path1."_".l:name."_r".a:rev0.".".l:ext endif if !filereadable(l:tmp) let l:svnCmd = g:svnTools_svnCmd let l:svnCmd .= s:CheckSvnUserAndPsswd() echo "svn cat ".l:rev0.a:file silent exec("r !".l:svnCmd." cat ".l:rev0.a:file) "silent exec("r !svn cat ".l:rev0.a:file) silent exec("0file") silent! exec("file ".l:tmp) setl nomodifiable setl buflisted setl bufhidden=delete setl buftype=nofile if a:saveDir != "" echo "Save ".l:tmp silent! exec("w! ".l:tmp) endif else echo "File1 found:".l:tmp silent exec("e ".l:tmp) endif call s:SetSyntax(l:ext) " Visually show tabs and spaces silent exec("set invlist") silent exec("set listchars=tab:>.,trail:_,extends:\#,nbsp:_") if a:rev1 != "" if a:saveDir != "" let l:tmp = a:saveDir."/".l:path1."_".l:name."_r".a:rev1.".".l:ext else let l:tmp = l:path1."_".l:name."_r".a:rev1.".".l:ext endif " Perform vertical vimdiff on two selected revisions of the file " Make this window part of the diff silent exec("diffthis") " New vertical split silent exec("vert new") if !filereadable(l:tmp) let l:svnCmd = g:svnTools_svnCmd let l:svnCmd .= s:CheckSvnUserAndPsswd() echo "svn cat ".l:rev1.a:file silent exec("r !.".l:svnCmd." cat ".l:rev1.a:file) "silent exec("r !svn cat ".l:rev1.a:file) silent exec("0file") silent! exec("file ".l:tmp) if a:saveDir != "" silent! exec("w! ".l:tmp) endif else echo "File2 found:".l:tmp silent exec("e ".l:tmp) endif silent exec("diffthis") call s:SetSyntax(l:ext) else " Perform vertical vimdiff between selected revision and current one. " WindowSplitMenu tab in a vimdiff manner silent exec("vert diffsplit ".l:file) endif call s:SetSyntax(l:ext) highlight DiffText cterm=BOLD ctermfg=Red ctermbg=DarkGrey endfunction " Vimdiff file " Arg1: file to check, if empty use current file. " Arg2: flags: check_binaries, skip_binaries, check_all. " Commands: SvnVimDiff, Svnvdf, Svnvda, Svnvdd. function! svnTools#VimDiff(path,flags) echo "svn st ".a:path.". In progress..." let l:list = s:GetStatusFilesList(a:path, '^[MAD] ') let l:omittBinaries = "no" let l:omittFiles = "no" if a:flags =~ "check_binaries" || a:flags =~ "check_all" let l:omittBinaries = "ask user" endif if a:flags =~ "skip_binaries" let l:omittBinaries = "yes" endif if a:flags =~ "check_all" let l:omittFiles = "ask user" endif " Check file number " Check if file must be skipped let l:filesList = [] let n = 0 for file in l:list let fileType = system("file ".l:file) " Check if file is directory if l:fileType =~ " directory" continue endif " Check if file is binary if l:omittBinaries != "no" if l:fileType !~ " text" && l:fileType !~ " link" if l:omittBinaries == "yes" call s:Warn("Skip binary file: ".l:file) continue endif if l:omittBinaries == "ask user" call s:Warn("Found binary file: ".l:file) let l:res = confirm("Omitt binary files?", "&yes\n&no\n&all\nn&One", 2) if l:res == 1 call s:Warn("Skip binary file: ".l:file) continue endif if l:res == 3 let l:omittBinaries = "yes" call s:Warn("Skip binary file: ".l:file) continue endif if l:res == 4 let l:omittBinaries = "no" endif endif endif endif " Check if file must be skipped if l:omittFiles != "no" if l:omittFiles == 1 call s:Warn("Skip file: ".l:file) continue endif if l:omittFiles == "ask user" let l:res = confirm("Skip file: ".l:file."?", "&yes\n&no\n&all\nn&one", 2) if l:res == 1 call s:Warn("Skip file: ".l:file) continue endif if l:res == 3 let l:omittFiles = "yes" call s:Warn("Skip file: ".l:file) continue endif if l:res == 4 let l:omittFiles = "no" endif endif endif let l:filesList += [ l:file ] let l:n += 1 endfor if l:n == 0 call s:Warn("No modifications found") return elseif l:n > 1 " Show all files modified redraw echo "Modified Files:" for file in l:filesList echo "+ ".l:file endfor echo "" else " Only one file modified " Open vimdiff without asking user call s:WindowSplitMenu(3) call s:WindowSplit() echo "This may take a while ..." silent! call s:VimDiffFileRev(l:file,"","","") call s:WindowSplitEnd() return endif echo "" call confirm("Perform vimdiff on each file?") redraw echo "Getting every file diff..." for file in l:filesList echo "* ".l:file silent! call s:VimDiffFileRev(l:file,"","","") endfor call s:WindowSplitEnd() redraw endfunction " Using svn st get the files changed/added/removed on path1 and path2 " Perform vimdiff on all files changed/added/remove between both paths. " Arg1: mode, diff or vimdiff. " Arg2: primary file/path to check for changes, if empty use current file. " Arg3: secondary sandbox path to compare changes. " Arg4: flags: omitt_equal_files, omitt_binaries. " Commands: Svnvdc SvnvdC. function! svnTools#CompareDirChanges(mode,path1,path2,flags) echo "Comparing changes on dir: '".a:path1."' with dir: '".a:path2."'" echo "This may take a while ..." echo " " if a:path1 == "./" let l:path1 = expand("%:p:h") else let l:path1 = a:path1 endif if a:path2 == "./" let l:path2 = expand("%:p:h") else let l:path2 = a:path2 endif let l:mssg1 = "" let l:mssg2 = "" " Get files changed on svn for path1 let st1 = s:GetStatusFilesString(l:path1, '^M\|^A\|^D') let st1 = substitute(l:st1, l:path1, '', 'g') let tmpList = split(l:st1," ") call s:LogLevel(1, expand(''), "Dir1: ".l:path1) call s:LogLevel(2, expand(''), "Files: ".l:st1) call s:LogLevel(2, expand(''), "") if len(l:tmpList) == 0 call s:Warn("No modifications found (".l:path1.")") else echo "Files modified: ".len(l:tmpList)." (".l:path1.")" let l:mssg1 = "Path1: ".l:path1." files modified: ".len(l:tmpList) endif " Get files changed on svn for path2 let st2 = s:GetStatusFilesString(l:path2, '^M\|^A\|^D') let st2 = substitute(l:st2, l:path2, '', 'g') let tmpList += split(l:st2," ") call s:LogLevel(1, expand(''), "Dir2: ".l:path2) call s:LogLevel(2, expand(''), "Files: ".l:st2) call s:LogLevel(2, expand(''), "") if len(split(l:st2," ")) == 0 call s:Warn("No modifications found (".l:path2.")") else let l:mssg2 = "Path2: ".l:path2." files modified: ".len(split(l:st2," ")) endif redraw if l:mssg1 != "" echo l:mssg1 endif if l:mssg2 != "" echo l:mssg2 endif if len(l:tmpList) == 0 echo "" call s:Warn("No modifications found") return endif " Remove duplicated files " Sort files let sortedList = sort(copy(l:tmpList)) call s:LogLevel(2, expand(''), "Files sorted: ".join(l:sortedList)) call s:LogLevel(2, expand(''), "") " Uniq files let filesList = filter(copy(l:sortedList), 'index(l:sortedList, v:val, v:key+1)==-1') call s:LogLevel(2, expand(''), "Files uniq: ".join(l:filesList)) call s:LogLevel(2, expand(''), "") echo "Files to check: ".len(l:filesList) echo " " echo "Check: ".a:path1." files against ".a:path2." files" echo " " call s:LogLevel(1, expand(''), "Continue?") " Check files based on the selected flags: let l:finalFilesList = [] let l:n = 0 for file in l:filesList let l:n += 1 let tmpPath1 = fnamemodify(l:file,":p:h") let file = substitute(l:file, l:tmpPath1, '', 'g') let file1 = a:path1."/".l:file let file2 = a:path2."/".l:file if !filereadable(l:file1) && !filereadable(l:file2) call s:Error("Error. Files not found:".l:file1.", ".l:file2) continue endif "echo "Check ".l:n.": ".l:file " Omitt binary files. if a:flags =~ "omitt_binaries" if filereadable(l:file1) let res = system("file ".l:file1) if l:res !~ " directory" if l:res !~ " text" && l:res !~ " link" call s:Warn("Omitt binary file: ".l:file) continue endif endif endif endif " Omitt files when no difference found. if a:flags =~ "omitt_equal_files" if filereadable(l:file2) && filereadable(l:file1) let res = system("diff -qa ".l:file1." ".l:file2) if l:res == "" call s:Warn("Omitt file with no differences: ".l:file) continue endif endif endif echo "Add file ".l:n.": ".l:file let l:finalFilesList += [ l:file ] endfor if len(l:finalFilesList) == 0 echo " " call s:Warn("NO DIFFERENCES FOUND.") return endif echo " " echo "Files to open: ".len(l:finalFilesList) call confirm("Continue to ".a:mode." on every file?") redraw " Perform diff/vimdiff between both paths for the same file. let l:diffText = "" let l:n = 0 for file in l:finalFilesList let l:n += 1 if a:mode == "vimdiff" echo "Vimdiff ".l:n.": ".l:file call s:VimDiffFiles(l:file, l:path2, l:path1) else echo "Diff ".l:n.": ".l:file let l:diffText .= s:DiffFiles(l:file, l:path2, l:path1) endif endfor if a:mode == "diff" call s:WindowSplitMenu(3) call s:WindowSplit() let @a = l:diffText silent put! a normal gg0 silent exec("0file") silent! exec("file _dirDiff.diff") set ft=diff endif call s:WindowSplitEnd() endfunction " Vimdiff current file with the selected revision " Arg1: revision number " Arg2: file to check, if empty use current file. function! s:VimdiffRev(rev,file) let l:confirm = "" if a:file == "" let l:file = expand('%') else let l:file = a:file endif if l:file == "" call s:Warn("File path not found") return endif if a:rev == "" let l:rev = expand('') let l:confirm = "yes" else let l:rev = a:rev endif if l:rev == "" call s:Warn("Revision number not found") return endif if l:confirm != "" if confirm("Check file: ".l:file,"Rev: ".l:rev."? &yes\n&no\n",2) == 2 return endif endif echo "This may take a while ..." call s:WindowSplitMenu(3) call s:WindowSplit() call s:VimDiffFileRev(l:file,l:rev,"","") call s:WindowSplitEnd() endfunction " On a unified diff file. Extract file path and revision number " Parse the file and perform diff between the paths found starting with " string: --- or string: +++. " Diff line format expected: " Ex: " --- path/file.cpp (revision 188593) " +++ path/file.cpp (revision 188594) let s:DiffFile = "" let s:DiffRev = "" function! s:DiffGetFileRev() let s:DiffFile = "" let s:DiffRev = "" normal 0yiW let l:tmp = @" if l:tmp != "---" && l:tmp != "+++" call s:Warn("Diff line format '".l:tmp."' unknown") return 1 endif if l:tmp =~ "revision" | return 1 | endif normal wyiW if @" == "" | return 1 | endif let s:DiffFile = @" normal Wwwyiw if @" == "" || @" == ")" || @" == "copy" | return 1 | endif let s:DiffRev = @" return 0 endfunction " On a unified diff file and current line with format Ex: " --- path/file.cpp (revision 188593) " or " +++ path/file.cpp (revision 188594) " " Open vimdiff with file path and revision number function! s:VimdiffRevDiffFile() let l:rev0 = "" let l:rev1 = "" if s:DiffGetFileRev() == 1 call s:Warn("Revision number not found") return 1 endif let match = matchstr(getline('.'), '.*--- ') " regex match if(!empty(match)) let l:rev0 = s:DiffRev normal j else let l:rev1 = s:DiffRev normal k endif if s:DiffGetFileRev() == 1 call s:Warn("Revision number not found") return 1 endif let match = matchstr(getline('.'), '.*+++ ') " regex match if(!empty(match)) let l:rev1 = s:DiffRev else let l:rev0 = s:DiffRev endif if confirm("Vimdiff file: ".s:DiffFile,"Rev: ".l:rev0.":".l:rev1."? &yes\n&no\n",2) == 2 return endif call s:VimDiffFileRev(s:DiffFile,l:rev0,l:rev1,"") return 0 endfunction " On a unified diff file. Search all lines starting on --- and get the " filename and revision number, then perform vimdiff. " Ex: line format: " --- path/file.cpp (revision 188593) function! s:VimdiffRevDiffAll() echo "Perform vimdiff on each modified file:" let l:list = [ ] if l:dir = "" let file = readfile(expand("%:p")) " read current file let cmd = "" | let File = "" | let rev = "" for line in file let match = matchstr(line, '.*--- ') " regex match if(!empty(match)) if l:cmd != "" echo " - Diff: ".l:cmd call insert(l:list, l:cmd) let File = "" let rev = "" endif new setlocal bt=nofile let @a = l:line put! a normal gg0 if s:DiffGetFileRev() == 0 let cmd = s:DiffFile." ".s:DiffRev let File = s:DiffFile let rev = s:DiffRev endif silent! execute 'bd!' endif let match = matchstr(line, '.*+++ ') " regex match if(!empty(match)) new setlocal bt=nofile let @a = l:line put! a normal gg0 if s:DiffGetFileRev() == 0 if l:rev != "" && l:File == s:DiffFile let cmd .= " ".s:DiffRev let dir = s:DiffRev endif endif silent! execute 'bd!' endif endfor if l:cmd != "" echo " - Diff: ".l:cmd call insert(l:list, l:cmd) endif if len(l:list) <= 0 call s:Warn("No modifications found") return endif let l:saveDir = "" if l:dir != "" if confirm("","Save/get dir: _r".l:dir."? &yes\n&no\n",2) == 1 " Create new directory named after with revision number let l:saveDir = "_r".l:dir if !isdirectory(l:saveDir) call mkdir(l:saveDir) endif endif endif let l:open = 1 for cmd in l:list let args = split(l:cmd, " ") let file = get(l:args, 0, "") let rev0 = get(l:args, 1, "") let rev1 = get(l:args, 2, "") if l:open != 3 echo "" let l:open = confirm("Vimdiff: ".l:file,"Rev: ".l:rev0."/".l:rev1."? &yes\n&no\nopen &all\n",1) if l:open == 2 | continue | endif endif call s:VimDiffFileRev(l:file,l:rev0,l:rev1,l:saveDir) endfor if l:saveDir != "" " Save current vim session exec("mksession! ".l:saveDir."/vdiff.vim") endif endfunction " When placed on a diff file, for each modified file, get its name and filepath. " Return: list containing all modified file paths. function! s:DiffGetModifiedFilesList() let list = [] let @z="" silent g/^Index: /y Z silent new silent put=@Z silent! %s/^$//g silent %s/Index: //g if line('$') == 1 && getline(".") == "" " Empty file else let @z="" silent normal ggVG"zy let files = @z let list = split(l:files, "\n") endif quit return l:list endfunction " When placed on a log diff file (exmple: r29456.diff) " Extract all files modified and perform vimdiff from the revision " Cmd: Svndvdr function! svnTools#LogVimDiffRevision() "function! svnTools#DiffGetVimDiffChanges() if expand("%") !~ "_r[1-9].*\.diff" echo "First launch commands: Svnr, to get the revision diff." call s:Error("Current file is not an svn diff file!") return endif " Extract from log file, the revision number. let l:list = s:SvnLogFileGetCommitNumberList() if len(l:list) != 1 call s:Error("Could't find the revision number!") return endif let l:rev = substitute(l:list[0], '[^0-9]*', '', 'g') let rev2 = str2nr(l:rev) let rev1 = l:rev2 - 1 " Extract from diff file, the list of modified files. let l:list = s:DiffGetModifiedFilesList() redraw echo "Files modified: " for file in l:list echo " ".l:file endfor echo "Number of files modified: ".len(l:list) call confirm("Perform vimdiff on each file for between revision: ".l:rev1." and".l:rev2.". Continue?") " Perform vimdiff for each file and revision. redraw echo "Opening ". l:rev1 .":". l:rev2 ." modifications with vimdiff:" for file in l:list echo "- ". l:file silent call s:VimDiffFileRev(l:file, l:rev1, l:rev2, 0) endfor endfunction " Perform vimdiff on all modified files on revision2 with the same files on revision1 " When no revision number provided as argument, try get word under cursor as the " revision number. " When REV2 not provided set REV2=REV1, and dreacrese REV1. " Arg1: [optional] revision1 " Arg2: [optional] revision2 " Cmd: Svnvdr function! svnTools#VimDiffRevision(...) let rev1 = "" let rev2 = "" if a:0 >= 2 let rev1 = a:1 let rev2 = a:2 elseif a:0 == 1 let rev2 = a:1 else let rev2 = expand("") endif if l:rev2 == "" call s:Error("Missing revision number") return endif let rev2 = substitute(l:rev2, '[^0-9]*', '', 'g') if l:rev2 == "" call s:Error("Wrong revision number ". l:rev2) return endif if l:rev1 == "" let l:rev1 = l:rev2 -1 else let rev1 = substitute(l:rev1, '[^0-9]*', '', 'g') if l:rev1 == "" call s:Error("Wrong revision number ". l:rev1) return endif endif " Get diff file echo "Getting r". l:rev2 ." log and diff..." let l:svnCmd = g:svnTools_svnCmd let l:svnCmd .= s:CheckSvnUserAndPsswd() " On Subversion versions after 1.7: let command = l:svnCmd." log -vr ".l:rev2." --diff" "let command = "svn log -vr ".l:rev2." --diff" let text = system(l:command) " Extract from diff file, the list of modified files. tabnew put=l:text normal ggdd let l:list = s:DiffGetModifiedFilesList() "quit silent! exec("0file") silent! exec("file! _r". l:rev2 .".diff") set ft=diff normal gg redraw for file in l:list echo l:file endfor call confirm(len(l:list) ." modified files found. Continue?") " Perform vimdiff for each file and revision. redraw echo "Opening ". l:rev1 .":". l:rev2 ." modifications with vimdiff:" for file in l:list echo "- ". l:file silent call s:VimDiffFileRev(l:file, l:rev1, l:rev2, 0) endfor endfunction " When placed on a diff file. Extract all modified file names and path. " Perform vimdiff on this files for revision2 with the same files on revision1 " When no revision number provided as argument, try get word under cursor as the " revision number. " When REV2 not provided set REV2=REV1, and dreacrese REV1. " Arg1: [optional] revision1 " Arg2: [optional] revision2 " Cmd: SvnDiffVdr function! svnTools#DiffFileVimDiffRevision(...) if &filetype !=# 'diff' call s:Warn("Attention, file type is not diff!") call input("") endif let rev1 = "" let rev2 = "" if a:0 >= 2 let rev1 = a:1 let rev2 = a:2 elseif a:0 == 1 let rev2 = a:1 else let rev2 = expand("") endif if l:rev2 == "" call s:Error("Missing revision number") return endif let rev2 = substitute(l:rev2, '[^0-9]*', '', 'g') if l:rev2 == "" call s:Error("Wrong revision number ". l:rev2) return endif if l:rev1 == "" let l:rev1 = l:rev2 -1 else let rev1 = substitute(l:rev1, '[^0-9]*', '', 'g') if l:rev1 == "" call s:Error("Wrong revision number ". l:rev1) return endif endif " Save window position let l:winview = winsaveview() " Extract from diff file, the list of modified files. let l:list = s:DiffGetModifiedFilesList() " Restore window position call winrestview(l:winview) redraw for file in l:list echo l:file endfor call confirm(len(l:list) ." modified files found. Continue?") " Perform vimdiff for each file and revision. redraw echo "Opening ". l:rev1 .":". l:rev2 ."modifications with vimdiff:" for file in l:list echo "- ". l:file silent call s:VimDiffFileRev(l:file, l:rev1, l:rev2, 0) setl nomodifiable endfor endfunction " When placed on a diff file. Extract current line modified file name and path. " Perform vimdiff on this files for revision2 with the same files on revision1 " When no revision number provided as argument, try get the revision from (revision xxxxxx) at line end. " revision number. " When REV2 not provided set REV2=REV1, and dreacrese REV1. " Arg1: [optional] revision1 " Arg2: [optional] revision2 " Cmd: SvnDiffVdrf function! svnTools#DiffFileVimDiffRevisionFile(...) if &filetype !=# 'diff' call s:Warn("Attention, file type is not diff! (Press key to continue)") endif let rev1 = "" let rev2 = "" let file = "" if a:0 >= 3 let file = a:3 endif if a:0 >= 2 let rev1 = a:1 let rev2 = a:2 elseif a:0 == 1 let rev2 = a:1 else " Verify current line type. normal $bbviW"zy let l:check = @z if l:check != "(revision" call s:Error("Verify cursor line position. Place on line ending with: '(revision xxxx)'.") return endif " Extract revision number normal $bviw"zy let l:rev2 = @z endif if l:rev2 == "" call s:Error("Missing revision number") return endif let rev2 = substitute(l:rev2, '[^0-9]*', '', 'g') if l:rev2 == "" call s:Error("Wrong revision number ". l:rev2) return endif if l:rev1 == "" let l:rev1 = l:rev2 -1 else let rev1 = substitute(l:rev1, '[^0-9]*', '', 'g') if l:rev1 == "" call s:Error("Wrong revision number ". l:rev1) return endif endif if file == "" " Get file path from current line. " Save window position let l:winview = winsaveview() " Verify current line type. normal 0lllvl"zy let l:tag0 = @z normal 0viW"zy let l:tag1 = @z if l:tag0 =~ "[A-Z] " " Log line with path normal 0wwviW"zy let l:file = @z elseif l:tag1 == "Index:" || l:tag1 == "---" || l:tag1 == "+++" " Diff line with path normal 0wviW"zy let l:file = @z else echo "Place cursor on diff line starting with: 'Index: PATH', '--- PATH', '+++ PATH'." echo "Place cursor on log changed path line starting with: ' [A-Z] PATH'." call s:Error("Line format error. Verify cursor line position.") return endif " Restore window position call winrestview(l:winview) endif " Extract from diff file, the file name and path. if l:file == "" call s:Error("File path not found on current file. Check cursor line position") return endif call confirm("Vimdiff file: ". l:file ." rev: ". l:rev1 .":". l:rev2) " Perform vimdiff for file and revision. redraw echo "Opening ". l:file ." ". l:rev1 .":". l:rev2 ." with vimdiff..." silent call s:VimDiffFileRev(l:file, l:rev1, l:rev2, 0) setl nomodifiable endfunction " Get the svn log history for the selected path. " Arg1: path. " Arg2: [optional] options ex: -v, --verbose. " Commands: Svnlf "function! svnTools#LogFile(filepath, options) "function! svnTools#LogFile(filepath) function! svnTools#LogFile(...) let l:options = "" if len(a:000) == 0 let l:filepath = expand("%") elseif len(a:000) >= 1 let l:filepath = substitute(a:1, getcwd(), "", "g") elseif len(a:000) >= 1 let l:options = a:2 ." " endif let file = substitute(l:filepath, "\/", "_", "g") let l:svnCmd = g:svnTools_svnCmd let l:svnCmd .= s:CheckSvnUserAndPsswd() let name = "_svnLog_". l:file .".log" let command = l:svnCmd." log ". l:options . l:filepath "let command = "svn log ". l:options . l:filepath "let command = "svn log ". l:filepath let callback = ["svnTools#SvnLogFileEnd", l:name] call s:WindowSplitMenu(4) call s:SystemCmd0(l:command, l:callback, 1) endfunction function! svnTools#SvnLogFileEnd(name, resfile) if exists('a:resfile') && !empty(glob(a:resfile)) call s:WindowSplit() " Rename buffer silent! exec("0file") silent! exec("bd! ".a:name) silent! exec("file! ".a:name) put = readfile(a:resfile) silent exec("normal gg") call delete(a:resfile) normal gg call s:WindowSplitEnd() else call s:Warn("Svn log file search empty") endif endfunction " When placed on a svn log file, get each commit number. " Return: list containing all commit numbers. function! s:SvnLogFileGetCommitNumberList() let list = [] let @z="" silent g/^r.*\|.*(.*).*lines/y Z silent new silent put=@Z silent! g/^$/d silent %s/ |.*$//g "silent bd! if line('$') == 1 && getline(".") == "" " Empty file else let @z="" silent normal ggVG"zy let revNum = @z let list = split(l:revNum, "\n") endif quit return l:list endfunction " Get svnl log and svn diff from the given revision number. " Arg1: rev. Revision number: r34567 or 34567. " Arg2: mode. Use new_tab to open each revison on new tab. function! s:SvnLogFileGetRevisionDiff(rev, mode) let l:rev = substitute(a:rev, '[^0-9]*', '', 'g') let prev = l:rev - 1 let name = "_r".l:rev.".diff" let l:svnCmd = g:svnTools_svnCmd let l:svnCmd .= s:CheckSvnUserAndPsswd() let command = l:svnCmd." log -vr ".l:rev." --diff" "let command = "svn log -vr ".l:rev." --diff" let text = system(l:command) if l:text == "" echo "Failed" return 1 endif if a:mode == "new_tab" silent tabnew normal ggO silent put=l:text normal ggdd " Rename buffer silent! exec("0file") silent! exec("bd! ".l:name) silent! exec("file! ".l:name) silent exec("set syntax=diff") silent exec("normal gg") else normal GO silent put=l:text endif endfunction " When placed on the svn log file open all revisions. " Commands: Svnlr. function! svnTools#LogGetRevDiff(num) if expand("%") !~ "_svnLog_.*\.log" echo "First launch one of following commands: Svnl, Svnls, Svnlf, Svnld or Svnlp, to get the revision log." call s:Error("Current file is not an svn log file!") return endif let l:filename = expand("%") " Get all revision numbers let l:list = s:SvnLogFileGetCommitNumberList() if len(l:list) == 0 call s:Error("No revision number found on current buffer!") return endif redraw echo "Number of commits: ".len(l:list) echo "Commits found: ".join(l:list) if a:num != "" call confirm("Open the first ".a:num." revisions. Continue?") let max = str2nr(a:num) else call confirm("Open each revision diff. Continue?") let max = len(l:list) endif if confirm("Open each revision on new tab?", "&yes\n&no", 1) == 2 let l:mode = "same_tab" silent tabnew " Rename buffer let l:newname = substitute(l:filename, 'svnLog', 'svnLogRevDiff_'.l:max.'Rev_', "g") if l:newname != "" silent! exec("0file") silent! exec("bd! ".l:newname) silent! exec("file! ".l:newname) endif else let l:mode = "new_tab" endif redraw " Perform svn log and svn diff for each revision. let n=1 for rev in l:list echo l:n."/".l:max.") Getting ".l:rev." log and diff..." call s:SvnLogFileGetRevisionDiff(l:rev, l:mode) let n+=1 if a:num != "" && l:n > l:max | break | endif endfor endfunction " Search the svn log for commint number " Svn log -v -g " Arg1: number of commits to search " Commands: SvnLog, Svnl. function! svnTools#Log(commitNumb) let lastCommits = "" let numbCommits = "All" if a:commitNumb != "" let lastCommits = " -l ".a:commitNumb let numbCommits = a:commitNumb endif echo "Get log changes (include trunk, branches and tags). Commit numb:".l:numbCommits.")" let l:svnCmd = g:svnTools_svnCmd let l:svnCmd .= s:CheckSvnUserAndPsswd() " Get URL, remove trunk word let l:url = system(l:svnCmd." info \| grep URL \| awk 'NR==1 {print $2}' \| sed 's/trunk//'") "let l:url = system("svn info \| grep URL \| awk 'NR==1 {print $2}' \| sed 's/trunk//'") " Get svn log from last x commits echo "svn log -v -g ".l:lastCommits." ".l:url echo "This may take a while ..." let l:name = "_svnSearch".l:numbCommits.".log" let command = l:svnCmd." log -v -g ".l:lastCommits." ".l:url "let command = "svn log -v -g ".l:lastCommits." ".l:url let callback = ["svnTools#SvnLogSearchEnd", l:name] call s:WindowSplitMenu(4) call s:SystemCmd0(l:command,l:callback,1) endfunction function! svnTools#SvnLogSearchEnd(name,resfile) if exists('a:resfile') && !empty(glob(a:resfile)) call s:WindowSplit() " Rename buffer silent! exec("0file") silent! exec("bd! ".a:name) silent! exec("file! ".a:name) put = readfile(a:resfile) silent exec("normal gg") call delete(a:resfile) normal gg call s:WindowSplitEnd() else call s:Warn("Svn log search empty") endif endfunction " Search the svn log for pattern " Svn log -v --search " Arg1: pattern to search. " Arg2: number of commits to search. " Commands: SvnLogSearch, Svnls. function! svnTools#LogSearchPattern(...) let lastCommits = g:svnTools_lastCommits if a:0 >= 2 let lastCommits = a:2 endif if a:0 < 1 let pattern = substitute(expand(""), 'r', '', '') if l:pattern == "" let l:pattern = expand('%:t') endif else let pattern = a:1 endif if l:pattern == "" call s:Warn("Argument 1: search pattern not found.") return endif echo "Search pattern: ".l:pattern." on last ".lastCommits." commits..." echo "svn log -v --search ".l:pattern." -l ".l:lastCommits let l:name = "_svnSearch".l:lastCommits."_".l:pattern.".log" let l:svnCmd = g:svnTools_svnCmd let l:svnCmd .= s:CheckSvnUserAndPsswd() let command = l:svnCmd." log -v --search ".l:pattern." -l ".l:lastCommits "let command = "svn log -v --search ".l:pattern." -l ".l:lastCommits let callback = ["svnTools#SvnLogSearchPatternEnd", l:name] call s:WindowSplitMenu(4) echo "This may take a while ..." call s:SystemCmd0(l:command,l:callback,1) endfunction function! svnTools#SvnLogSearchPatternEnd(name,resfile) if exists('a:resfile') && !empty(glob(a:resfile)) call s:WindowSplit() " Rename buffer silent! exec("0file") silent! exec("bd! ".a:name) silent! exec("file! ".a:name) put = readfile(a:resfile) silent exec("set syntax=diff") silent exec("normal gg") call delete(a:resfile) normal gg call s:WindowSplitEnd() else call s:Warn("Svn log search pattern empty") endif endfunction " Get log and diff from the selected revision. " Arg1: revision number to search. " Command: Svnr function! svnTools#GetLogDiffRev(rev) if a:rev == "" let l:rev = expand("") else let l:rev = a:rev endif let rev = substitute(l:rev, '[^0-9]*', '', 'g') if l:rev == "" call s:Error("Revision number not found.") return endif let prev = l:rev - 1 let name = "_r".l:rev.".diff" echo "Getting ".l:rev." log and diff" let l:svnCmd = g:svnTools_svnCmd let l:svnCmd .= s:CheckSvnUserAndPsswd() "On Subversion versions after 1.7: let command = l:svnCmd." log -vr ".l:rev." --diff" "let command = "svn log -vr ".l:rev." --diff" let callback = ["svnTools#SvnLogDiffEnd", l:name] call s:WindowSplitMenu(4) call s:SystemCmd0(l:command,l:callback,1) endfunction function! svnTools#SvnLogDiffEnd(name,resfile) if !exists('a:resfile') || empty(glob(a:resfile)) call s:Warn("Svn log and diff empty") return endif call s:WindowSplit() call s:LogLevel(1, expand(''), "name=".a:name) " Rename buffer silent! exec("0file") silent! exec("bd! ".a:name) silent! exec("file! ".a:name) put = readfile(a:resfile) silent exec("set syntax=diff") silent exec("normal gg") call delete(a:resfile) call s:WindowSplitEnd() endfunction " Get vimdiff from current file on the selected revision. " Arg1: revision number to search. function! s:SvnRevVimdiffFile(rev) let rev = a:rev if a:rev == "" let l:rev = substitute(expand(""), 'r', '', '') endif if l:rev == "" call s:Warn("Argument 1: revision number not found.") return endif let file = expand('%') let pos = "" if l:file =~ "_svn" " Filename is something like: _svnBlame_myFile.cpp " Get original file name without the _svn... prefix if l:file =~ "_svnBlame" let pos = line('.') endif let file = substitute(l:file,"_svnBlame_",'','') let file = substitute(l:file,"_svn.\{-\}_",'','') endif echo "Getting vimdiff on ".l:file." r".l:rev echo "This may take a while ..." silent exec("tabnew") | call MyTabLineRefresh() call s:VimDiffFileRev(l:file,l:rev,"","") if l:pos != "" set foldlevel=20 silent exec("normal ".l:pos."G") endif endfunction " Get the current file's selected svn revision. " Arg1: revision number to search. " Commands: SvnCatRev, Svncr. function! svnTools#GetRevision(rev) let l:file = expand("%") let l:ext = expand("%:e") let l:name = expand("%:t") if a:rev == "" let l:rev = substitute(expand(""), 'r', '', '') else let l:rev = substitute(a:rev, 'r', '', '') endif if l:rev == "" call s:Warn("Argument 1: revision number not found.") return endif let file = "r".l:rev.".diff" let filepath = expand("%") let filename = expand("%:t:r") echo "Get file: ".l:filename." revision: ".l:rev call s:WindowSplitMenu(3) call s:WindowSplit() echo "svn cat -r ".l:rev." ".l:filepath echo "This may take a while ..." let l:svnCmd = g:svnTools_svnCmd let l:svnCmd .= s:CheckSvnUserAndPsswd() exec("r! ".l:svnCmd." cat -r ".l:rev." ".l:filepath) "exec("r! svn cat -r ".l:rev." ".l:filepath) if line('$') == 1 call s:WindowSplitEnd() call s:Warn("Not found") return endif call s:SetSyntax(l:ext) silent exec("0file") silent! exec("file _svnCat_".l:name."_r".l:rev.".".l:ext) call s:WindowSplitEnd() endfunction " Show on a new window the svn status lines matching the selected filter " pattern. " Arg1: filter pattern. " Commands: Svnst function! svnTools#Status(path, filter, remove) let l:svnCmd = g:svnTools_svnCmd let l:svnCmd .= s:CheckSvnUserAndPsswd() let command = l:svnCmd." st ".a:path "let command = "svn st ".a:path let callback = ["svnTools#StatusEnd", a:path, a:filter, a:remove] call s:WindowSplitMenu(1) call s:SystemCmd0(l:command, l:callback, 1) endfunction function! svnTools#StatusEnd(path, filter, remove, resfile) if !exists('a:resfile') || empty(glob(a:resfile)) call s:Warn("Svn st ". a:path .". No modifications found.") return endif call s:WindowSplit() put = readfile(a:resfile) " Filter window content. if a:filter != "" silent exec "g!/". a:filter ."/d" let l:filter0 = a:filter else let l:filter = "" let l:filter0 = "none" endif " Filter window content. if a:remove != "" silent exec "g/". a:remove ."/d" endif let noResults = 0 if line('$') == 1 && getline(".") == "" let noResults = 1 endif if line('$') == 2 && getline(1) == "" && getline(2) == "" let noResults = 1 endif " Close window if empty "if line('$') == 1 && getline(".") == "" if l:noResults == 1 if a:filter == "" && a:remove == "" call s:Warn("Svn st ". a:path .". No modifications found.") else call s:Warn("Svn st ". a:path .". No modifications found. (Keep: ". a:filter .". Remove: ". a:remove.")" ) endif quit return endif let l:lines0 = line('$') call delete(a:resfile) " Rename window silent exec("0file") silent! exec("file _svnSt.txt") " Add header on top normal ggO if a:filter == "" && a:remove == "" let text = "[svnTools.vim] svn st '".a:path."' (".l:lines0." results)" else let text = "[svnTools.vim] svn st '".a:path."' Filter: keep:'".l:filter0."', remove:'".a:remove."' (".l:lines0." results)" endif put=l:text normal ggdd " Resize window to fit content. call s:WindowSplitEnd() " Highlight lines in different colors. If hi.vim plugin available. if exists('g:HiLoaded') let g:HiCheckPatternAvailable = 0 silent! call hi#config#PatternColorize(" C ", "m*") // Conflicted silent! call hi#config#PatternColorize("! ", "m1@") // Missing silent! call hi#config#PatternColorize("\\~ ", "m2@") // Obstructed by some item of different kind silent! call hi#config#PatternColorize("A ", "g*") // Added "silent! call hi#config#PatternColorize("C ", "m*") // Conflicted silent! call hi#config#PatternColorize("D ", "r*") // Deleted silent! call hi#config#PatternColorize("I ", "n*") // Ignored silent! call hi#config#PatternColorize("M ", "b*") // Modified silent! call hi#config#PatternColorize("R ", "o*") // Replaced silent! call hi#config#PatternColorize("X ", "w7*-") // Unversioned directory silent! call hi#config#PatternColorize("? ", "w7*-") // Unversioned file let g:HiCheckPatternAvailable = 1 endif endfunction " Get files from svn status that match the selected filter pattern. " Arg1: filter pattern. " Arg2: pattern to keep from the result: " Keep only files in conflict: "^C ". " Keep only modified files: "^M ". " Keep only modified, added or deleted files: "^[MAD] ". " Return: list with all files. function! s:GetStatusFilesList(path, filter) let l:list = [] let l:svnCmd = g:svnTools_svnCmd let l:svnCmd .= s:CheckSvnUserAndPsswd() let text = system(l:svnCmd." st ". a:path) "let text = system("svn st ". a:path) if l:text == "" return l:list endif silent new normal ggO silent put=l:text normal ggdd if a:filter != "" silent exec "g!/". a:filter ."/d" endif silent exec "g/^$/d" if line('$') == 1 && getline(".") == "" " Empty file else " Get last column. File paths. silent normal gg0f/G$"zy let files = @z "echo "Files: ".l:i let list = split(l:files, "\n") "for i in l:list | echo "List: ".l:i | endfor endif quit return l:list endfunction " Get files from svn status that match the selected filter pattern. " Arg1: filter pattern. " Return: string with all files. function! s:GetStatusFilesString(path, filter) let l:list = [] let l:svnCmd = g:svnTools_svnCmd let l:svnCmd .= s:CheckSvnUserAndPsswd() let text = system(l:svnCmd." st ". a:path) if l:text == "" return l:list endif silent new normal ggO silent put=l:text normal ggdd if a:filter != "" silent exec "g!/". a:filter ."/d" endif silent exec "g/^$/d" if line('$') == 1 && getline(".") == "" " Empty file let l:res = "" else "normal VGww"z silent normal gg0WG$"zy let files = @z let l:res = substitute(l:files, "\n", " ", "g") if l:res =~ "not a working copy" call s:Error("Not a svn working copy") return [] endif endif silent exec("bd!") return l:res endfunction " Merge window layout 2. " Window layout 2: " -------------------------- " | | | " | Current | Right | " | | | " -------------------------- function! MergeLayout2(list) let fileNum = len(a:list) if l:fileNum < 4 call s:Error("Merge file list not compleat. Only ". l:fileNum ." files provided.") return endif let fileCenter = a:list[1] let fileRight = a:list[3] tabnew let n = 0 if !empty(glob(l:fileCenter)) silent exec("edit ". l:fileCenter) if line('$') == 1 && getline(".") == "" quit else if expand('%') == l:fileCenter let n += 1 else call s:Warn("missing file: ". l:fileCenter) quit endif endif else call s:Warn("missing file: ". l:fileCenter) endif if !empty(glob(l:fileRight)) if l:n != 0 silent exec("vert new") endif silent exec("edit ". l:fileRight) if line('$') == 1 && getline(".") == "" quit else if expand('%') != l:fileRight call s:Warn("missing file: ". l:fileRight) quit else let n += 1 endif endif else call s:Warn("Missing or empty file: ". l:fileRight) endif if l:n == 0 silent! tabclose elseif l:n > 1 silent exec("windo diffthis") endif endfunction " Merge window layout 3. " Window layout 3: " -------------------------- " | | | | " | Left | Current | Right | " | | | | " -------------------------- function! MergeLayout3(list) let fileNum = len(a:list) if l:fileNum < 4 call s:Error("Merge file list not compleat. Only ". l:fileNum ." files provided.") return endif let fileCenter = a:list[1] let fileLeft = a:list[2] let fileRight = a:list[3] tabnew let n = 0 if !empty(glob(l:fileLeft)) silent exec("edit ". l:fileLeft) if line('$') == 1 && getline(".") == "" quit else if expand('%') != l:fileLeft call s:Warn("missing file: ". l:fileLeft) quit else let n += 1 endif endif else call s:Warn("Missing or empty file: ". l:fileLeft) endif if !empty(glob(l:fileCenter)) if l:n != 0 silent exec("vert new") endif silent exec("edit ". l:fileCenter) if line('$') == 1 && getline(".") == "" quit else if expand('%') == l:fileCenter let n += 1 else call s:Warn("missing file: ". l:fileCenter) quit endif endif else call s:Warn("missing file: ". l:fileCenter) endif if !empty(glob(l:fileRight)) if l:n != 0 silent exec("vert new") endif silent exec("edit ". l:fileRight) if line('$') == 1 && getline(".") == "" quit else if expand('%') != l:fileRight call s:Warn("missing file: ". l:fileRight) quit else let n += 1 endif endif else call s:Warn("Missing or empty file: ". l:fileRight) endif if l:n == 0 silent! tabclose elseif l:n > 1 silent exec("windo diffthis") endif endfunction " Merge window layout 4. " Window layout 4: " -------------------------- " | | | | " | Left | Working | Right | " | | | | " -------------------------- " | Current | " -------------------------- function! MergeLayout4(list) let fileNum = len(a:list) if l:fileNum < 4 call s:Error("Merge file list not compleat. Only ". l:fileNum ." files provided.") return endif let fileDown = a:list[0] let fileCenter = a:list[1] let fileLeft = a:list[2] let fileRight = a:list[3] "echom "MergeLayout4: ".a:list[0]. " ".a:list[1]. " ".a:list[2]. " ".a:list[3] tabnew let n = 0 if !empty(glob(l:fileLeft)) silent exec("edit ". l:fileLeft) if line('$') == 1 && getline(".") == "" quit else let fileName = fnamemodify(l:fileLeft, ':t') if expand('%:t') == l:fileName let n += 1 else call s:Warn("Missing file (left): ". l:fileLeft.", found: ".expand('%')) quit endif endif else call s:Warn("Missing or empty file: ". l:fileLeft) endif if !empty(glob(l:fileCenter)) if l:n != 0 silent exec("vert new") endif silent exec("edit ". l:fileCenter) if line('$') == 1 && getline(".") == "" quit else let fileName = fnamemodify(l:fileCenter, ':t') if expand('%:t') == l:fileName let n += 1 else call s:Warn("Missing file (center): ". l:fileCenter.", found: ".expand('%')) quit endif endif else call s:Warn("Missing or empty file: ". l:fileCenter) endif if !empty(glob(l:fileRight)) if l:n != 0 silent exec("vert new") endif silent exec("edit ". l:fileRight) if line('$') == 1 && getline(".") == "" quit else let fileName = fnamemodify(l:fileRight, ':t') if expand('%:t') == l:fileName let n += 1 else call s:Warn("Missing file (right): ". l:fileRight.", found: ".expand('%')) quit endif endif else call s:Warn("Missing or empty file: ". l:fileRight) endif if l:n == 0 silent! tabclose elseif l:n > 1 silent exec("windo diffthis") endif if !empty(glob(l:fileDown)) if l:n != 0 silent exec("new") silent wincmd J silent resize 20 endif silent exec("edit ". l:fileDown) let fileName = fnamemodify(l:fileDown, ':t') if expand('%:t') == l:fileName let n += 1 else call s:Warn("Missing file (down): ".l:fileName.", found: ".expand('%:t')) quit endif else call s:Warn("Missing or empty file (down): ".l:fileDown) endif if l:n == 0 silent! tabclose endif endfunction " Reorder the files related to the merge in the order to be displayed on " screen. " Arg1: list with files related to the merge (file.mine, file.working, " file.rxxxxx, file.left, file.right). " Return: list with the files ordered, first the one to display on the left, " center, right and down. function! s:MergeFilesArrange(list) let l:list = ["", "", "", ""] let revFlag = 0 for file in sort(a:list) call s:LogLevel(2, expand(''), "Check file: ". l:file) if l:file =~ ".working" let l:list[1] = l:file call s:LogLevel(2, expand(''), "working") elseif l:file =~ ".mine" let l:list[1] = l:file call s:LogLevel(2, expand(''), "mine") elseif l:file =~ ".merge.left.r[0-9]" let l:list[2] = l:file call s:LogLevel(2, expand(''), ".merge.left.r") elseif l:file =~ ".merge.right.r[0-9]" let l:list[3] = l:file call s:LogLevel(2, expand(''), ".merge.right.r") elseif l:file =~ "left.r[0-9]" let l:list[2] = l:file call s:LogLevel(2, expand(''), "left.r") elseif l:file =~ "right.r[0-9]" let l:list[3] = l:file call s:LogLevel(0, expand(''), "right.r") elseif l:file =~ ".r[0-9]" if l:revFlag == 1 let l:list[3] = l:file call s:LogLevel(2, expand(''), ".r second") else let l:list[2] = l:file call s:LogLevel(2, expand(''), ".r first") endif let revFlag = 1 else let l:list[0] = l:file call s:LogLevel(0, expand(''), "original") endif endfor call s:LogLevel(1, expand(''), "Original: ". l:list[0]) call s:LogLevel(1, expand(''), "Center: ". l:list[1]) call s:LogLevel(1, expand(''), "Left: ". l:list[2]) call s:LogLevelStop(1, expand(''), "Right: ". l:list[3]) return l:list endfunction " Merge all file conflicts. " Open every file in conflict on a new tab and split vertical showing left, " working and right conflic files. " Arg1: path. " Arg2: [optional] window layout configuration. " Commands: Svnm, Svnmf, Svnmd. function! svnTools#Merge(path, layout) let l:mergeLayout = g:svnTools_mergeLayout if a:layout != "" if a:layout =~ g:svnTools_mergeLayouts let l:mergeLayout = a:layout else call s:Error("Selected layout ". a:layout ." not found on layout list: ". g:svnTools_mergeLayouts) return endif endif echo "Search files with conflicts. In progress..." let list = s:GetStatusFilesList(a:path, "^[C!]") let list += s:GetStatusFilesList(a:path, " C ") "let list = s:GetStatusFilesList(a:path, "^C\|^!*C") let len = len(l:list) if len(l:list) == 0 call s:Warn("No conflicts found") return endif redraw echo "Files in conflict:" for file in l:list echo " ".l:file endfor call input("Open all with merge tool?") redraw for file in l:list if !filereadable(l:file) call s:Warn("File not found: ".l:file) else echo "Open ".l:file." on merge tool. Files: " let list1 = s:GetStatusFilesList(l:file."*", "^[C!?]") let list1 += s:GetStatusFilesList(l:file."*", " C ") let l:listArranged = s:MergeFilesArrange(l:list1) if len(l:listArranged) <= 0 call s:Warn("File not found: ".l:file) endif for file2 in l:listArranged | echo " ".l:file2 | endfor | echo "" exec "call MergeLayout". l:mergeLayout ."(l:listArranged)" endif endfor endfunction " Ask user how to resolve the conflict. " Return: user selected option. function! s:UserDialogResolve() while 1 let l:opt = confirm("Resolve, accept: ", "&base\n&working\n&mine\n&theirs\n&help\n&cancel", 10) if l:opt == 1 return "--accept base" elseif l:opt == 2 return = "--accept working" elseif l:opt == 3 return "--accept theirs-full" elseif l:opt == 4 return "--accept theirs-full" elseif l:opt == 5 echo "" echo "base:" echo " Choose the file that was the BASE revision before you updated your working copy. That is, the file that you checked out before you made your latest edits." echo "working:" echo " Assuming that you've manually handled the conflict resolution, choose the version of the file as it currently stands in your working copy." echo "mine-full:" echo " Resolve all conflicted files with copies of the files as they stood immediately before you ran svn update." echo "theirs-full:" echo " Resolve all conflicted files with copies of the files that were fetched from the server when you ran svn update." echo "" else return "" endif endwhile endfunction " Resolve the conflicts " Commands: Svnres, Svnresf, Svnresd " Arg1: path. " Arg2: [optional] use 'all' to resolve all conflicts. File path to resolve " single file in conflict. "function! svnTools#Resolve(option) function! svnTools#Resolve(path, option) "let l:file = "" if a:option != "all" && a:option != "" call s:Error("Unknown option ". a:option) endif " Perform svn st and extract all files in conflict echo "Search files with conflicts. In progress..." let list = s:GetStatusFilesList(a:path, "^[C!]") let list += s:GetStatusFilesList(a:path, " C ") "let list = s:GetStatusFilesList(a:path, "^C|^!*C") if len(l:list) == 0 call s:Warn("No conflicts found ". a:path) return endif redraw echo "Files in conflict:" "echo l:text for file in l:list echo "C ". l:file endfor call confirm("Open resolve tool?") redraw let l:svnCmd = g:svnTools_svnCmd let l:svnCmd .= s:CheckSvnUserAndPsswd() if a:option == "all" if confirm("ATTENTION! This will resolve all conflicts?", "&yes\n&no", 2) == 2 return endif redraw echo "Resolve all conflicts" let l:cmd = s:UserDialogResolve() if l:cmd == "" | return | endif "echo "base" "echo " Choose the file that was the BASE revision before you updated your working copy. That is, the file that you checked out before you made your latest edits." "echo "working "echo " Assuming that you've manually handled the conflict resolution, choose the version of the file as it currently stands in your working copy." "echo "mine-full" "echo " Resolve all conflicted files with copies of the files as they stood immediately before you ran svn update." "echo "theirs-full" "echo " Resolve all conflicted files with copies of the files that were fetched from the server when you ran svn update." "let l:opt = confirm("Resolve ", "&base\n&working\n&mine\n&theirs\n&cancel", 10) "if l:opt == 1 "let l:cmd = "--accept base" "elseif l:opt == 2 "let l:cmd = "--accept working" "elseif l:opt == 3 "let l:cmd = "--accept theirs-full" "elseif l:opt == 4 "let l:cmd = "--accept theirs-full" "else "return "endif call system(l:svnCmd." resolve -R ". l:cmd) "call system("svn resolve -R ". l:cmd) else " Iterate on all files found with conflict. for file in l:list echo "File: ". l:file let l:cmd = s:UserDialogResolve() if l:cmd == "" | continue | endif "let l:opt = confirm("Resolve ", "&mine\n&theirs\n&cancel", 3) "if l:opt == 1 "let l:cmd = "--accept mine-full" "elseif l:opt == 2 "let l:cmd = "--accept theirs-full" "else "continue "endif call system(l:svnCmd." resolve ". l:cmd ." ". l:file) call system("svn resolve ". l:cmd ." ". l:file) echo "" endfor endif endfunction function! s:SystemCmd(command,callback,async) if !exists("g:loaded_jobs") call s:Error("Plugin jobs.vim not loaded.") return endif let jobName = "svnTools" if g:svnTools_runInBackground == 0 || a:async == 0 let l:async = 0 else let l:async = 1 endif if g:svnTools_userAndPsswd != 1 let cmd = "let @a = \"".a:command."\" \| normal G! \| exec(\"put a\")" call histadd(':', l:cmd) endif call jobs#RunCmd(a:command,a:callback,l:async,l:jobName) endfunction function! s:SystemCmd0(command,callbackList,async) if !exists("g:loaded_jobs") call s:Error("Plugin jobs.vim not loaded.") return endif let jobName = "svnTools" if g:svnTools_runInBackground == 0 || a:async == 0 let l:async = 0 else let l:async = 1 endif " Do not add command with user password from history if g:svnTools_userAndPsswd != 1 let cmd = "let @a = \"".a:command."\" \| normal G! \| exec(\"put a\")" call histadd(':', l:cmd) endif call jobs#RunCmd0(a:command,a:callbackList,l:async,l:jobName) endfunction " Change background/foreground execution of the svn commands. " Arg1: [options]. " - Change current mode when aragument is b/f (f:foreground, b:background). " - Show current mode when aragument is '?'. " - Toogle the background/foregraund execution mode when no argument provided. " Commands: Svnbg function! svnTools#BackgroundMode(options) if a:options =~ "b" || a:options =~ "f" if a:options =~ "f" let g:svnTools_runInBackground = 0 else let g:svnTools_runInBackground = 1 endif elseif a:options == "" if g:svnTools_runInBackground == 1 let g:svnTools_runInBackground = 0 else let g:svnTools_runInBackground = 1 endif endif if g:svnTools_runInBackground == 1 let l:mode = "background" else let l:mode = "foreground" endif echo "[".s:plugin_name."] run commands in ".l:mode."." endfunction "- Help functions ----------------------------------------------------------- function! svnTools#StatusHelp() let text = "[SvnTools.vim] help (v".g:svnTools_version."):\n" let text .= "\n" let text .= "Svn status' symbols help:\n" let text .= "\n" let text .= "- First column: Says if item was added, deleted, or otherwise changed\n" let text .= " ' ' no modifications\n" let text .= " 'A' Added\n" let text .= " 'C' Conflicted\n" let text .= " 'D' Deleted\n" let text .= " 'I' Ignored\n" let text .= " 'M' Modified\n" let text .= " 'R' Replaced\n" let text .= " 'X' an unversioned directory created by an externals definition\n" let text .= " '?' item is not under version control\n" let text .= " '!' item is missing (removed by non-svn command) or incomplete\n" let text .= " '~' versioned item obstructed by some item of a different kind\n" let text .= "\n" let text .= "- Second column: Modifications of a file's or directory's properties\n" let text .= " ' ' no modifications\n" let text .= " 'C' Conflicted\n" let text .= " 'M' Modified\n" let text .= "\n" let text .= "- Third column: Whether the working copy directory is locked\n" let text .= " ' ' not locked\n" let text .= " 'L' locked\n" let text .= "\n" let text .= "- Fourth column: Scheduled commit will contain addition-with-history\n" let text .= " ' ' no history scheduled with commit\n" let text .= " '+' history scheduled with commit\n" let text .= "\n" let text .= " Fifth column: Whether the item is switched or a file external\n" let text .= " ' ' normal\n" let text .= " 'S' the item has a Switched URL relative to the parent\n" let text .= " 'X' a versioned file created by an eXternals definition\n" let text .= "\n" let text .= "- Sixth column: Repository lock token\n" let text .= " (without -u)\n" let text .= " ' ' no lock token\n" let text .= " 'K' lock token present\n" let text .= " (with -u)\n" let text .= " ' ' not locked in repository, no lock token\n" let text .= " 'K' locked in repository, lock toKen present\n" let text .= " 'O' locked in repository, lock token in some Other working copy\n" let text .= " 'T' locked in repository, lock token present but sTolen\n" let text .= " 'B' not locked in repository, lock token present but Broken\n" let text .= "\n" let text .= "- Seventh column: Whether the item is the victim of a tree conflict\n" let text .= " ' ' normal\n" let text .= " 'C' tree-Conflicted\n" call s:WindowSplitMenu(4) call s:WindowSplit() call s:WindowSplitEnd() setl nowrap set buflisted set bufhidden=delete set buftype=nofile setl noswapfile silent put = l:text silent! exec '0file | file svnTools_plugin_svn_status_help' normal ggdd endfunction function! svnTools#MergeLayoutHelp() let text = "[SvnTools.vim] help (v".g:svnTools_version."):\n" let text .= "\n" let text .= "Svn merge's layout help:\n" let text .= "\n" let text .= "Layout 2:\n" let text .= "--------------------------\n" let text .= "| | |\n" let text .= "| Current | Right |\n" let text .= "| | |\n" let text .= "--------------------------\n" let text .= "\n" let text .= "Layout 3:\n" let text .= "--------------------------\n" let text .= "| | | |\n" let text .= "| Left | Current | Right |\n" let text .= "| | | |\n" let text .= "--------------------------\n" let text .= "\n" let text .= "Layout 4:\n" let text .= "--------------------------\n" let text .= "| | | |\n" let text .= "| Left | Working | Right |\n" let text .= "| | | |\n" let text .= "--------------------------\n" let text .= "| Current |\n" let text .= "--------------------------\n" let text .= "\n" let text .= "Default layout: ".g:svnTools_mergeLayout."\n" let text .= "\n" call s:WindowSplitMenu(4) call s:WindowSplit() call s:WindowSplitEnd() setl nowrap set buflisted set bufhidden=delete set buftype=nofile setl noswapfile silent put = l:text silent! exec '0file | file svnTools_plugin_svn_merge_layout_help' normal ggdd endfunction function! svnTools#Help() if g:svnTools_runInBackground == 1 let l:job = "foreground" else let l:job = "background" endif let text = "[svnTools.vim] help (v".g:svnTools_version."):\n" let text .= "\n" let text .= "Abridged command help:\n" let text .= "\n" let text .= "- Info:\n" let text .= " Svni : get current revision info.\n" let text .= " Svnif : get current file revision info.\n" let text .= "\n" let text .= "- Blame:\n" let text .= " Svnbl : get blame of current file.\n" let text .= " Svnblv : get verbose blame of current file.\n" let text .= "\n" let text .= "- Status:\n" let text .= " Svnst : show file's status (conceal symbols: X and ?).\n" let text .= " Svnsta : show status files (show all symbols).\n" let text .= " Svnstf : show current file status.\n" let text .= " Svnstd : show current directory status.\n" let text .= " Svnsth : show the svn status symbols' help.\n" let text .= "\n" let text .= "- Log:\n" let text .= " Svnl [NUM] : get subversion log (num. commits, defaults to ".g:svnTools_lastCommits.").\n" let text .= " Svnls PATTERN [NUM] : log search pattern (num. commits, defaults to ".g:svnTools_lastCommits.").\n" let text .= " Svnlf FILEPATH : show file log.\n" let text .= " Svnlr [NUM] : when placed on a svn log file, get the log and diff of each revison.\n" let text .= " NUM: given a number, only first number of changes will be get.\n" let text .= "\n" let text .= "- Diff:\n" let text .= " Svndf : get diff of changes on current file.\n" let text .= " Svndd : get diff of changes on current directory.\n" let text .= " Svnda : get diff of changes on current workind directory.\n" let text .= " Svndvdr : when placed on a svn log and diff file (after Svnr/Svndd/Svndf/Svnda)\n" let text .= " get each file changes vimdiff.\n" let text .= "\n" let text .= "- Vimdiff:\n" let text .= " Svnvda : show (vimdiff) all files with changes (alows to skip binaries).\n" let text .= " SvnvdA : show (vimdiff) selected files with changes (alows to skip binaries).\n" let text .= " Svnvdf : show (vimdiff) current file changes.\n" let text .= " Svnvdd : show (vimdiff) current directory changes.\n" let text .= " Svnvdr REV : show revision log and open the selected revision changes with vimdiff.\n" let text .= "\n" let text .= "- Directory compare (sandbox compare):\n" let text .= " Svndc DIR1 DIR2 : show (diff) changes between two directories.\n" let text .= " SvndC DIR1 DIR2 : show (diff) changes between two directories, skip binaries..\n" let text .= " Svnvdc DIR1 DIR2 : show (vimdiff) changes between two directories.\n" let text .= " SvnvdC DIR1 DIR2 : show (vimdiff) changes between two directories, skip binaries, files not differing.\n" let text .= "\n" let text .= "- Revision:\n" let text .= " Svnr REV : get diff of selected revision number.\n" let text .= " Svncr REV : cat revision number\n" let text .= "\n" let text .= "- Conflicts:\n" let text .= " Svnm [LAYOUT] : merge all conflicts with vimdiff. Layouts: ".g:svnTools_mergeLayouts." (default layout: ".g:svnTools_mergeLayout.").\n" let text .= " Svnmf [LAYOUT] : merge current file conflict with vimdiff. Layouts: ".g:svnTools_mergeLayouts." (default layout: ".g:svnTools_mergeLayout.").\n" let text .= " Svnmp PATH [LAYOUT] : merge selected path conflicts with vimdiff. Layouts: ".g:svnTools_mergeLayouts." (default layout: ".g:svnTools_mergeLayout.").\n" let text .= " Svnres [all] : perform svn resolve. Use 'all' to resolve all conflicts.\n" let text .= " Svnmh : show merge layout help.\n" let text .= "\n" let text .= "- Tools:\n" let text .= " Svnbg : toogle svn job run on ".l:job."\n" let text .= "\n" call s:WindowSplitMenu(4) call s:WindowSplit() call s:WindowSplitEnd() setl nowrap set buflisted set bufhidden=delete set buftype=nofile setl noswapfile silent put = l:text silent! exec '0file | file svnTools_plugin_help' normal ggdd endfunction "- GUI menu ------------------------------------------------------------ " " Create menu items for the specified modes. function! svnTools#CreateMenus(modes, submenu, target, desc, cmd) " Build up a map command like let plug = a:target let plug_start = 'noremap ' . ' :call SvnTools("' let plug_end = '", "' . a:target . '")' " Build up a menu command like let menuRoot = get(['', 'SvnTools', '&SvnTools', "&Plugin.&SvnTools".a:submenu], 3, '') let menu_command = 'menu ' . l:menuRoot . '.' . escape(a:desc, ' ') if strlen(a:cmd) let menu_command .= '' . a:cmd endif let menu_command .= ' ' . (strlen(a:cmd) ? plug : a:target) call s:LogLevel(1, expand(''), l:menu_command) " Execute the commands built above for each requested mode. for mode in (a:modes == '') ? [''] : split(a:modes, '\zs') if strlen(a:cmd) execute mode . plug_start . mode . plug_end call s:LogLevel(1, expand(''), "execute ". mode . plug_start . mode . plug_end) endif " Check if the user wants the menu to be displayed. if g:svnTools_mode != 0 call s:LogLevel(1, expand(''), "execute " . mode . menu_command) execute mode . menu_command endif endfor endfunction "- Release tools ------------------------------------------------------------ " " Create a vimball release with the plugin files. " Commands: Svnvba function! svnTools#NewVimballRelease() let text = "" let text .= "plugin/svnTools.vim\n" let text .= "autoload/svnTools.vim\n" let text .= "plugin/jobs.vim\n" let text .= "autoload/jobs.vim\n" silent tabedit silent put = l:text silent! exec '0file | file vimball_files' silent normal ggdd let l:plugin_name = substitute(s:plugin_name, ".vim", "", "g") let l:releaseName = l:plugin_name."_".g:svnTools_version.".vmb" let l:workingDir = getcwd() silent cd ~/.vim silent exec "1,$MkVimball! ".l:releaseName." ./" silent exec "vertical new ".l:releaseName silent exec "cd ".l:workingDir "call s:WindowSplitEnd() endfunction "- initializations ------------------------------------------------------------ " let s:plugin = expand('') let s:plugin_path = expand(':p:h') let s:plugin_name = expand(':t') call s:Initialize() plugin/jobs.vim [[[1 131 " Script Name: jobs.vim " Description: run system commands in background. " Tool to be used on other plugins to launch system commands in background. " Includes several commands to check the commands in progress (Jobsl). " Stop all commands in background (Jobska) " Choose wich commands in background to stop (Jobsk) " Showw all commands in background related to current vim window (Jobshw) " Showw all commands history (Jobshy) " " Example: " function! svnTools#Blame() " let file = expand("%") " let name = expand("%:t") " let pos = line('.') " let ext = s:GetSyntax() " " let command = "svn blame -v ".l:file " let callback = ["svnTools#SvnBlameEnd", l:pos, l:ext, l:name] " let l:async = 1 " " call jobs#RunCmd(a:command, a:callback, l:async, "svn") " endfunction " " function! svnTools#SvnBlameEnd(pos,ext,name,resfile) " if exists('a:resfile') && !empty(glob(a:resfile)) " " Process the svn blame file " else " echo "ERROR. Svn blame empty" " endif " endfunction " " Copyright: (C) 2017-2020 Javier Puigdevall " The VIM LICENSE applies to this script; see ':help copyright'. " " Maintainer: Javier Puigdevall " Contributors: " " Dependencies: jpLib.vim (optional) " " NOTES: " " Version: 0.1.1 " Changes: " 0.1.1 Fry, 28 May 21. JPuigdevall " - New: hide passwords, prevent showing command passwords on the command " line, instead replace the password with * characters. Use option: g:jobs_hidePsswd " to dissable it. " 0.1.0 Wed, 15 Jul 20. JPuigdevall " - Change the callback variable to a list conatining the function name and " arguments. " 0.0.1 Fry, 22 Jun 20. JPuigdevall if exists('g:loaded_jobs') finish endif let g:loaded_jobs = 1 let s:save_cpo = &cpo set cpo&vim "- configuration -------------------------------------------------------------- let g:jobs_version = get(g:, 'jobs_version', "0.1.0") let g:jobs_run_in_background = get(g:, 'jobs_run_in_background', 1) let g:jobs_return_to_base_window = get(g:, 'jobs_return_to_base_window', 1) " Do not show the password on the commands, replace with ******* let g:jobs_hidePsswd = get(g:, 'jobs_hidePsswd', 1) if (v:version < 800 || !has("job")) " +job option needed for job_start, job_stop, job_status functions. " Dissable running job in background let g:jobs_run_in_background = 0 endif let g:jobs_mode = get(g:, 'jobs_mode', 3) "- commands ------------------------------------------------------------------- command! -nargs=0 JobsKill call jobs#Stop() command! -nargs=0 Jobsk call jobs#Stop() command! -nargs=0 JobsList call jobs#Status() command! -nargs=0 Jobsl call jobs#Status() command! -nargs=0 JobsHist call jobs#History() command! -nargs=0 Jobshy call jobs#History() command! -nargs=0 JobsListWin call jobs#StatusCurrentWindow() command! -nargs=0 Jobsw call jobs#StatusCurrentWindow() command! -nargs=0 JobsKillAll call jobs#StopAll() command! -nargs=0 Jobska call jobs#StopAll() command! -nargs=0 Jobsh call jobs#Help() command! -nargs=? Jobsv call jobs#Verbose("") command! -nargs=* Jobs call jobs#Menu() "- mappings ------------------------------------------------------------------- " if !hasmapto('Jobsl', 'n') nmap jl :Jobsl endif "- abbreviations ------------------------------------------------------------------- " DEBUG functions: reload plugin cnoreabbrev _jobsrl =jobs#Reload() "- menus ------------------------------------------------------------------- if has("gui_running") call jobs#CreateMenus('cn' , '' , ':Jobsl ' , 'Show all running jobs' , ':Jobsl') call jobs#CreateMenus('cn' , '' , ':Jobsw ' , 'Show all jobs running for current window' , ':Jobsw') call jobs#CreateMenus('cn' , '' , ':Jobshy' , 'Show all jobs history' , ':Jobshy') call jobs#CreateMenus('cn' , '' , ':' , '-Sep-' , '') call jobs#CreateMenus('cn' , '' , ':Jobsk ' , 'Show the running jobs. Choose job to kill' , ':Jobsk') call jobs#CreateMenus('cn' , '' , ':Jobska' , 'Kill all running jobs' , ':Jobska') call jobs#CreateMenus('cn' , '' , ':' , '-Sep2-' , '') call jobs#CreateMenus('cn' , '' , ':Jobsv ' , 'Change verbosity level' , ':Jobsv') endif let &cpo = s:save_cpo unlet s:save_cpo autoload/jobs.vim [[[1 697 " Script Name: jobs.vim "Description: run system commands in background. " " Copyright: (C) 2017-2020 Javier Puigdevall " The VIM LICENSE applies to this script; see ':help copyright'. " " Maintainer: Javier Puigdevall " Contributors: " " Dependencies: jpLib.vim (optional) " " NOTES: " " Version: 0.1.0 " Changes: " - New: Svnlr command, when placed on an svn log file, for each revision " number get its log and diff changes. " 0.1.0 Wed, 15 Jul 20. JPuigdevall " - Change the callback variable to a list conatining the function name and " arguments. " 0.0.1 Fry, 22 Jun 20. JPuigdevall " Get the plugin reload command function! jobs#Reload() let l:pluginPath = substitute(s:plugin_path, "autoload", "plugin", "") let l:autoloadFile = s:plugin_path."/".s:plugin_name let l:pluginFile = l:pluginPath."/".s:plugin_name return "silent! unlet g:loaded_jobs | so ".l:autoloadFile." | so ".l:pluginFile endfunction function! s:Initialize() let s:LogLevel = 0 let s:jobsRunningDict = {} let s:jobsHistoryDict = {} endfunction function! s:Error(mssg) echohl ErrorMsg | echom s:plugin.": ".a:mssg | echohl None endfunction function! s:Warn(mssg) echohl WarningMsg | echom a:mssg | echohl None endfunction " Debug function. Log message function! s:LogLevel(level,func,mssg) if s:LogLevel >= a:level echom "["s:plugin_name." : ".a:func." ] ".a:mssg endif endfunction " Debug function. Log message and wait user key function! s:LogLevelStop(level,func,mssg) if s:LogLevel >= a:level call input("[".s:plugin_name." : ".a:func." ] ".a:mssg." (press key)") endif endfunction func! jobs#Verbose(level) if a:level == "" call s:LogLevel(0, expand(''), "Verbose level: ".s:LogLevel) return endif let s:LogLevel = a:level call s:LogLevel(0, expand(''), "Set verbose level: ".s:LogLevel) endfun function! s:JobHistList(jobsDict) if empty(a:jobsDict) call s:Warn("No jobs history found.") return 1 endif let jobIdList = [] let n = 1 let format = "%' '-3s) %' '-11s %s " let format1 = "%' '-3d) %' '-11s \"%s\" " echo printf(l:format, "Pos", "Time", "Cmd") " for jobList in items(a:jobsDict) let jobId = l:jobList[0] let jobCmd = l:jobList[1][0] let starTime = l:jobList[1][5] let endTime = l:jobList[1][8] let timeList = reltime([l:starTime, l:endTime]) let time = l:timeList[0] if l:time >= 3600 let l:time = l:time / 3600.0 let timeStr = printf("%.1f", l:time)." h" elseif l:time >= 60 let l:time = l:time / 60.0 let timeStr = printf("%.1f", l:time)." m" else let timeStr = printf("%.f", l:time)." s" endif echo printf(l:format1, l:n, l:timeStr, l:jobCmd) let n += 1 endfor return 0 endfunction function! s:JobList(jobsDict,winId) if empty(a:jobsDict) call s:Warn("No jobs found running in background.") return endif let jobIdList = [] let n = 1 let format = "%' '-3s) %' '-8s %' '-6s %' '-11s %s " let format1 = "%' '-3d) %' '-8s %' '-6s %' '-11s \"%s\" " echo printf(l:format, "Pos", "JobId", "Status", "Time", "Cmd") " for jobList in items(a:jobsDict) let jobId = l:jobList[0] let jobCmd = l:jobList[1][0] let winId = l:jobList[1][1] let starTime = l:jobList[1][5] let job = l:jobList[1][7] if a:winId != "" && a:winId != l:winId continue endif let status = job_status(l:job) let timeList = reltime([l:starTime, localtime()]) let time = l:timeList[0] if l:time >= 60 let timeStr = printf("%.1f", l:time / 60.0)." m" else let timeStr = printf("%.f", l:time)." s" endif if l:status == "dead" | echohl ErrorMsg | endif echo printf(l:format1, l:n, l:jobId, l:status, l:timeStr, l:jobCmd) if l:status == "dead" echohl None call s:JobCancel(l:job) else let jobIdList += [ l:jobId ] endif let n += 1 endfor return l:jobIdList endfunction function! s:JobCancel(jobId) if empty(s:jobsRunningDict) call s:Warn("No jobs running in backgraund.") return endif if !has_key(s:jobsRunningDict, a:jobId) return 1 endif let jobCfgList = s:jobsRunningDict[a:jobId] let winId = l:jobCfgList[1] let result = l:jobCfgList[4] let script = l:jobCfgList[5] let name = l:jobCfgList[6] let job = l:jobCfgList[7] " Add to jobs history list let l:jobCfgList += [ localtime() ] " Add end time call extend(s:jobsHistoryDict, { a:jobId : l:jobCfgList }) " Remove from the running jobs list call remove(s:jobsRunningDict, a:jobId) call delete(l:script) call delete(l:result) if exists("w:jobsWinList") let n = index(w:jobsWinList, l:name) if l:n >= 0 call remove(w:jobsWinList, l:n) endif endif if exists("w:jobsTabList") let n = index(w:jobsTabList, l:name) if l:n >= 0 call remove(w:jobsTabList, l:n) endif endif silent! call job_stop(l:job) call s:Warn("Cancel job ".l:job) endfunction " Check if there's a job already running on background for this window " If name is not empty, search only for jors in bg with the " provided name runngin on current window. function! jobs#IsOnWindow(name) if !exists("w:jobsWinList") return 0 endif if a:name == "" && len(w:jobsWinList) > 0 return 1 endif let l:n = index(w:jobsWinList, a:name) call s:LogLevel(1, expand(''), "position:".l:n) if (l:n > 0) return 1 endif return 0 endfunction " Check if job already running in background on thi tab " If name is not empty, search only for jors in bg with the " provided name runngin on current tab. function! jobs#IsOnTab(name) if !exists("w:jobsTabList") return 0 endif if a:name == "" && len(w:jobsTabdDict) > 0 return 1 endif if (index(w:jobsTabList, a:name) >= 0) return 1 endif return 0 endfunction function! s:MountCallbackCall(list, resfile) if len(a:list) == 0 call s:LogLevel(1, expand(''), "Callback: empty") return "" endif let n = 0 for arg in a:list silent! call s:LogLevel(2, expand(''), "arg". l:n .": ". l:arg) if l:n == 0 let l:callback = l:arg ."(" else let l:callback .= "\"". l:arg ."\", " endif let n += 1 silent! call s:LogLevel(2, expand(''), "Callback: ". l:callback) endfor let l:callback .= "\"". a:resfile ."\")" call s:LogLevel(1, expand(''), "Callback: ". l:callback) return l:callback endfunction " Run system command. " Arg1: system command. " Arg2: callback function to process the system results. " Arg3: if true run the system call on backgraund. " Arg4: command name, used to search for commands of the same type already running on background. function! jobs#RunCmd0(command,callbackList,async,name) " Make sure we're running VIM version 8 or higher. if g:jobs_run_in_background == 0 || a:async == 0 echo a:command." (fg)" " Launch command in foreground let l:result = tempname() let command = a:command." > ".l:result echo "This may take a while..." call system(l:command) if len(a:callbackList) != 0 execute "call ". s:MountCallbackCall(a:callbackList,l:result) endif call delete(l:result) else if g:jobs_hidePsswd == 1 " Hide passwords from command line. " Admitted formats: "password MY_PASSWORD", "password=MY_PASSWORD". let l:modifiedJobCmd = substitute(a:command, "password[ =]\\([a-zA-Z0-9\-\.\*\/?,;':\"~!@#$%^&*()_+='|]\\)*", "password ********", "") " DEBUG: Test substitute command on vim's command line: " echo substitute("cmd --password mypass./*,:12 --option1 --option2", "password[ =]\\([a-zA-Z0-9\-\.\*\/?,;':\"~!@#$%^&*()_+='|]\\)*", "password ********", "") " echo substitute("cmd --password=mypass./*,:12 --option1 --option2", "password[ =]\\([a-zA-Z0-9\-\.\*\/?,;':\"~!@#$%^&*()_+='|]\\)*", "password ********", "") " echo substitute("cmd --password mypass./*,:12", "password[ =]\\([a-zA-Z0-9\-\.\*\/?,;':\"~!@#$%^&*()_+='|]\\)*", "password ********", "") " echo substitute("cmd --password=mypass./*,:12", "password[ =]\\([a-zA-Z0-9\-\.\*\/?,;':\"~!@#$%^&*()_+='|]\\)*", "password ********", "") echo l:modifiedJobCmd." (bg)" else echo a:command." (bg)" let l:modifiedJobCmd = a:command endif let result = tempname() let script = tempname() let command = "( ".a:command." ) 2>&1" call system("echo '".l:command."' > ".l:script) call system("chmod 744 ".l:script) call s:LogLevel(1, expand(''), l:script) call s:LogLevel(1, expand(''), system("cat ".l:script)) let l:callback = s:MountCallbackCall(a:callbackList, l:result) " Launch the job. let jobCfgList = [ l:modifiedJobCmd, win_getid(), l:callback, l:result, l:script, localtime(), a:name ] call s:LogLevel(1, expand(''), "Job start: ".l:modifiedJobCmd." Cmd: ".l:script." File: ".l:result) let job = job_start(l:script, {'exit_cb': 'jobs#SystemCmdCallback0', 'out_io': 'file', 'out_name': l:result}) let l:jobCfgList += [ l:job ] let jobId = split(job,' ')[1] call extend(s:jobsRunningDict, { l:jobId : l:jobCfgList }) "sleep 500ms sleep if job_status(l:job) != "dead" && a:name != "" if !exists("w:jobsWinList") let w:jobsWinList = [] endif if !exists("w:jobsTabList") let w:jobsTabList = [] endif let w:jobsWinList += [ a:name ] let w:jobsTabList += [ a:name ] "else "call s:Error("Job ".a:name." failed (".a:command.")") endif endif endfunction " Check the system command status, launch the callback function on finish. function! jobs#SystemCmdCallback0(job,message) call s:LogLevelStop(1, expand(''), "job1:".a:job." mssg:".a:message) if job_status(a:job) ==# "run" "call s:Error("Job ".a:job." failed. ".a:message) return endif let jobId = split(a:job,' ')[1] call s:LogLevelStop(1, expand(''), "jobId1:".l:jobId) if !has_key(s:jobsRunningDict, l:jobId) "call s:Error("Job ".a:job." failed. ".a:message) return 1 endif let jobCfgList = s:jobsRunningDict[l:jobId] let command = l:jobCfgList[0] let winId = l:jobCfgList[1] let callback = l:jobCfgList[2] let result = l:jobCfgList[3] let script = l:jobCfgList[4] let time = l:jobCfgList[5] let jobName = l:jobCfgList[6] let baseWinId = win_getid() if l:winId != "" cal s:LogLevelStop(1, expand(''), "Goto window:".l:winId) if win_gotoid(l:winId) != 1 call s:Error("Can't find the window associated to this job") return 1 endif endif if l:callback != "" cal s:LogLevelStop(1, expand(''), "Launch callback:".l:callback) execute "call ".l:callback endif " Add to jobs history list let l:jobCfgList += [ localtime() ] " Add end time call extend(s:jobsHistoryDict, { l:jobId : l:jobCfgList }) call delete(l:script) call delete(l:result) " Remove from the running jobs list call remove(s:jobsRunningDict, l:jobId) if exists("w:jobsWinList") let n = index(w:jobsWinList, l:jobName) if l:n >= 0 cal s:LogLevel(1, expand(''), "Remove job:".l:n." from win list") call remove(w:jobsWinList, l:n) endif endif if exists("w:jobsTabList") let n = index(w:jobsTabList, l:jobName) if l:n >= 0 cal s:LogLevel(1, expand(''), "Remove job:".l:n." from tab list") call remove(w:jobsTabList, l:n) endif endif return 1 endfunction " Run system command. " Arg1: system command. " Arg2: callback function to process the system results. " Arg3: if true run the system call on backgraund. " Arg4: command name, used to search for commands of the same type already running on background. function! jobs#RunCmd(command,callback,async,name) " Make sure we're running VIM version 8 or higher. if g:jobs_run_in_background == 0 || a:async == 0 echo a:command." (fg)" " Launch command in foreground let l:result = tempname() let command = a:command." > ".l:result echo "This may take a while..." call system(l:command) if a:callback != "" "echomsg "Launch callback:"s:callback."\"".l:result."\")" execute "call ".a:callback."\"".l:result."\")" endif call delete(l:result) else echo a:command." (bg)" let result = tempname() let script = tempname() let command = "( ".a:command." ) 2>&1" call system("echo '".l:command."' > ".l:script) call system("chmod 744 ".l:script) call s:LogLevel(1, expand(''), l:script) call s:LogLevel(1, expand(''), system("cat ".l:script)) " Launch the job. let jobCfgList = [ a:command, win_getid(), a:callback , l:result, l:script, localtime(), a:name ] call s:LogLevel(1, expand(''), "Job start: ".a:command." Cmd: ".l:script." File: ".l:result) let job = job_start(l:script, {'exit_cb': 'jobs#SystemCmdCallback', 'out_io': 'file', 'out_name': l:result}) let l:jobCfgList += [ l:job ] let jobId = split(job,' ')[1] call extend(s:jobsRunningDict, { l:jobId : l:jobCfgList }) "sleep 500ms sleep if job_status(l:job) != "dead" && a:name != "" if !exists("w:jobsWinList") let w:jobsWinList = [] endif if !exists("w:jobsTabList") let w:jobsTabList = [] endif let w:jobsWinList += [ a:name ] let w:jobsTabList += [ a:name ] "else "call s:Error("Job ".a:name." failed (".a:command.")") endif endif endfunction " Check the system command status, launch the callback function on finish. function! jobs#SystemCmdCallback(job,message) call s:LogLevelStop(1, expand(''), "job1:".a:job." mssg:".a:message) if job_status(a:job) ==# "run" "call s:Error("Job ".a:job." failed. ".a:message) return endif let jobId = split(a:job,' ')[1] call s:LogLevelStop(1, expand(''), "jobId1:".l:jobId) if !has_key(s:jobsRunningDict, l:jobId) "call s:Error("Job ".a:job." failed. ".a:message) return 1 endif let jobCfgList = s:jobsRunningDict[l:jobId] let command = l:jobCfgList[0] let winId = l:jobCfgList[1] let callback = l:jobCfgList[2] let result = l:jobCfgList[3] let script = l:jobCfgList[4] let time = l:jobCfgList[5] let jobName = l:jobCfgList[6] let baseWinId = win_getid() if l:winId != "" cal s:LogLevelStop(1, expand(''), "Goto window:".l:winId) if win_gotoid(l:winId) != 1 call s:Error("Can't find the window associated to this job") return 1 endif endif if l:callback != "" cal s:LogLevelStop(1, expand(''), "Launch callback:".l:callback."\"".l:result."\")") execute "call ".l:callback."\"".l:result."\")" endif " Add to jobs history list let l:jobCfgList += [ localtime() ] " Add end time call extend(s:jobsHistoryDict, { l:jobId : l:jobCfgList }) call delete(l:script) call delete(l:result) " Remove from the running jobs list call remove(s:jobsRunningDict, l:jobId) if exists("w:jobsWinList") let n = index(w:jobsWinList, l:jobName) if l:n >= 0 cal s:LogLevel(1, expand(''), "Remove job:".l:n." from win list") call remove(w:jobsWinList, l:n) endif endif if exists("w:jobsTabList") let n = index(w:jobsTabList, l:jobName) if l:n >= 0 cal s:LogLevel(1, expand(''), "Remove job:".l:n." from tab list") call remove(w:jobsTabList, l:n) endif endif return 1 endfunction " Show list with all jobs running in background. " Stop the selected job. function! jobs#Stop() if empty(s:jobsRunningDict) call s:Warn("No jobs running in backgraund.") return endif let jobIdList = s:JobList(s:jobsRunningDict,"") let pos = input("Remove position: ") echo " " for n in split(l:pos) if l:n != "" && l:n <= len(l:jobIdList) let n -= 1 call s:JobCancel(l:jobIdList[l:n]) endif endfor return endfunction " Show list with all jobs running in background. function! jobs#Status() if empty(s:jobsRunningDict) call s:Warn("No jobs running in backgraund.") return endif let jobIdList = s:JobList(s:jobsRunningDict,"") call input("") endfunction " Show list with all jobs running in background on currnet window function! jobs#StatusCurrentWindow() if empty(s:jobsRunningDict) call s:Warn("No jobs running in backgraund.") return endif let jobIdList = s:JobList(s:jobsRunningDict,win_getid()) call input("") endfunction " Show job history function! jobs#History() if empty(s:jobsHistoryDict) call s:Warn("No jobs history found.") return endif let jobIdList = s:JobHistList(s:jobsHistoryDict) call input("") endfunction " Stop all jobs running in background function! jobs#StopAll() let n = 0 if !empty(s:jobsRunningDict) for jobList in items(s:jobsRunningDict) call s:JobCancel(l:jobList[0]) let n += 1 endfor endif let s:jobsRunningDict = {} let w:jobsWinList = [] echo "Jobs stopped:".l:n endfunction function! jobs#Help() echo "jobs.vim" echo " " echo " Jobsl : show all jobs running." echo " Jobsw : show all jobs running on current window." echo " Jobshy : show jobs history." echo " Jobsk : show all jobs running, kill selected one." echo " Jobska : kill all running jobs." echo " Jobsv : change verbosity level (0 is default)." echo " " call input("(Press key)") endfunction function! jobs#Menu(...) " Check jpLib.vim plugin installed if empty(glob(s:plugin_path."/jpLib.vim")) call s:Error("missing plugin jpLib.vim (".s:plugin_path."/jpLib.vim".")") call input("") endif let l:selection = "" if a:0 >= 1 let l:selection = a:1 endif let l:options = [] let l:options += [ [ "#jobs.vim commands:" , "" , "" ] ] let l:options += [ [ "Show all jobs running (Jobsl)" , "Jobsl " , "" ] ] let l:options += [ [ "Show all jobs running on current window (Jobsw)", "Jobsw " , "" ] ] let l:options += [ [ "Show jobs history (Jobshy)" , "Jobshy ", "" ] ] let l:options += [ [ "Show all jobs running kill selected one (Jobsk)", "Jobsk " , "" ] ] let l:options += [ [ "Kill all running jobs (Jobska)" , "Jobska ", "" ] ] let l:options += [ [ "Change verbosity level. 0 is default (Jobsv)" , "Jobsv" , "" ] ] call jpLib#OptionsMenu(l:options, l:selection) endfunction " Create menu items for the specified modes. function! jobs#CreateMenus(modes, submenu, target, desc, cmd) "let s:LogLevel = 4 " Build up a map command like let plug = a:target let plug_start = 'noremap ' . ' :call JobsMenu("' let plug_end = '", "' . a:target . '")' " Build up a menu command like let menuRoot = get(['', 'JobsMenu', '&JobsUtils', "&Plugin.&JobsUtils".a:submenu], 3, '') let menu_command = 'menu ' . l:menuRoot . '.' . escape(a:desc, ' ') if strlen(a:cmd) let menu_command .= '' . a:cmd endif let menu_command .= ' ' . (strlen(a:cmd) ? plug : a:target) "let menu_command .= ' ' . (strlen(a:cmd) ? a:target) call s:LogLevel(1, expand(''), "menu_command :".l:menu_command) " Execute the commands built above for each requested mode. for mode in (a:modes == '') ? [''] : split(a:modes, '\zs') if strlen(a:cmd) execute mode . plug_start . mode . plug_end call s:LogLevel(1, expand(''), "execute ". mode . plug_start . mode . plug_end) endif " Check if the user wants the menu to be displayed. if g:jobs_mode != 0 call s:LogLevel(1, expand(''), "execute " . mode . menu_command) execute mode . menu_command endif endfor "let s:LogLevel = 0 endfunction "- initializations ------------------------------------------------------------ let s:plugin = expand('') let s:plugin_path = expand(':p:h') let s:plugin_name = expand(':t') if !exists("s:initialized") call s:Initialize() let s:initialized = 1 endif