diff options
Diffstat (limited to '.config/nvim/plugged/nerdtree-git-plugin/nerdtree_plugin')
-rw-r--r-- | .config/nvim/plugged/nerdtree-git-plugin/nerdtree_plugin/git_status.vim | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/.config/nvim/plugged/nerdtree-git-plugin/nerdtree_plugin/git_status.vim b/.config/nvim/plugged/nerdtree-git-plugin/nerdtree_plugin/git_status.vim new file mode 100644 index 0000000..af7eb55 --- /dev/null +++ b/.config/nvim/plugged/nerdtree-git-plugin/nerdtree_plugin/git_status.vim @@ -0,0 +1,360 @@ +" ============================================================================ +" File: git_status.vim +" Description: plugin for NERD Tree that provides git status support +" Maintainer: Xuyuan Pang <xuyuanp at gmail dot com> +" Last Change: 4 Apr 2014 +" License: This program is free software. It comes without any warranty, +" to the extent permitted by applicable law. You can redistribute +" it and/or modify it under the terms of the Do What The Fuck You +" Want To Public License, Version 2, as published by Sam Hocevar. +" See http://sam.zoy.org/wtfpl/COPYING for more details. +" ============================================================================ +if exists('g:loaded_nerdtree_git_status') + finish +endif +let g:loaded_nerdtree_git_status = 1 + +if !exists('g:NERDTreeShowGitStatus') + let g:NERDTreeShowGitStatus = 1 +endif + +if g:NERDTreeShowGitStatus == 0 + finish +endif + +if !exists('g:NERDTreeMapNextHunk') + let g:NERDTreeMapNextHunk = ']c' +endif + +if !exists('g:NERDTreeMapPrevHunk') + let g:NERDTreeMapPrevHunk = '[c' +endif + +if !exists('g:NERDTreeUpdateOnWrite') + let g:NERDTreeUpdateOnWrite = 1 +endif + +if !exists('g:NERDTreeUpdateOnCursorHold') + let g:NERDTreeUpdateOnCursorHold = 1 +endif + +if !exists('g:NERDTreeShowIgnoredStatus') + let g:NERDTreeShowIgnoredStatus = 0 +endif + +if !exists('s:NERDTreeIndicatorMap') + let s:NERDTreeIndicatorMap = { + \ 'Modified' : '✹', + \ 'Staged' : '✚', + \ 'Untracked' : '✭', + \ 'Renamed' : '➜', + \ 'Unmerged' : '═', + \ 'Deleted' : '✖', + \ 'Dirty' : '✗', + \ 'Clean' : '✔︎', + \ 'Ignored' : '☒', + \ 'Unknown' : '?' + \ } +endif + + +function! NERDTreeGitStatusRefreshListener(event) + if !exists('b:NOT_A_GIT_REPOSITORY') + call g:NERDTreeGitStatusRefresh() + endif + let l:path = a:event.subject + let l:flag = g:NERDTreeGetGitStatusPrefix(l:path) + call l:path.flagSet.clearFlags('git') + if l:flag !=# '' + call l:path.flagSet.addFlag('git', l:flag) + endif +endfunction + +" FUNCTION: g:NERDTreeGitStatusRefresh() {{{2 +" refresh cached git status +function! g:NERDTreeGitStatusRefresh() + let b:NERDTreeCachedGitFileStatus = {} + let b:NERDTreeCachedGitDirtyDir = {} + let b:NOT_A_GIT_REPOSITORY = 1 + + let l:root = fnamemodify(b:NERDTree.root.path.str(), ':p:gs?\\?/?:S') + let l:gitcmd = 'git -c color.status=false -C ' . l:root . ' status -s' + if g:NERDTreeShowIgnoredStatus + let l:gitcmd = l:gitcmd . ' --ignored' + endif + if exists('g:NERDTreeGitStatusIgnoreSubmodules') + let l:gitcmd = l:gitcmd . ' --ignore-submodules' + if g:NERDTreeGitStatusIgnoreSubmodules ==# 'all' || g:NERDTreeGitStatusIgnoreSubmodules ==# 'dirty' || g:NERDTreeGitStatusIgnoreSubmodules ==# 'untracked' + let l:gitcmd = l:gitcmd . '=' . g:NERDTreeGitStatusIgnoreSubmodules + endif + endif + let l:statusesStr = system(l:gitcmd) + let l:statusesSplit = split(l:statusesStr, '\n') + if l:statusesSplit != [] && l:statusesSplit[0] =~# 'fatal:.*' + let l:statusesSplit = [] + return + endif + let b:NOT_A_GIT_REPOSITORY = 0 + + for l:statusLine in l:statusesSplit + " cache git status of files + let l:pathStr = substitute(l:statusLine, '...', '', '') + let l:pathSplit = split(l:pathStr, ' -> ') + if len(l:pathSplit) == 2 + call s:NERDTreeCacheDirtyDir(l:pathSplit[0]) + let l:pathStr = l:pathSplit[1] + else + let l:pathStr = l:pathSplit[0] + endif + let l:pathStr = s:NERDTreeTrimDoubleQuotes(l:pathStr) + if l:pathStr =~# '\.\./.*' + continue + endif + let l:statusKey = s:NERDTreeGetFileGitStatusKey(l:statusLine[0], l:statusLine[1]) + let b:NERDTreeCachedGitFileStatus[fnameescape(l:pathStr)] = l:statusKey + + if l:statusKey == 'Ignored' + if isdirectory(l:pathStr) + let b:NERDTreeCachedGitDirtyDir[fnameescape(l:pathStr)] = l:statusKey + endif + else + call s:NERDTreeCacheDirtyDir(l:pathStr) + endif + endfor +endfunction + +function! s:NERDTreeCacheDirtyDir(pathStr) + " cache dirty dir + let l:dirtyPath = s:NERDTreeTrimDoubleQuotes(a:pathStr) + if l:dirtyPath =~# '\.\./.*' + return + endif + let l:dirtyPath = substitute(l:dirtyPath, '/[^/]*$', '/', '') + while l:dirtyPath =~# '.\+/.*' && has_key(b:NERDTreeCachedGitDirtyDir, fnameescape(l:dirtyPath)) == 0 + let b:NERDTreeCachedGitDirtyDir[fnameescape(l:dirtyPath)] = 'Dirty' + let l:dirtyPath = substitute(l:dirtyPath, '/[^/]*/$', '/', '') + endwhile +endfunction + +function! s:NERDTreeTrimDoubleQuotes(pathStr) + let l:toReturn = substitute(a:pathStr, '^"', '', '') + let l:toReturn = substitute(l:toReturn, '"$', '', '') + return l:toReturn +endfunction + +" FUNCTION: g:NERDTreeGetGitStatusPrefix(path) {{{2 +" return the indicator of the path +" Args: path +let s:GitStatusCacheTimeExpiry = 2 +let s:GitStatusCacheTime = 0 +function! g:NERDTreeGetGitStatusPrefix(path) + if localtime() - s:GitStatusCacheTime > s:GitStatusCacheTimeExpiry + let s:GitStatusCacheTime = localtime() + call g:NERDTreeGitStatusRefresh() + endif + let l:pathStr = a:path.str() + let l:cwd = b:NERDTree.root.path.str() . a:path.Slash() + if nerdtree#runningWindows() + let l:pathStr = a:path.WinToUnixPath(l:pathStr) + let l:cwd = a:path.WinToUnixPath(l:cwd) + endif + let l:cwd = substitute(l:cwd, '\~', '\\~', 'g') + let l:pathStr = substitute(l:pathStr, l:cwd, '', '') + let l:statusKey = '' + if a:path.isDirectory + let l:statusKey = get(b:NERDTreeCachedGitDirtyDir, fnameescape(l:pathStr . '/'), '') + else + let l:statusKey = get(b:NERDTreeCachedGitFileStatus, fnameescape(l:pathStr), '') + endif + return s:NERDTreeGetIndicator(l:statusKey) +endfunction + +" FUNCTION: s:NERDTreeGetCWDGitStatus() {{{2 +" return the indicator of cwd +function! g:NERDTreeGetCWDGitStatus() + if b:NOT_A_GIT_REPOSITORY + return '' + elseif b:NERDTreeCachedGitDirtyDir == {} && b:NERDTreeCachedGitFileStatus == {} + return s:NERDTreeGetIndicator('Clean') + endif + return s:NERDTreeGetIndicator('Dirty') +endfunction + +function! s:NERDTreeGetIndicator(statusKey) + if exists('g:NERDTreeIndicatorMapCustom') + let l:indicator = get(g:NERDTreeIndicatorMapCustom, a:statusKey, '') + if l:indicator !=# '' + return l:indicator + endif + endif + let l:indicator = get(s:NERDTreeIndicatorMap, a:statusKey, '') + if l:indicator !=# '' + return l:indicator + endif + return '' +endfunction + +function! s:NERDTreeGetFileGitStatusKey(us, them) + if a:us ==# '?' && a:them ==# '?' + return 'Untracked' + elseif a:us ==# ' ' && a:them ==# 'M' + return 'Modified' + elseif a:us =~# '[MAC]' + return 'Staged' + elseif a:us ==# 'R' + return 'Renamed' + elseif a:us ==# 'U' || a:them ==# 'U' || a:us ==# 'A' && a:them ==# 'A' || a:us ==# 'D' && a:them ==# 'D' + return 'Unmerged' + elseif a:them ==# 'D' + return 'Deleted' + elseif a:us ==# '!' + return 'Ignored' + else + return 'Unknown' + endif +endfunction + +" FUNCTION: s:jumpToNextHunk(node) {{{2 +function! s:jumpToNextHunk(node) + let l:position = search('\[[^{RO}].*\]', '') + if l:position + call nerdtree#echo('Jump to next hunk ') + endif +endfunction + +" FUNCTION: s:jumpToPrevHunk(node) {{{2 +function! s:jumpToPrevHunk(node) + let l:position = search('\[[^{RO}].*\]', 'b') + if l:position + call nerdtree#echo('Jump to prev hunk ') + endif +endfunction + +" Function: s:SID() {{{2 +function s:SID() + if !exists('s:sid') + let s:sid = matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze_SID$') + endif + return s:sid +endfun + +" FUNCTION: s:NERDTreeGitStatusKeyMapping {{{2 +function! s:NERDTreeGitStatusKeyMapping() + let l:s = '<SNR>' . s:SID() . '_' + + call NERDTreeAddKeyMap({ + \ 'key': g:NERDTreeMapNextHunk, + \ 'scope': 'Node', + \ 'callback': l:s.'jumpToNextHunk', + \ 'quickhelpText': 'Jump to next git hunk' }) + + call NERDTreeAddKeyMap({ + \ 'key': g:NERDTreeMapPrevHunk, + \ 'scope': 'Node', + \ 'callback': l:s.'jumpToPrevHunk', + \ 'quickhelpText': 'Jump to prev git hunk' }) + +endfunction + +augroup nerdtreegitplugin + autocmd CursorHold * silent! call s:CursorHoldUpdate() +augroup END +" FUNCTION: s:CursorHoldUpdate() {{{2 +function! s:CursorHoldUpdate() + if g:NERDTreeUpdateOnCursorHold != 1 + return + endif + + if !g:NERDTree.IsOpen() + return + endif + + " Do not update when a special buffer is selected + if !empty(&l:buftype) + return + endif + + let l:winnr = winnr() + let l:altwinnr = winnr('#') + + call g:NERDTree.CursorToTreeWin() + call b:NERDTree.root.refreshFlags() + call NERDTreeRender() + + exec l:altwinnr . 'wincmd w' + exec l:winnr . 'wincmd w' +endfunction + +augroup nerdtreegitplugin + autocmd BufWritePost * call s:FileUpdate(expand('%:p')) +augroup END +" FUNCTION: s:FileUpdate(fname) {{{2 +function! s:FileUpdate(fname) + if g:NERDTreeUpdateOnWrite != 1 + return + endif + + if !g:NERDTree.IsOpen() + return + endif + + let l:winnr = winnr() + let l:altwinnr = winnr('#') + + call g:NERDTree.CursorToTreeWin() + let l:node = b:NERDTree.root.findNode(g:NERDTreePath.New(a:fname)) + if l:node == {} + return + endif + call l:node.refreshFlags() + let l:node = l:node.parent + while !empty(l:node) + call l:node.refreshDirFlags() + let l:node = l:node.parent + endwhile + + call NERDTreeRender() + + exec l:altwinnr . 'wincmd w' + exec l:winnr . 'wincmd w' +endfunction + +augroup AddHighlighting + autocmd FileType nerdtree call s:AddHighlighting() +augroup END +function! s:AddHighlighting() + let l:synmap = { + \ 'NERDTreeGitStatusModified' : s:NERDTreeGetIndicator('Modified'), + \ 'NERDTreeGitStatusStaged' : s:NERDTreeGetIndicator('Staged'), + \ 'NERDTreeGitStatusUntracked' : s:NERDTreeGetIndicator('Untracked'), + \ 'NERDTreeGitStatusRenamed' : s:NERDTreeGetIndicator('Renamed'), + \ 'NERDTreeGitStatusIgnored' : s:NERDTreeGetIndicator('Ignored'), + \ 'NERDTreeGitStatusDirDirty' : s:NERDTreeGetIndicator('Dirty'), + \ 'NERDTreeGitStatusDirClean' : s:NERDTreeGetIndicator('Clean') + \ } + + for l:name in keys(l:synmap) + exec 'syn match ' . l:name . ' #' . escape(l:synmap[l:name], '~') . '# containedin=NERDTreeFlags' + endfor + + hi def link NERDTreeGitStatusModified Special + hi def link NERDTreeGitStatusStaged Function + hi def link NERDTreeGitStatusRenamed Title + hi def link NERDTreeGitStatusUnmerged Label + hi def link NERDTreeGitStatusUntracked Comment + hi def link NERDTreeGitStatusDirDirty Tag + hi def link NERDTreeGitStatusDirClean DiffAdd + " TODO: use diff color + hi def link NERDTreeGitStatusIgnored DiffAdd +endfunction + +function! s:SetupListeners() + call g:NERDTreePathNotifier.AddListener('init', 'NERDTreeGitStatusRefreshListener') + call g:NERDTreePathNotifier.AddListener('refresh', 'NERDTreeGitStatusRefreshListener') + call g:NERDTreePathNotifier.AddListener('refreshFlags', 'NERDTreeGitStatusRefreshListener') +endfunction + +if g:NERDTreeShowGitStatus && executable('git') + call s:NERDTreeGitStatusKeyMapping() + call s:SetupListeners() +endif |