From 30ba91ea743ae646c52e50d6a540c64862e0ea10 Mon Sep 17 00:00:00 2001 From: Iain Patterson Date: Mon, 27 Jan 2014 13:46:47 +0000 Subject: [PATCH] gitv plugin. --- .vim/doc/gitv.txt | 391 +++++++++++++++++++++++ .vim/ftplugin/gitv.vim | 48 +++ .vim/plugin/gitv.vim | 689 +++++++++++++++++++++++++++++++++++++++++ .vim/syntax/gitv.vim | 89 ++++++ 4 files changed, 1217 insertions(+) create mode 100644 .vim/doc/gitv.txt create mode 100644 .vim/ftplugin/gitv.vim create mode 100644 .vim/plugin/gitv.vim create mode 100644 .vim/syntax/gitv.vim diff --git a/.vim/doc/gitv.txt b/.vim/doc/gitv.txt new file mode 100644 index 0000000..262ca34 --- /dev/null +++ b/.vim/doc/gitv.txt @@ -0,0 +1,391 @@ +gitv -- gitk for vim. + +AUTHOR: Greg Sexton *gitv-author* +WEBSITE: http://www.gregsexton.org/portfolio/gitv/ +LICENSE: Same terms as Vim itself (see :help license). +NOTES: Much of the credit for gitv goes to Tim Pope and the fugitive plugin + where this plugin either uses functionality directly or was inspired + heavily. + +gitv *gitv* + +1. Introduction |gitv-introduction| +2. Installation |gitv-installation| +3. Usage |gitv-usage| +4. Configuration Options |gitv-config-options| +5. Changelog |gitv-changelog| +6. Misc |gitv-misc| + +============================================================================== +1. Introduction *gitv-introduction* + +|gitv| is a 'gitk clone' plugin for the text editor Vim. The goal is to give +you a similar set of functionality as a repository viewer. Using this plugin +you can view a repository's history including branching and merging, you can +see which commits refs point to. You can quickly and easily view what changed +to which files and when. You can perform arbitrary diffs (using Vim's +excellent built in diff functionality) and you can easily check out whole +commits and branches or just individual files if need be. + +Throw in the fact that it is running in Vim and you get for free: the ability +to move over repository history quickly and precisely using Vim's built in +movement operators. You get excellent code syntax highlighting due to Vim's +built in ability. You can open up all sorts of repository views in multiple +windows and position them exactly how you like. You can take advantage of +Vim's registers to copy multiple fragments of code from previous commits. The +list goes on. + +This plugin is an extension of the |fugitive| plugin. + +I hope you like it! + +============================================================================== +2. Installation *gitv-installation* + +Install in ~/.vim, or in ~\vimfiles if you're on Windows. This plugin should +be fully pathogen compatible if you want to install it this way. + +|gitv| was developed against Vim 7.3 but earlier versions of Vim should work. +Vim 7.2+ is recommended as it ships with syntax highlighting for many Git file +types. You will also need the |fugitive| plugin installed and working for +|gitv| to work. + +============================================================================== +3. Usage *gitv-usage* + +|gitv| defines the following command. + +:Gitv[!] [args] Invoking this command on a buffer that belongs to a git + repository causes the gitv browser to open. '!' causes gitv + to open in file mode rather than browser mode. Any [args] + supplied are passed on to the gitv viewer and can be used to + narrow the commits that are shown. If this command is run + on a buffer not belonging to a git repository a message + stating 'Not a git repository.' is displayed. + +The following abbreviation is also defined so that you can type :gitv without +capitalisation. +> + cabbrev gitv Gitv +< + +3.1 Browser mode + +|gitv| has two distinct modes. Browser mode and file mode. The browser mode is +opened in a new tab and allows the repository history to be viewed for all +files. This is activated by running :Gitv without a '!'. + +In this mode you can view the entire repository history and see which files +were changed with each commit. This mode tries to closely resemble gitk. + +3.2 File mode + +File mode is opened in a |preview-window| above the buffer you are currently in. +This view is tailored to act on the buffer that :Gitv! was run from, the +"focused" file. Whilst in this mode all actions performed are specific to the +single focused file. The browser only shows commits where the focused file +changed. Selecting a commit, views the focused file as it was in that commit. +Performing a check out only checks the focused file out as it was in the +commit. And so on. See 3.4 for the differences. + +3.3 Arguments + +You can pass arguments to the :Gitv command. These allow you to filter and +narrow the commits shown. Essentially, gitv is a glorified 'git log' wrapper +and so any flag that 'git log' accepts so will gitv. gitv does not inspect the +arguments passed on to 'git log' and so may not work if they don't make sense. + +Without any arguments gitv behaves just like gitk and git log without +arguments. It will display the commit history for the currently checked out +branch. + +Here are some particularly useful examples of arguments that could be +passed to :Gitv. For more info see 'git help log' and in particular the +section: "Commit Limiting". + + Flag Description ~ + + --all View repository history for all refs. + + .. Show only commits between the named two commits. When + either or is omitted, it defaults to + HEAD, i.e. the tip of the current branch. For a more + complete list of ways to spell and , + see gitrevisions(7). + + --merges View only merge commits. + + -S Look for differences that introduce or remove an + instance of . Note that this is different than + the string simply appearing in diff output; see the + pickaxe entry in gitdiffcore(7) for more details. + + -G Look for differences whose added or removed line + matches the given . + +3.4 Key mappings. + +This is a list of key mappings that will work only in the gitv browser window. +Where appropriate the differences in action is described for the two modes. + + Mode Map Description~ + + normal Opens a commit. In browser mode this will show the + commit header information including the commit + message. It will also dispaly a full diff showing all the + changes to files. + + Tip: if you press on anything sensible you can + view the expected output. For example pressing on + the line beginning a file diff, it will display the + diff using Vim's built in diff viewing capability. + Pressing on the tree sha will list all the files + in the commit and pressing on one of these will + show that file as it was in the commit. And so on. + + Pressing on the line "-- Load More --" will load + |g:Gitv_CommitStep| more commits. + + In file mode this will open the focused file as it was + in the currently selected diff. This allows you to + easily go back in time and look at the focused file. + + Pressing on the top line in file mode opens the + current working copy of the focused file. + + normal o The same as but opens in a new |split|. + normal O The same as but opens in a new |tab|. + normal s The same as but opens in a new |vsplit|. + + normal This performs the same thing as in browser mode. + In file mode it opens the commit details rather than + the focused file. + + normal q Quits gitv. In browser mode this will close the entire + tab. In file mode this closes only the |preview-window|. + Note: in browser mode this will close the tab + regardless of any windows you may have opened as well + as the gitv windows. + + normal u Forces an update of the content of the browser window. + + normal co Performs a 'git checkout' of the commit the cursor is + on. In both modes this will present you with a choice + of whether you would like to checkout the sha or any + ref that might point to this commit. + + File mode differs in that it doesn't check out the + entire commit but just the focused file in that + commit. + + Tip: in gVim this will present you with a pop up dialog. + You can make this a text choice by performing ':set + guioptions+=c.' + + normal D Performs a diff using Vim's built in diff viewing + capabilities. This does nothing in browser mode. In + file mode it will diff the current file with the + focused file in the commit under the cursor. + + visual D In visual mode this performs a diff against the file + in the commit at the top of the selection against the + file in the commit at the bottom of the selection. The + newest file is always on the right. + + normal S This works for both browser and file mode. It opens a + diffstat of everything that has changed since the + commit under the cursor. + + visual S In visual mode this works in exactly the same way as + normal mode. However, it only shows what has changed in + the range of commits that are highlighted. + +Here is a list of extra key mappings that can be used to efficiently move +around a repository history in the browser window. + + Mode Map Description~ + + normal x Jumps the cursor forward to the next branching point + in the history. + + normal X Jumps the cursor backward to the previous branching + point in the history. + + normal r Jumps the cursor forward to the next ref in the + history. + + normal R Jumps the cursor backward to the previous ref in the + history. + + normal P Jumps the cursor to the commit referenced by HEAD. + +3.5 Commands + +Running the |:Git| command in the commit browser window, in either mode, will +cause the |:Git| command to be run as expected but the commit history will +automatically update to reflect any changes too. + +3.6 Windows + +In browser mode, two windows are opened initially. The "browser window" that +displays the commit history and the "preview window" that shows the currently +selected commit. + +In file mode, a |preview-window| is opened above the current file. This is a +browser window filtered to show commits only affecting the focused file. The +window holding the focused file acts as the preview window in this mode. + +NOTE: In both modes the buffer in the preview window is wiped after replacing +it. This is to stop the quick build up of fugitive buffers. A buffer will not +be wiped if it contains unsaved changes. Buffers are not wiped when opening a +commit in a split, vsplit or a new tab. + +When opening a commit, the window it will be opened in is determined by simple +rules. If in browser mode and the layout is vertical it will open in the +window to the immediate right (exactly as if you performed l). If in a +horizontal layout, it will be opened in the window immediately below (exactly +as if you performed j). If in file mode, it will open exactly like +browser mode split horizontally. NOTE: It is for this reason that you should +not move the browser window as it will cause commits to be opened in +unexpected places. + +3.7 Folding + +Folding of branches is supported in the browser window. Initially all folds +are always open and will open on a reload. You can collapse any branch by +using Vim's built in fold operators. See |folding| for more details. + +============================================================================== +4. Configuration Options *gitv-config-options* + +You can set the following options in your .vimrc to override the values used +by |gitv|. The defaults are shown. + +4.1 Commit Step + +This is the number of commits to show each time. When pressing on +"-- Load More --", the number of extra commits loaded is g:Gitv_CommitStep. +The default is a screen's worth of lines. This should be set to an integer +number. Setting this to a value _really_ high will load the entire repo in one +go. +> + g:Gitv_CommitStep = &lines +< + +4.2 Open Horizontal + +This is the default layout to use in browser mode. If set to 0 then browser +mode will open in a vertical split. If set to 1 then browser mode will open in +a horizontal split. If set to 'auto' then browser mode will open in a vertical +split unless the content fits better in a horizontal split, in which case it +will open horizontally. + +The commit browser width and height is automatically sized to best fit the +content in all modes and settings. +> + g:Gitv_OpenHorizontal = 0 +< + +4.3 Git Executable + +This is the name of the git executable to use to run commands. This should be +a string. +> + g:Gitv_GitExecutable = 'git' +< +4.4 Wipe All on Close + +This option should be set to either 0 (to disable) or 1 (to enable). If set to +1 then on closing the browser mode by using the q key all buffers displayed in +a window in the tab will be wiped before the tab is closed. This option allows +you to limit the number of fugitive buffers that accumulate in the use of gitv. + +NOTE: This will not wipe any buffer with unsaved content. It will however +mercilessly wipe all buffers in the tab regardless of the file they hold. +> + g:Gitv_WipeAllOnClose = 0 +< + +4.5 Wrap Lines + +If set to 1 then line wrapping is enabled. This is useful if you have +occasional very long commit messages. +> + g:Gitv_WrapLines = 0 +< + +4.6 Truncate Commit Subjects + +If set to 1 then commit subject truncation is enabled. This will truncate +commit subjects, where necessary, so that the whole line will fit in one +screen width. If this is set, then automatically switching to a horizontal +layout will no longer work as commits will be truncated to always fit in a +vertical split. NOTE: It is possible that this can truncate any refs pointing +at a commit. In this situation it will not be possible to check out any of +these refs. This is due to gitv being unable to recognise that they are refs. +> + g:Gitv_TruncateCommitSubjects = 0 +< +============================================================================== +5. Changelog *gitv-changelog* + +1.0 First release. I hope you enjoy gitv! + +============================================================================== +6. Misc *gitv-misc* + +6.1 Tips and tricks + +I use the following mappings to make working with |gitv| easier. +> + nmap gv :Gitv --all + nmap gV :Gitv! --all +< + +The following abbreviation makes running arbitrary git commands much easier. +> + cabbrev git Git +< + +The function: 'Gitv_OpenGitCommand(command, windowCmd)' is provided to allow +the more advanced user to create their own commands. This function will +execute the git command provided in the new window created using windowCmd. + +By using this function you get for free: the buffer set up for read only git +output, including syntax highlighting and many other tailored options. You +also get mappings for 'u' to update the output and 'q' to easily close the +window. + +Here is an example of getting diff output both cached and not, in a vertical +and horizontal split respectively. +> + call Gitv_OpenGitCommand("diff --no-color --cached", 'vnew') + call Gitv_OpenGitCommand("diff --no-color", 'new') +< + +I like my diff colors to be green for added lines and red for removed lines, +just like in the shell. Adding this to your vimrc will accomplish this. +> + highlight diffAdded guifg=#00bf00 + highlight diffRemoved guifg=#bf0000 +< + +I highly recommend adding to your vimrc: +> + set lazyredraw +< + +This stops Vim from redrawing the screen during complex operations and results +in much smoother looking plugins. + + +6.2 Bugs, issues, features and contributing. + +There are no known bugs. Hopefully there are not too many unknown. Please see +below to help. + +Bugs, suggestions, pull requests and patches are all very welcome. If you find +issues with |gitv| please add them to the issues page on the github project. +Anything else, feel free to email me: gregsexton@gmail.com. + + vim:tw=78:ts=8:ft=help:norl: diff --git a/.vim/ftplugin/gitv.vim b/.vim/ftplugin/gitv.vim new file mode 100644 index 0000000..90fa45c --- /dev/null +++ b/.vim/ftplugin/gitv.vim @@ -0,0 +1,48 @@ +"AUTHOR: Greg Sexton +"WEBSITE: http://www.gregsexton.org/portfolio/gitv/ +"LICENSE: Same terms as Vim itself (see :help license). +"NOTES: Much of the credit for gitv goes to Tim Pope and the fugitive plugin +" where this plugin either uses functionality directly or was inspired heavily. + +"enabling these next lines breaks settings when reloading the buffer +"if exists("b:did_ftplugin") | finish | endif +"let b:did_ftplugin = 1 + +let s:cpo_save = &cpo +set cpo&vim + +setlocal fdm=expr + +fu! Foldlevelforbranch() "{{{ + let line = getline(v:lnum) + + if line == "-- Load More --" + return 0 + endif + if line =~ "^-- \\[.*\\] --$" + return 0 + endif + + let line = substitute(line, "\\s", "", "g") + let level = match(line, "*") + 1 + return level == 0 ? -1 : level +endfu "}}} +setlocal foldexpr=Foldlevelforbranch() + +fu! BranchFoldText() "{{{ + "get first non-blank line + let fs = v:foldstart + while getline(fs) =~ '^\s*$' | let fs = nextnonblank(fs + 1) + endwhile + if fs > v:foldend + let line = getline(v:foldstart) + else + let line = getline(fs) + endif + return line +endf "}}} +setlocal foldtext=BranchFoldText() +setlocal foldlevel=99 + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/.vim/plugin/gitv.vim b/.vim/plugin/gitv.vim new file mode 100644 index 0000000..6ef6ff0 --- /dev/null +++ b/.vim/plugin/gitv.vim @@ -0,0 +1,689 @@ +"AUTHOR: Greg Sexton +"WEBSITE: http://www.gregsexton.org/portfolio/gitv/ +"LICENSE: Same terms as Vim itself (see :help license). +"NOTES: Much of the credit for gitv goes to Tim Pope and the fugitive plugin +" where this plugin either uses functionality directly or was inspired heavily. + +if exists("g:loaded_gitv") || v:version < 700 + finish +endif +let g:loaded_gitv = 1 + +let s:savecpo = &cpo +set cpo&vim + +"configurable options: +"g:Gitv_CommitStep - int +"g:Gitv_OpenHorizontal - {0,1,'AUTO'} +"g:Gitv_GitExecutable - string +"g:Gitv_WipeAllOnClose - int +"g:Gitv_WrapLines - {0,1} +"g:Gitv_TruncateCommitSubjects - {0,1} + +if !exists("g:Gitv_CommitStep") + let g:Gitv_CommitStep = &lines +endif + +if !exists('g:Gitv_GitExecutable') + let g:Gitv_GitExecutable = 'git' +endif + +if !exists('g:Gitv_WipeAllOnClose') + let g:Gitv_WipeAllOnClose = 0 "default for safety +endif + +if !exists('g:Gitv_WrapLines') + let g:Gitv_WrapLines = 0 +endif + +if !exists('g:Gitv_TruncateCommitSubjects') + let g:Gitv_TruncateCommitSubjects = 0 +endif + +"this counts up each time gitv is opened to ensure a unique file name +let g:Gitv_InstanceCounter = 0 + +let s:localUncommitedMsg = '* Local uncommitted changes, not checked in to index.' +let s:localCommitedMsg = '* Local changes checked in to index but not committed.' + +command! -nargs=* -bang Gitv call s:OpenGitv(shellescape(), 0) +cabbrev gitv Gitv + +"Public API:"{{{ +fu! Gitv_OpenGitCommand(command, windowCmd, ...) "{{{ + "returns 1 if command succeeded with output + "optional arg is a flag, if present runs command verbatim + + "this function is not limited to script scope as is useful for running other commands. + "e.g call Gitv_OpenGitCommand("diff --no-color", 'vnew') is useful for getting an overall git diff. + + let [result, finalCmd] = s:RunGitCommand(a:command, a:0) + + if type(result) == type(0) + return 0 + endif + if type(result) == type("") && result == "" + echom "No output." + return 0 + else + if a:windowCmd == '' + silent setlocal modifiable + silent setlocal noreadonly + 1,$ d + else + let goBackTo = winnr() + let dir = s:GetRepoDir() + let workingDir = fnamemodify(dir,':h') + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let bufferDir = getcwd() + let tempSplitBelow = &splitbelow + let tempSplitRight = &splitright + try + set nosplitbelow + set nosplitright + execute cd.'`=workingDir`' + exec a:windowCmd + let newWindow = winnr() + finally + exec goBackTo . 'wincmd w' + execute cd.'`=bufferDir`' + if exists('newWindow') + exec newWindow . 'wincmd w' + endif + exec 'set '. (tempSplitBelow ? '' : 'no') . 'splitbelow' + exec 'set '. (tempSplitRight ? '' : 'no') . 'splitright' + endtry + endif + if !(&modifiable) + return 0 + endif + let b:Git_Command = finalCmd + silent setlocal ft=git + silent setlocal buftype=nofile + silent setlocal nobuflisted + silent setlocal noswapfile + silent setlocal bufhidden=wipe + silent setlocal nonumber + if g:Gitv_WrapLines + silent setlocal wrap + else + silent setlocal nowrap + endif + silent setlocal fdm=syntax + silent setlocal foldlevel=0 + nmap q :q! + nmap u :if exists('b:Git_Command')call Gitv_OpenGitCommand(b:Git_Command, '', 1)endif + call append(0, split(result, '\n')) "system converts eols to \n regardless of os. + silent setlocal nomodifiable + silent setlocal readonly + 1 + return 1 + endif +endf "}}} }}} +"General Git Functions: "{{{ +fu! s:RunGitCommand(command, verbatim) "{{{ + "if verbatim returns result of system command, else + "switches to the buffer repository before running the command and switches back after. + if !a:verbatim + "switches to the buffer repository before running the command and switches back after. + let dir = s:GetRepoDir() + let workingDir = fnamemodify(dir,':h') + if workingDir == '' + return 0 + endif + + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let bufferDir = getcwd() + try + execute cd.'`=workingDir`' + let finalCmd = g:Gitv_GitExecutable.' --git-dir="' .dir. '" ' . a:command + let result = system(finalCmd) + finally + execute cd.'`=bufferDir`' + endtry + else + let result = system(a:command) + let finalCmd = a:command + endif + return [result, finalCmd] +endfu "}}} +fu! s:GetRepoDir() "{{{ + let dir = fugitive#buffer().repo().dir() + if dir == '' + echom "No git repository could be found." + endif + return dir +endfu "}}} }}} +"Open And Update Gitv:"{{{ +fu! s:OpenGitv(extraArgs, fileMode) "{{{ + let sanatizedArgs = a:extraArgs == "''" ? '' : a:extraArgs + let sanatizedArgs = sanatizedArgs == '""' ? '' : sanatizedArgs + let g:Gitv_InstanceCounter += 1 + if !s:IsCompatible() "this outputs specific errors + return + endif + try + if a:fileMode + call s:OpenFileMode(sanatizedArgs) + else + call s:OpenBrowserMode(sanatizedArgs) + endif + catch /not a git repository/ + echom 'Not a git repository.' + return + endtry +endf "}}} +fu! s:IsCompatible() "{{{ + if !exists('g:loaded_fugitive') + echoerr "gitv requires the fugitive plugin to be installed." + endif + return exists('g:loaded_fugitive') +endfu "}}} +fu! s:OpenBrowserMode(extraArgs) "{{{ + "this throws an exception if not a git repo which is caught immediately + let fubuffer = fugitive#buffer() + silent Gtabedit HEAD + + if s:IsHorizontal() + let direction = 'new gitv'.'-'.g:Gitv_InstanceCounter + else + let direction = 'vnew gitv'.'-'.g:Gitv_InstanceCounter + endif + if !s:LoadGitv(direction, 0, g:Gitv_CommitStep, a:extraArgs, '') + return 0 + endif + call s:SetupBufferCommands(0) + "open the first commit + silent call s:OpenGitvCommit("Gedit", 0) +endf "}}} +fu! s:OpenFileMode(extraArgs) "{{{ + let relPath = fugitive#buffer().path() + pclose! + if !s:LoadGitv(&previewheight . "new gitv".'-'.g:Gitv_InstanceCounter, 0, g:Gitv_CommitStep, a:extraArgs, relPath) + return 0 + endif + set previewwindow + set winfixheight + let b:Gitv_FileMode = 1 + let b:Gitv_FileModeRelPath = relPath + call s:SetupBufferCommands(1) +endf "}}} +fu! s:LoadGitv(direction, reload, commitCount, extraArgs, filePath) "{{{ + if a:reload + let jumpTo = line('.') "this is for repositioning the cursor after reload + endif + + if !s:ConstructAndExecuteCmd(a:direction, a:reload, a:commitCount, a:extraArgs, a:filePath) + return 0 + endif + call s:SetupBuffer(a:commitCount, a:extraArgs, a:filePath) + exec exists('jumpTo') ? jumpTo : '1' + call s:SetupMappings() "redefines some of the mappings made by Gitv_OpenGitCommand + call s:ResizeWindow(a:filePath!='') + + echom "Loaded up to " . a:commitCount . " commits." + return 1 +endf "}}} +fu! s:ConstructAndExecuteCmd(direction, reload, commitCount, extraArgs, filePath) "{{{ + if a:reload "run the same command again with any extra args + if exists('b:Git_Command') + "substitute in the potentially new commit count taking account of a potential filePath + let newcmd = b:Git_Command + if a:filePath != '' + let newcmd = substitute(newcmd, " -- " . a:filePath . "$", "", "") + endif + let newcmd = substitute(newcmd, " -\\d\\+$", " -" . a:commitCount, "") + if a:filePath != '' + let newcmd .= ' -- ' . a:filePath + endif + silent let res = Gitv_OpenGitCommand(newcmd, a:direction, 1) + return res + endif + else + let cmd = "log " . a:extraArgs + let cmd .= " --no-color --decorate --pretty=format:\"%d %s__SEP__%ar__SEP__%an__SEP__[%h]\" --graph -" + let cmd .= a:commitCount + if a:filePath != '' + let cmd .= ' -- ' . a:filePath + endif + silent let res = Gitv_OpenGitCommand(cmd, a:direction) + return res + endif + return 0 +endf "}}} +fu! s:SetupBuffer(commitCount, extraArgs, filePath) "{{{ + silent set filetype=gitv + let b:Gitv_CommitCount = a:commitCount + let b:Gitv_ExtraArgs = a:extraArgs + silent setlocal modifiable + silent setlocal noreadonly + silent %s/refs\/tags\//t:/ge + silent %s/refs\/remotes\//r:/ge + silent %s/refs\/heads\///ge + silent %call s:Align("__SEP__", a:filePath) + silent %s/\s\+$//e + call s:AddLoadMore() + call s:AddLocalNodes(a:filePath) + if a:filePath != '' + call append(0, '-- ['.a:filePath.'] --') + endif + silent setlocal nomodifiable + silent setlocal readonly + silent setlocal cursorline +endf "}}} +fu! s:AddLocalNodes(filePath) "{{{ + let suffix = a:filePath == '' ? '' : ' -- '.a:filePath + let gitCmd = "diff --no-color --cached" . suffix + let [result, cmd] = s:RunGitCommand(gitCmd, 0) + if result != "" + call append(0, s:localCommitedMsg) + endif + let gitCmd = "diff --no-color" . suffix + let [result, cmd] = s:RunGitCommand(gitCmd, 0) + if result != "" + call append(0, s:localUncommitedMsg) + endif +endfu "}}} +fu! s:AddLoadMore() "{{{ + call append(line('$'), '-- Load More --') +endfu "}}} +fu! s:SetupMappings() "{{{ + "operations + nmap :call OpenGitvCommit("Gedit", 0) + nmap o :call OpenGitvCommit("Gsplit", 0) + nmap O :call OpenGitvCommit("Gtabedit", 0) + nmap s :call OpenGitvCommit("Gvsplit", 0) + "force opening the fugitive buffer for the commit + nmap :call OpenGitvCommit("Gedit", 1) + + nmap q :call CloseGitv() + nmap u :call LoadGitv('', 1, b:Gitv_CommitCount, b:Gitv_ExtraArgs, GetRelativeFilePath()) + nmap co :call CheckOutGitvCommit() + + nmap D :call DiffGitvCommit() + vmap D :call DiffGitvCommit() + + nmap S :call StatGitvCommit() + vmap S :call StatGitvCommit() + + "movement + nmap x :call JumpToBranch(0) + nmap X :call JumpToBranch(1) + nmap r :call JumpToRef(0) + nmap R :call JumpToRef(1) + nmap P :call JumpToHead() +endf "}}} +fu! s:SetupBufferCommands(fileMode) "{{{ + silent command! -buffer -nargs=* -complete=customlist,s:fugitive_GitComplete Git call MoveIntoPreviewAndExecute("Git ",1)|normal u +endfu "}}} +fu! s:ResizeWindow(fileMode) "{{{ + if a:fileMode "window height determined by &previewheight + return + endif + if !s:IsHorizontal() + "size window based on longest line + let longest = max(map(range(1, line('$')), "virtcol([v:val, '$'])")) + if longest > &columns/2 + "potentially auto change to horizontal + if s:AutoHorizontal() + "switching to horizontal + let b:Gitv_AutoHorizontal=1 + wincmd K + call s:ResizeWindow(a:fileMode) + return + else + let longest = &columns/2 + endif + endif + exec "vertical resize " . longest + else + "size window based on num lines + call s:ResizeHorizontal() + endif +endf "}}} }}} +"Utilities:"{{{ +fu! s:GetGitvSha(lineNumber) "{{{ + let l = getline(a:lineNumber) + let sha = matchstr(l, "\\[\\zs[0-9a-f]\\{7}\\ze\\]$") + return sha +endf "}}} +fu! s:GetGitvRefs() "{{{ + let l = getline('.') + let refstr = matchstr(l, "^\\(\\(|\\|\\/\\|\\\\\\|\\*\\)\\s\\?\\)*\\s\\+(\\zs.\\{-}\\ze)") + let refs = split(refstr, ', ') + return refs +endf "}}} +fu! s:RecordBufferExecAndWipe(cmd, wipe) "{{{ + "this should be used to replace the buffer in a window + let buf = bufnr('%') + exec a:cmd + if a:wipe + "safe guard against wiping out buffer you're in + if bufnr('%') != buf && bufexists(buf) + exec 'bwipeout ' . buf + endif + endif +endfu "}}} +fu! s:MoveIntoPreviewAndExecute(cmd, tryToOpenNewWin) "{{{ + if winnr("$") == 1 "is the only window + call s:AttemptToCreateAPreviewWindow(a:tryToOpenNewWin, a:cmd) + return + endif + let horiz = s:IsHorizontal() + let filem = s:IsFileMode() + let currentWin = winnr() + + if horiz || filem + wincmd j + else + wincmd l + endif + + if currentWin == winnr() "haven't moved anywhere + call s:AttemptToCreateAPreviewWindow(a:tryToOpenNewWin, a:cmd) + return + endif + + silent exec a:cmd + if horiz || filem + wincmd k + else + wincmd h + endif +endfu "}}} +fu! s:AttemptToCreateAPreviewWindow(shouldAttempt, cmd) "{{{ + if a:shouldAttempt + call s:CreateNewPreviewWindow() + call s:MoveIntoPreviewAndExecute(a:cmd, 0) + else + echoerr "No preview window detected." + endif +endfu "}}} +fu! s:CreateNewPreviewWindow() "{{{ + "this should not be called by anything other than AttemptToCreateAPreviewWindow + let horiz = s:IsHorizontal() + let filem = s:IsFileMode() + if horiz || filem + Gsplit HEAD + else + Gvsplit HEAD + endif + wincmd x +endfu "}}} +fu! s:IsHorizontal() "{{{ + "NOTE: this can only tell you if horizontal while cursor in browser window + let horizGlobal = exists('g:Gitv_OpenHorizontal') && g:Gitv_OpenHorizontal == 1 + let horizBuffer = exists('b:Gitv_AutoHorizontal') && b:Gitv_AutoHorizontal == 1 + return horizGlobal || horizBuffer +endf "}}} +fu! s:AutoHorizontal() "{{{ + return exists('g:Gitv_OpenHorizontal') && + \ type(g:Gitv_OpenHorizontal) == type("") && + \ g:Gitv_OpenHorizontal ==? 'auto' +endf "}}} +fu! s:IsFileMode() "{{{ + return exists('b:Gitv_FileMode') && b:Gitv_FileMode == 1 +endf "}}} +fu! s:ResizeHorizontal() "{{{ + let lines = line('$') + if lines > (&lines/2)-2 + let lines = (&lines/2)-2 + endif + exec "resize " . lines +endf "}}} +fu! s:GetRelativeFilePath() "{{{ + return exists('b:Gitv_FileModeRelPath') ? b:Gitv_FileModeRelPath : '' +endf "}}} +fu! s:OpenRelativeFilePath(sha, geditForm) "{{{ + let relPath = s:GetRelativeFilePath() + if relPath == '' + return + endif + let cmd = a:geditForm . " " . a:sha . ":" . relPath + let cmd = 'call s:RecordBufferExecAndWipe("'.cmd.'", '.(a:geditForm=='Gedit').')' + call s:MoveIntoPreviewAndExecute(cmd, 1) +endf "}}} }}} +"Mapped Functions:"{{{ +"Operations: "{{{ +fu! s:OpenGitvCommit(geditForm, forceOpenFugitive) "{{{ + if getline('.') == "-- Load More --" + call s:LoadGitv('', 1, b:Gitv_CommitCount+g:Gitv_CommitStep, b:Gitv_ExtraArgs, s:GetRelativeFilePath()) + return + endif + if s:IsFileMode() && getline('.') =~ "^-- \\[.*\\] --$" + call s:OpenWorkingCopy(a:geditForm) + return + endif + if getline('.') == s:localUncommitedMsg + call s:OpenWorkingDiff(a:geditForm, 0) + return + endif + if getline('.') == s:localCommitedMsg + call s:OpenWorkingDiff(a:geditForm, 1) + return + endif + let sha = s:GetGitvSha(line('.')) + if sha == "" + return + endif + if s:IsFileMode() && !a:forceOpenFugitive + call s:OpenRelativeFilePath(sha, a:geditForm) + else + let cmd = a:geditForm . " " . sha + let cmd = 'call s:RecordBufferExecAndWipe("'.cmd.'", '.(a:geditForm=='Gedit').')' + call s:MoveIntoPreviewAndExecute(cmd, 1) + endif +endf +fu! s:OpenWorkingCopy(geditForm) + let fp = s:GetRelativeFilePath() + let form = a:geditForm[1:] "strip off the leading 'G' + let cmd = form . " " . fugitive#buffer().repo().tree() . "/" . fp + let cmd = 'call s:RecordBufferExecAndWipe("'.cmd.'", '.(form=='edit').')' + call s:MoveIntoPreviewAndExecute(cmd, 1) +endfu +fu! s:OpenWorkingDiff(geditForm, staged) + let winCmd = a:geditForm[1:] == 'edit' ? '' : a:geditForm[1:] + if s:IsFileMode() + let fp = s:GetRelativeFilePath() + let suffix = ' -- '.fp + let g:Gitv_InstanceCounter += 1 + let winCmd = 'new gitv'.'-'.g:Gitv_InstanceCounter + else + let suffix = '' + endif + if a:staged + let cmd = 'call Gitv_OpenGitCommand(\"diff --no-color --cached'.suffix.'\", \"'.winCmd.'\")' + else + let cmd = 'call Gitv_OpenGitCommand(\"diff --no-color'.suffix.'\", \"'.winCmd.'\")' + endif + let cmd = 'call s:RecordBufferExecAndWipe("'.cmd.'", '.(winCmd=='').')' + call s:MoveIntoPreviewAndExecute(cmd, 1) +endfu "}}} +fu! s:CheckOutGitvCommit() "{{{ + let allrefs = s:GetGitvRefs() + let sha = s:GetGitvSha(line('.')) + if sha == "" + return + endif + let refs = allrefs + [sha] + let refstr = join(refs, "\n") + let choice = confirm("Checkout commit:", refstr . "\nCancel") + if choice == 0 + return + endif + let choice = get(refs, choice-1, "") + if choice == "" + return + endif + let choice = substitute(choice, "^t:", "", "") + let choice = substitute(choice, "^r:", "", "") + if s:IsFileMode() + let relPath = s:GetRelativeFilePath() + let choice .= " -- " . relPath + endif + exec "Git checkout " . choice +endf "}}} +fu! s:CloseGitv() "{{{ + if s:IsFileMode() + q + else + if g:Gitv_WipeAllOnClose + silent windo setlocal bufhidden=wipe + endif + let moveLeft = tabpagenr() == tabpagenr('$') ? 0 : 1 + tabc + if moveLeft && tabpagenr() != 1 + tabp + endif + endif +endf "}}} +fu! s:DiffGitvCommit() range "{{{ + if !s:IsFileMode() + echom "Diffing is not possible in browser mode." + return + endif + let shafirst = s:GetGitvSha(a:firstline) + let shalast = s:GetGitvSha(a:lastline) + if shafirst == "" || shalast == "" + return + endif + if a:firstline != a:lastline + call s:OpenRelativeFilePath(shafirst, "Gedit") + endif + call s:MoveIntoPreviewAndExecute("Gdiff " . shalast, a:firstline != a:lastline) +endf "}}} +fu! s:StatGitvCommit() range "{{{ + let shafirst = s:GetGitvSha(a:firstline) + let shalast = s:GetGitvSha(a:lastline) + if shafirst == "" || shalast == "" + return + endif + let cmd = 'diff '.shafirst + if shafirst != shalast + let cmd .= ' '.shalast + endif + let cmd .= ' --stat' + let cmd = "call s:SetupStatBuffer('".cmd."')" + if s:IsFileMode() + exec cmd + else + call s:MoveIntoPreviewAndExecute(cmd, 1) + endif +endf +fu! s:SetupStatBuffer(cmd) + silent let res = Gitv_OpenGitCommand(a:cmd, s:IsFileMode()?'vnew':'') + if res + silent set filetype=gitv + endif +endfu "}}} }}} +"Movement: "{{{ +fu! s:JumpToBranch(backward) "{{{ + if a:backward + silent! ?|/\||\\?-1 + else + silent! /|\\\||\//+1 + endif +endf "}}} +fu! s:JumpToRef(backward) "{{{ + if a:backward + silent! ?^\(\(|\|\/\|\\\|\*\)\s\=\)\+\s\+\zs( + else + silent! /^\(\(|\|\/\|\\\|\*\)\s\?\)\+\s\+\zs(/ + endif +endf "}}} +fu! s:JumpToHead() "{{{ + silent! /^\(\(|\|\/\|\\\|\*\)\s\?\)\+\s\+\zs(HEAD/ +endf "}}} +"}}} }}} +"Align And Truncate Functions: "{{{ +fu! s:Align(seperator, filePath) range "{{{ + let lines = getline(a:firstline, a:lastline) + call map(lines, 'split(v:val, a:seperator)') + + let newlines = copy(lines) + call filter(newlines, 'len(v:val)>1') + let maxLens = s:MaxLengths(newlines) + + let newlines = [] + for tokens in lines + if len(tokens)>1 + let newline = [] + for i in range(len(tokens)) + let token = tokens[i] + call add(newline, token . repeat(' ', maxLens[i]-strlen(token)+1)) + endfor + call add(newlines, newline) + else + call add(newlines, tokens) + endif + endfor + + if g:Gitv_TruncateCommitSubjects + call s:TruncateLines(newlines, a:filePath) + endif + + call map(newlines, "join(v:val)") + call setline(a:firstline, newlines) +endfu "}}} +fu! s:TruncateLines(lines, filePath) "{{{ + "truncates the commit subject for any line > &columns + call map(a:lines, "s:TruncateHelp(v:val, a:filePath)") +endfu "}}} +fu! s:TruncateHelp(line, filePath) "{{{ + let length = strlen(join(a:line)) + let maxWidth = s:IsHorizontal() ? &columns : &columns/2 + let maxWidth = a:filePath != '' ? winwidth(0) : maxWidth + if length > maxWidth + let delta = length - maxWidth + "offset = 3 for the elipsis and 1 for truncation + let offset = 3 + 1 + if a:line[0][-(delta + offset + 1):] =~ "^\\s\\+$" + let extension = " " + else + let extension = "..." + endif + let a:line[0] = a:line[0][:-(delta + offset)] . extension + endif + return a:line +endfu "}}} +fu! s:MaxLengths(colls) "{{{ + "precondition: coll is a list of lists of strings -- should be rectangular + "returns a list of maximum string lengths + let lengths = [] + for x in a:colls + for y in range(len(x)) + let length = strlen(x[y]) + if length > get(lengths, y, 0) + if len(lengths)-1 < y + call add(lengths, length) + else + let lengths[y] = length + endif + endif + endfor + endfor + return lengths +endfu "}}} }}} +"Fugitive Functions: "{{{ +"These functions are lifted directly from fugitive and modified only to work with gitv. +function! s:fugitive_sub(str,pat,rep) abort "{{{ + return substitute(a:str,'\v\C'.a:pat,a:rep,'') +endfunction "}}} +function! s:fugitive_GitComplete(A,L,P) abort "{{{ + if !exists('s:exec_path') + let s:exec_path = s:fugitive_sub(system(g:fugitive_git_executable.' --exec-path'),'\n$','') + endif + let cmds = map(split(glob(s:exec_path.'/git-*'),"\n"),'s:fugitive_sub(v:val[strlen(s:exec_path)+5 : -1],"\\.exe$","")') + if a:L =~ ' [[:alnum:]-]\+ ' + return fugitive#buffer().repo().superglob(a:A) + elseif a:A == '' + return cmds + else + return filter(cmds,'v:val[0 : strlen(a:A)-1] ==# a:A') + endif +endfunction "}}} }}} + +let &cpo = s:savecpo +unlet s:savecpo + + " vim:fdm=marker diff --git a/.vim/syntax/gitv.vim b/.vim/syntax/gitv.vim new file mode 100644 index 0000000..e238549 --- /dev/null +++ b/.vim/syntax/gitv.vim @@ -0,0 +1,89 @@ +" Vim syntax file +" Language: Custom git log output +" Maintainer: Greg Sexton +" Last Change: 2011-04-08 +" + +if exists("b:current_syntax") + finish +endif + +"set conceallevel=2 +"set concealcursor=n + +syn match gitvSubject /.*/ + +syn match gitvDate /\(\d\+ years\?, \)\?\d\+ \%(second\|seconds\|minute\|minutes\|hour\|hours\|day\|days\|week\|weeks\|month\|months\|year\) ago/ contained containedin=gitvSubject +syn match gitvHash /\[[0-9a-f]\{7}\]$/ contained containedin=gitvSubject + +syn match gitvGraphEdge9 /\(|\|\/\|\\\|\*\)\s\?/ nextgroup=gitvRef,gitvSubject skipwhite +syn match gitvGraphEdge8 /\(|\|\/\|\\\|\*\)\s\?/ nextgroup=gitvGraphEdge9,gitvRef,gitvSubject skipwhite +syn match gitvGraphEdge7 /\(|\|\/\|\\\|\*\)\s\?/ nextgroup=gitvGraphEdge8,gitvRef,gitvSubject skipwhite +syn match gitvGraphEdge6 /\(|\|\/\|\\\|\*\)\s\?/ nextgroup=gitvGraphEdge7,gitvRef,gitvSubject skipwhite +syn match gitvGraphEdge5 /\(|\|\/\|\\\|\*\)\s\?/ nextgroup=gitvGraphEdge6,gitvRef,gitvSubject skipwhite +syn match gitvGraphEdge4 /\(|\|\/\|\\\|\*\)\s\?/ nextgroup=gitvGraphEdge5,gitvRef,gitvSubject skipwhite +syn match gitvGraphEdge3 /\(|\|\/\|\\\|\*\)\s\?/ nextgroup=gitvGraphEdge4,gitvRef,gitvSubject skipwhite +syn match gitvGraphEdge2 /\(|\|\/\|\\\|\*\)\s\?/ nextgroup=gitvGraphEdge3,gitvRef,gitvSubject skipwhite +syn match gitvGraphEdge1 /\(|\|\/\|\\\|\*\)\s\?/ nextgroup=gitvGraphEdge2,gitvRef,gitvSubject skipwhite +syn match gitvGraphEdge0 /^\(\(|\|\/\|\\\|\*\)\s\?\)/ nextgroup=gitvGraphEdge1,gitvRef,gitvSubject skipwhite + +syn match gitvRef /\s*(.\{-})/ nextgroup=gitvSubject skipwhite +syn match gitvRefTag /t:\zs.\{-}\ze\(, \|)\)/ contained containedin=gitvRef +syn match gitvRefRemote /r:\zs.\{-}\ze\(, \|)\)/ contained containedin=gitvRef + +syn match gitvLoadMore /^-- Load More --$/ +syn match gitvWorkingCopy /^-- \[.*\] --$/ contained containedin=gitvSubject +syn match gitvLocalUncommit /^\* Local uncommitted changes, not checked in to index\.$/ +syn match gitvLocalCommited /^\* Local changes checked in to index but not committed\.$/ +syn match gitvLocalCommitedNode /\*/ contained containedin=gitvLocalCommited +syn match gitvLocalUncommitNode /\*/ contained containedin=gitvLocalUncommit + +syn match gitvAddedMarks /|\s\+\d\+ \zs+*-*\ze$/ contained containedin=gitvSubject +syn match gitvAddedMarks /|\s\+Bin \zs\d\+ -> \d\+\ze bytes$/ contained containedin=gitvSubject +syn match gitvRemovedMarks /-*$/ contained containedin=gitvAddedMarks +syn match gitvRemovedMarks /\d\+\ze ->/ contained containedin=gitvAddedMarks +syn match gitvSeperatorMarks /\s\+->\s\+/ contained containedin=gitvAddedMarks + +hi def link gitvHash Number +hi def link gitvRef Directory +hi def link gitvRefTag String +hi def link gitvRefRemote Statement +hi def link gitvDate Statement +hi def link gitvSubject Normal +hi def link gitvLoadMore Question +hi def link gitvWorkingCopy Question + +hi def link gitvAddedMarks diffAdded +hi def link gitvRemovedMarks diffRemoved +hi def link gitvSeperatorMarks Normal + +hi def link gitvGraphEdge0 Delimiter + +if &background == "dark" + highlight default gitvGraphEdge1 ctermfg=magenta guifg=green1 + highlight default gitvGraphEdge2 ctermfg=green guifg=yellow1 + highlight default gitvGraphEdge3 ctermfg=yellow guifg=orange1 + highlight default gitvGraphEdge4 ctermfg=cyan guifg=greenyellow + highlight default gitvGraphEdge5 ctermfg=red guifg=springgreen1 + highlight default gitvGraphEdge6 ctermfg=yellow guifg=cyan1 + highlight default gitvGraphEdge7 ctermfg=green guifg=slateblue1 + highlight default gitvGraphEdge8 ctermfg=cyan guifg=magenta1 + highlight default gitvGraphEdge9 ctermfg=magenta guifg=purple1 +else + highlight default gitvGraphEdge1 ctermfg=darkyellow guifg=orangered3 + highlight default gitvGraphEdge2 ctermfg=darkgreen guifg=orange2 + highlight default gitvGraphEdge3 ctermfg=blue guifg=yellow3 + highlight default gitvGraphEdge4 ctermfg=darkmagenta guifg=olivedrab4 + highlight default gitvGraphEdge5 ctermfg=red guifg=green4 + highlight default gitvGraphEdge6 ctermfg=darkyellow guifg=paleturquoise3 + highlight default gitvGraphEdge7 ctermfg=darkgreen guifg=deepskyblue4 + highlight default gitvGraphEdge8 ctermfg=blue guifg=darkslateblue + highlight default gitvGraphEdge9 ctermfg=darkmagenta guifg=darkviolet +endif + +highlight default gitvLocalCommitedNode ctermfg=green guifg=green +highlight default gitvLocalUncommitNode ctermfg=red guifg=red +highlight default gitvLocalCommited gui=bold +highlight default gitvLocalUncommit gui=bold + +let b:current_syntax = "gitv" -- 2.20.1