Initial checkin
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
" ============================================================================
|
||||
" File: autoload/gitstatus.vim
|
||||
" Description: library for indicators
|
||||
" Maintainer: Xuyuan Pang <xuyuanp at gmail dot com>
|
||||
" 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_autoload')
|
||||
finish
|
||||
endif
|
||||
let g:loaded_nerdtree_git_status_autoload = 1
|
||||
|
||||
function! gitstatus#isWin() abort
|
||||
return has('win16') || has('win32') || has('win64')
|
||||
endfunction
|
||||
|
||||
if get(g:, 'NERDTreeGitStatusUseNerdFonts', 0)
|
||||
let s:indicatorMap = {
|
||||
\ 'Modified' :nr2char(61545),
|
||||
\ 'Staged' :nr2char(61543),
|
||||
\ 'Untracked' :nr2char(61736),
|
||||
\ 'Renamed' :nr2char(62804),
|
||||
\ 'Unmerged' :nr2char(61556),
|
||||
\ 'Deleted' :nr2char(63167),
|
||||
\ 'Dirty' :nr2char(61453),
|
||||
\ 'Ignored' :nr2char(61738),
|
||||
\ 'Clean' :nr2char(61452),
|
||||
\ 'Unknown' :nr2char(61832)
|
||||
\ }
|
||||
elseif &encoding ==? 'utf-8'
|
||||
let s:indicatorMap = {
|
||||
\ 'Modified' :nr2char(10041),
|
||||
\ 'Staged' :nr2char(10010),
|
||||
\ 'Untracked' :nr2char(10029),
|
||||
\ 'Renamed' :nr2char(10140),
|
||||
\ 'Unmerged' :nr2char(9552),
|
||||
\ 'Deleted' :nr2char(10006),
|
||||
\ 'Dirty' :nr2char(10007),
|
||||
\ 'Ignored' :nr2char(33),
|
||||
\ 'Clean' :nr2char(10004),
|
||||
\ 'Unknown' :nr2char(120744)
|
||||
\ }
|
||||
else
|
||||
let s:indicatorMap = {
|
||||
\ 'Modified' :'*',
|
||||
\ 'Staged' :'+',
|
||||
\ 'Untracked' :'!',
|
||||
\ 'Renamed' :'R',
|
||||
\ 'Unmerged' :'=',
|
||||
\ 'Deleted' :'D',
|
||||
\ 'Dirty' :'X',
|
||||
\ 'Ignored' :'?',
|
||||
\ 'Clean' :'C',
|
||||
\ 'Unknown' :'E'
|
||||
\ }
|
||||
endif
|
||||
|
||||
function! gitstatus#getIndicator(status) abort
|
||||
return get(get(g:, 'NERDTreeGitStatusIndicatorMapCustom', {}),
|
||||
\ a:status,
|
||||
\ s:indicatorMap[a:status])
|
||||
endfunction
|
||||
|
||||
function! gitstatus#shouldConceal() abort
|
||||
return has('conceal') && g:NERDTreeGitStatusConcealBrackets
|
||||
endfunction
|
||||
@@ -0,0 +1,167 @@
|
||||
" ============================================================================
|
||||
" File: autoload/gitstatus/doctor.vim
|
||||
" Description: what does the doctor say?
|
||||
" Maintainer: Xuyuan Pang <xuyuanp at gmail dot com>
|
||||
" 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.
|
||||
" ============================================================================
|
||||
|
||||
let s:types = {
|
||||
\ 'NUMBER': type(0),
|
||||
\ 'STRING': type(''),
|
||||
\ 'FUNCREF': type(function('tr')),
|
||||
\ 'LIST': type([]),
|
||||
\ 'DICT': type({}),
|
||||
\ 'FLOAT': type(0.0),
|
||||
\ 'BOOL': type(v:true),
|
||||
\ 'NULL': type(v:null)
|
||||
\ }
|
||||
|
||||
let s:type_formatters = {}
|
||||
let s:type_formatters[s:types.NUMBER] = { nbr -> string(nbr) }
|
||||
let s:type_formatters[s:types.STRING] = { str -> printf("'%s'", str) }
|
||||
let s:type_formatters[s:types.FUNCREF] = { fn -> string(fn) }
|
||||
let s:type_formatters[s:types.LIST] = { lst -> s:prettifyList(lst, ' \ ', 0, ' ') }
|
||||
let s:type_formatters[s:types.DICT] = { dct -> s:prettifyDict(dct, ' \ ', 0, ' ') }
|
||||
let s:type_formatters[s:types.FLOAT] = { flt -> string(flt) }
|
||||
let s:type_formatters[s:types.BOOL] = { bol -> bol ? 'v:true' : 'v:false' }
|
||||
let s:type_formatters[s:types.NULL] = { nul -> string(nul) }
|
||||
|
||||
function! s:get_git_version() abort
|
||||
return split(system('git version'), "\n")[0]
|
||||
endfunction
|
||||
|
||||
function! s:get_git_status_output(workdir) abort
|
||||
return system(join(gitstatus#util#BuildGitStatusCommand(a:workdir, g:), ' '))
|
||||
endfunction
|
||||
|
||||
function! s:prettifyDict(obj, prefix, level, indent) abort
|
||||
let l:prefix = a:prefix . repeat(a:indent, a:level)
|
||||
if empty(a:obj)
|
||||
return '{}'
|
||||
endif
|
||||
let l:res = "{\n"
|
||||
for [l:key, l:val] in items(a:obj)
|
||||
let l:type = type(l:val)
|
||||
if l:type is# s:types.DICT
|
||||
let l:val = s:prettifyDict(l:val, a:prefix, a:level + 1, a:indent)
|
||||
elseif l:type is# s:types.LIST
|
||||
let l:val = s:prettifyList(l:val, a:prefix , a:level + 1, a:indent)
|
||||
else
|
||||
let l:val = s:prettify(l:val)
|
||||
endif
|
||||
let l:res .= l:prefix . a:indent . "'" . l:key . "': " . l:val . ",\n"
|
||||
endfor
|
||||
let l:res .= l:prefix . '}'
|
||||
return l:res
|
||||
endfunction
|
||||
|
||||
function! s:prettifyList(obj, prefix, level, indent) abort
|
||||
let l:prefix = a:prefix . repeat(a:indent, a:level)
|
||||
if empty(a:obj)
|
||||
return '[]'
|
||||
endif
|
||||
let l:res = "[\n"
|
||||
for l:val in a:obj
|
||||
let l:type = type(l:val)
|
||||
if l:type is# s:types.LIST
|
||||
let l:val = s:prettifyList(l:val, a:prefix, a:level + 1, a:indent)
|
||||
elseif l:type is# s:types.DICT
|
||||
let l:val = s:prettifyDict(l:val, a:prefix, a:level + 1, a:indent)
|
||||
else
|
||||
let l:val = s:prettify(l:val)
|
||||
endif
|
||||
let l:res .= l:prefix . a:indent . l:val . ",\n"
|
||||
endfor
|
||||
let l:res .= l:prefix . ']'
|
||||
return l:res
|
||||
endfunction
|
||||
|
||||
function! s:prettify(obj) abort
|
||||
let l:type = type(a:obj)
|
||||
return call(s:type_formatters[l:type], [a:obj])
|
||||
endfunction
|
||||
|
||||
function! s:loaded_vim_devicons() abort
|
||||
return get(g:, 'loaded_webdevicons', 0) && get(g:, 'webdevicons_enable', 0) && get(g:, 'webdevicons_enable_nerdtree', 0)
|
||||
endfunction
|
||||
|
||||
function! s:loaded_vim_nerdtree_syntax_highlight() abort
|
||||
return exists('g:NERDTreeSyntaxEnabledExtensions')
|
||||
endfunction
|
||||
|
||||
function! s:loaded_vim_nerdtree_tabs() abort
|
||||
return exists('g:nerdtree_tabs_open_on_gui_startup')
|
||||
endfunction
|
||||
|
||||
function! gitstatus#doctor#Say() abort
|
||||
call g:NERDTree.MustBeOpen()
|
||||
call g:NERDTree.CursorToTreeWin()
|
||||
|
||||
let l:line = repeat('=', 80)
|
||||
|
||||
echo has('nvim') ? 'Neovim:' : 'Vim:'
|
||||
echo execute('version')
|
||||
echo l:line
|
||||
|
||||
echo 'NERDTree:'
|
||||
echo 'version: ' . nerdtree#version()
|
||||
echo 'root: ' . b:NERDTree.root.path.str()
|
||||
|
||||
echo l:line
|
||||
|
||||
echo 'Git:'
|
||||
echo 'version: ' . s:get_git_version()
|
||||
let l:git_workdir = get(g:, 'NTGitWorkdir', '')
|
||||
echo 'workdir: ' . l:git_workdir
|
||||
if !empty(l:git_workdir)
|
||||
echo 'status output:'
|
||||
echo s:get_git_status_output(l:git_workdir)
|
||||
endif
|
||||
|
||||
echo l:line
|
||||
|
||||
echo 'Options:'
|
||||
for [l:key, l:val] in items(g:)
|
||||
if l:key =~# 'NERDTreeGitStatus*'
|
||||
echo '' . l:key . ' = ' . s:prettify(l:val)
|
||||
endif
|
||||
endfor
|
||||
|
||||
echo l:line
|
||||
|
||||
echo 'Others:'
|
||||
echo 'vim-devicons: ' . (s:loaded_vim_devicons() ? 'yes' : 'no')
|
||||
if s:loaded_vim_devicons()
|
||||
for [l:key, l:val] in items(g:)
|
||||
if l:key =~# 'WebDevIconsNerdTree*'
|
||||
echo '' . l:key . ' = ' . s:prettify(l:val)
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
|
||||
echo repeat('-', 40)
|
||||
echo 'vim-nerdtree-syntax-highlight: ' . (s:loaded_vim_nerdtree_syntax_highlight() ? 'yes': 'no')
|
||||
if s:loaded_vim_nerdtree_syntax_highlight()
|
||||
for [l:key, l:val] in items(g:)
|
||||
if l:key =~# 'NERDTreeSyntax*'
|
||||
echo '' . l:key . ' = ' . s:prettify(l:val)
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
|
||||
echo repeat('-', 40)
|
||||
echo 'vim-nerdtree-tabs: ' . (s:loaded_vim_nerdtree_tabs() ? 'yes': 'no')
|
||||
if s:loaded_vim_nerdtree_tabs()
|
||||
for [l:key, l:val] in items(g:)
|
||||
if l:key =~# 'nerdtree_tabs_*'
|
||||
echo '' . l:key . ' = ' . s:prettify(l:val)
|
||||
endif
|
||||
endfor
|
||||
endif
|
||||
|
||||
echo l:line
|
||||
endfunction
|
||||
@@ -0,0 +1,131 @@
|
||||
" ============================================================================
|
||||
" File: autoload/gitstatus/job.vim
|
||||
" Description: async-jobs
|
||||
" Maintainer: Xuyuan Pang <xuyuanp at gmail dot com>
|
||||
" 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_job')
|
||||
finish
|
||||
endif
|
||||
let g:loaded_nerdtree_git_status_job = 1
|
||||
|
||||
" stolen from vim-plug
|
||||
let s:nvim = has('nvim-0.2') || (has('nvim') && exists('*jobwait'))
|
||||
let s:vim8 = has('patch-8.0.0039') && exists('*job_start')
|
||||
|
||||
let s:Job = {
|
||||
\ 'running': 0,
|
||||
\ 'failed': 0,
|
||||
\ 'chunks': [''],
|
||||
\ 'err_chunks': [''],
|
||||
\ }
|
||||
|
||||
" disabled ProhibitImplicitScopeVariable because we will use lots of `self`
|
||||
" disabled ProhibitUnusedVariable because lambda
|
||||
" vint: -ProhibitImplicitScopeVariable -ProhibitUnusedVariable
|
||||
function! s:newJob(name, opts) abort
|
||||
return extend(deepcopy(s:Job), {
|
||||
\ 'name': a:name,
|
||||
\ 'opts': a:opts
|
||||
\ })
|
||||
endfunction
|
||||
|
||||
function! s:Job.onStdoutCB(data) abort
|
||||
let self.chunks[-1] .= a:data[0]
|
||||
call extend(self.chunks, a:data[1:])
|
||||
endfunction
|
||||
|
||||
function! s:Job.onStderrCB(data) abort
|
||||
let self.failed = self.failed || !s:isEOF(a:data)
|
||||
let self.err_chunks[-1] .= a:data[0]
|
||||
call extend(self.err_chunks, a:data[1:])
|
||||
endfunction
|
||||
|
||||
function! s:Job.onExitCB() abort
|
||||
let self.running = 0
|
||||
if self.failed
|
||||
call self.onFailed()
|
||||
else
|
||||
call self.onSuccess()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:Job.onFailed() abort
|
||||
if has_key(self.opts, 'on_failed_cb')
|
||||
call call(self.opts.on_failed_cb, [self])
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:Job.onSuccess() abort
|
||||
if has_key(self.opts, 'on_success_cb')
|
||||
call call(self.opts.on_success_cb, [self])
|
||||
endif
|
||||
endfunction
|
||||
|
||||
if s:nvim
|
||||
function! s:Job.run(cmd) abort
|
||||
let jid = jobstart(a:cmd, {
|
||||
\ 'on_stdout': {_job_id, data, _event -> self.onStdoutCB(data)},
|
||||
\ 'on_stderr': {_job_id, data, _event -> self.onStderrCB(data)},
|
||||
\ 'on_exit': {_job_id, _data, _event -> self.onExitCB()},
|
||||
\ })
|
||||
let self.id = jid
|
||||
let self.running = jid > 0
|
||||
if jid <= 0
|
||||
let self.failed = 1
|
||||
let self.err_chunks = jid == 0 ?
|
||||
\ ['invalid arguments'] :
|
||||
\ ['command is not executable']
|
||||
call self.onExitCB()
|
||||
endif
|
||||
endfunction
|
||||
elseif s:vim8
|
||||
function! s:Job.run(cmd) abort
|
||||
let options = {
|
||||
\ 'out_cb': { _ch, data -> self.onStdoutCB([data]) },
|
||||
\ 'err_cb': { _ch, data -> self.onStderrCB([data]) },
|
||||
\ 'close_cb': { _ch -> self.onExitCB() },
|
||||
\ 'out_mode': 'nl',
|
||||
\ 'err_mode': 'nl',
|
||||
\ }
|
||||
if has('patch-8.1.350')
|
||||
let options['noblock'] = 1
|
||||
endif
|
||||
let jid = job_start(a:cmd, options)
|
||||
if job_status(jid) ==# 'run'
|
||||
let self.id = jid
|
||||
let self.running = 1
|
||||
else
|
||||
let self.running = 0
|
||||
let self.failed = 1
|
||||
let self.err_chunks = ['failed to start job']
|
||||
call self.onExitCB()
|
||||
endif
|
||||
endfunction
|
||||
else
|
||||
function! s:Job.run(cmd) abort
|
||||
let output = substitute(system(join(a:cmd, ' ')), "\<C-A>", "\n", 'g')
|
||||
let self.failed = v:shell_error isnot# 0
|
||||
if self.failed
|
||||
let self.err_chunks = [output]
|
||||
else
|
||||
let self.chunks = [output]
|
||||
endif
|
||||
call self.onExitCB()
|
||||
endfunction
|
||||
endif
|
||||
" vint: +ProhibitImplicitScopeVariable +ProhibitUnusedVariable
|
||||
|
||||
function! s:isEOF(data) abort
|
||||
return len(a:data) == 1 && a:data[0] is# ''
|
||||
endfunction
|
||||
|
||||
function! gitstatus#job#Spawn(name, cmd, opts) abort
|
||||
let l:job = s:newJob(a:name, a:opts)
|
||||
call l:job.run(a:cmd)
|
||||
return l:job
|
||||
endfunction
|
||||
@@ -0,0 +1,112 @@
|
||||
" ============================================================================
|
||||
" File: autoload/gitstatus/listener.vim
|
||||
" Description: nerdtree event listener
|
||||
" Maintainer: Xuyuan Pang <xuyuanp at gmail dot com>
|
||||
" 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_listener')
|
||||
finish
|
||||
endif
|
||||
let g:loaded_nerdtree_git_status_listener = 1
|
||||
|
||||
let s:Listener = {
|
||||
\ 'current': {},
|
||||
\ 'next': {},
|
||||
\ }
|
||||
|
||||
" disabled ProhibitImplicitScopeVariable because we will use lots of `self`
|
||||
" vint: -ProhibitImplicitScopeVariable
|
||||
function! s:Listener.OnInit(event) abort
|
||||
call self.callback(a:event)
|
||||
endfunction
|
||||
|
||||
function! s:Listener.OnRefresh(event) abort
|
||||
call self.callback(a:event)
|
||||
endfunction
|
||||
|
||||
function! s:Listener.OnRefreshFlags(event) abort
|
||||
call self.callback(a:event)
|
||||
endfunction
|
||||
|
||||
function! s:Listener.callback(event) abort
|
||||
let l:path = a:event.subject
|
||||
let l:indicator = self.getIndicatorByPath(l:path)
|
||||
call l:path.flagSet.clearFlags('git')
|
||||
if l:indicator !=# ''
|
||||
if gitstatus#shouldConceal()
|
||||
let l:indicator = printf(' %s ', l:indicator)
|
||||
endif
|
||||
call l:path.flagSet.addFlag('git', l:indicator)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function!s:Listener.getIndicatorByPath(path) abort
|
||||
let l:pathStr = gitstatus#util#FormatPath(a:path)
|
||||
let l:statusKey = get(self.current, l:pathStr, '')
|
||||
|
||||
if l:statusKey !=# ''
|
||||
return gitstatus#getIndicator(l:statusKey)
|
||||
endif
|
||||
|
||||
if self.getOption('ShowClean', 0)
|
||||
return gitstatus#getIndicator('Clean')
|
||||
endif
|
||||
|
||||
if self.getOption('ConcealBrackets', 0) && self.getOption('AlignIfConceal', 0)
|
||||
return ' '
|
||||
endif
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
function! s:Listener.SetNext(cache) abort
|
||||
let self.next = a:cache
|
||||
endfunction
|
||||
|
||||
function! s:Listener.HasPath(path_str) abort
|
||||
return has_key(self.current, a:path_str)
|
||||
endfunction
|
||||
|
||||
function! s:Listener.changed() abort
|
||||
return self.current !=# self.next
|
||||
endfunction
|
||||
|
||||
function! s:Listener.update() abort
|
||||
let self.current = self.next
|
||||
endfunction
|
||||
|
||||
function! s:Listener.TryUpdateNERDTreeUI() abort
|
||||
if !g:NERDTree.IsOpen()
|
||||
return
|
||||
endif
|
||||
|
||||
if !self.changed()
|
||||
return
|
||||
endif
|
||||
|
||||
call self.update()
|
||||
|
||||
let l:winnr = winnr()
|
||||
let l:altwinnr = winnr('#')
|
||||
|
||||
try
|
||||
call g:NERDTree.CursorToTreeWin()
|
||||
call b:NERDTree.root.refreshFlags()
|
||||
call NERDTreeRender()
|
||||
finally
|
||||
exec l:altwinnr . 'wincmd w'
|
||||
exec l:winnr . 'wincmd w'
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:Listener.getOption(name, default) abort
|
||||
return get(self.opts, 'NERDTreeGitStatus' . a:name, a:default)
|
||||
endfunction
|
||||
" vint: +ProhibitImplicitScopeVariable
|
||||
|
||||
function! gitstatus#listener#New(opts) abort
|
||||
return extend(deepcopy(s:Listener), {'opts': a:opts})
|
||||
endfunction
|
||||
@@ -0,0 +1,56 @@
|
||||
" ============================================================================
|
||||
" File: autoload/gitstatus/job.vim
|
||||
" Description: leveled-logger
|
||||
" Maintainer: Xuyuan Pang <xuyuanp at gmail dot com>
|
||||
" 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_log')
|
||||
finish
|
||||
endif
|
||||
let g:loaded_nerdtree_git_status_log = 1
|
||||
|
||||
let s:debug = 0 | :lockvar s:debug
|
||||
let s:info = 1 | :lockvar s:info
|
||||
let s:warning = 2 | :lockvar s:warning
|
||||
let s:error = 3 | :lockvar s:error
|
||||
|
||||
let s:Logger = {}
|
||||
|
||||
" vint: -ProhibitImplicitScopeVariable
|
||||
function! s:Logger.output(level, msg) abort
|
||||
if a:level < self.level
|
||||
return
|
||||
endif
|
||||
echomsg '[nerdtree-git-status] ' . a:msg
|
||||
endfunction
|
||||
|
||||
function! s:Logger.debug(msg) abort
|
||||
echohl LineNr |
|
||||
\ call self.output(s:debug, a:msg) |
|
||||
\ echohl None
|
||||
endfunction
|
||||
|
||||
function! s:Logger.info(msg) abort
|
||||
call self.output(s:info, a:msg)
|
||||
endfunction
|
||||
|
||||
function! s:Logger.warning(msg) abort
|
||||
echohl WarningMsg |
|
||||
\ call self.output(s:warning, a:msg) |
|
||||
\ echohl None
|
||||
endfunction
|
||||
|
||||
function! s:Logger.error(msg) abort
|
||||
echohl ErrorMsg |
|
||||
\ call self.output(s:error, a:msg) |
|
||||
\ echohl None
|
||||
endfunction
|
||||
" vint: +ProhibitImplicitScopeVariable
|
||||
|
||||
function! gitstatus#log#NewLogger(level) abort
|
||||
return extend(copy(s:Logger), {'level': a:level})
|
||||
endfunction
|
||||
@@ -0,0 +1,225 @@
|
||||
" ============================================================================
|
||||
" File: autoload/git_status/util.vim
|
||||
" Description: utils
|
||||
" Maintainer: Xuyuan Pang <xuyuanp at gmail dot com>
|
||||
" 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_util')
|
||||
finish
|
||||
endif
|
||||
let g:loaded_nerdtree_git_status_util = 1
|
||||
|
||||
" FUNCTION: gitstatus#utilFormatPath
|
||||
" This function is used to format nerdtree.Path.
|
||||
" For Windows, returns in format 'C:/path/to/file'
|
||||
"
|
||||
" ARGS:
|
||||
" path: nerdtree.Path
|
||||
"
|
||||
" RETURNS:
|
||||
" absolute path
|
||||
if gitstatus#isWin()
|
||||
if exists('+shellslash')
|
||||
function! gitstatus#util#FormatPath(path) abort
|
||||
let l:sslbak = &shellslash
|
||||
try
|
||||
set shellslash
|
||||
return a:path.str()
|
||||
finally
|
||||
let &shellslash = l:sslbak
|
||||
endtry
|
||||
endfunction
|
||||
else
|
||||
function! gitstatus#util#FormatPath(path) abort
|
||||
let l:pathStr = a:path.str()
|
||||
let l:pathStr = a:path.WinToUnixPath(l:pathStr)
|
||||
let l:pathStr = a:path.drive . l:pathStr
|
||||
return l:pathStr
|
||||
endfunction
|
||||
endif
|
||||
else
|
||||
function! gitstatus#util#FormatPath(path) abort
|
||||
return a:path.str()
|
||||
endfunction
|
||||
endif
|
||||
|
||||
function! gitstatus#util#BuildGitWorkdirCommand(root, opts) abort
|
||||
return [
|
||||
\ get(a:opts, 'NERDTreeGitStatusGitBinPath', 'git'),
|
||||
\ '-C', a:root,
|
||||
\ 'rev-parse',
|
||||
\ '--show-toplevel',
|
||||
\ ]
|
||||
endfunction
|
||||
|
||||
function! gitstatus#util#BuildGitStatusCommand(root, opts) abort
|
||||
let l:cmd = [
|
||||
\ get(a:opts, 'NERDTreeGitStatusGitBinPath', 'git'),
|
||||
\ '-C', a:root,
|
||||
\ 'status',
|
||||
\ '--porcelain' . (get(a:opts, 'NERDTreeGitStatusPorcelainVersion', 2) ==# 2 ? '=v2' : ''),
|
||||
\ '-z'
|
||||
\ ]
|
||||
if has_key(a:opts, 'NERDTreeGitStatusUntrackedFilesMode')
|
||||
let l:cmd += ['--untracked-files=' . a:opts['NERDTreeGitStatusUntrackedFilesMode']]
|
||||
endif
|
||||
|
||||
if get(a:opts, 'NERDTreeGitStatusShowIgnored', 0)
|
||||
let l:cmd += ['--ignored=traditional']
|
||||
endif
|
||||
|
||||
if has_key(a:opts, 'NERDTreeGitStatusIgnoreSubmodules')
|
||||
let l:cmd += ['--ignore-submodules=' . a:opts['NERDTreeGitStatusIgnoreSubmodules']]
|
||||
endif
|
||||
|
||||
return l:cmd
|
||||
endfunction
|
||||
|
||||
function! gitstatus#util#ParseGitStatusLines(root, statusLines, opts) abort
|
||||
let l:result = {}
|
||||
let l:is_rename = 0
|
||||
for l:line in a:statusLines
|
||||
if l:is_rename
|
||||
call gitstatus#util#UpdateParentDirsStatus(l:result, a:root, a:root . '/' . l:line, 'Dirty', a:opts)
|
||||
let l:is_rename = 0
|
||||
continue
|
||||
endif
|
||||
let [l:pathStr, l:statusKey] = gitstatus#util#ParseGitStatusLine(l:line, a:opts)
|
||||
|
||||
let l:pathStr = a:root . '/' . l:pathStr
|
||||
if l:pathStr[-1:-1] is# '/'
|
||||
let l:pathStr = l:pathStr[:-2]
|
||||
endif
|
||||
let l:is_rename = l:statusKey is# 'Renamed'
|
||||
let l:result[l:pathStr] = l:statusKey
|
||||
|
||||
call gitstatus#util#UpdateParentDirsStatus(l:result, a:root, l:pathStr, l:statusKey, a:opts)
|
||||
endfor
|
||||
return l:result
|
||||
endfunction
|
||||
|
||||
let s:unmerged_status = {
|
||||
\ 'DD': 1,
|
||||
\ 'AU': 1,
|
||||
\ 'UD': 1,
|
||||
\ 'UA': 1,
|
||||
\ 'DU': 1,
|
||||
\ 'AA': 1,
|
||||
\ 'UU': 1,
|
||||
\ }
|
||||
|
||||
" Function: s:getStatusKey() function {{{2
|
||||
" This function is used to get git status key
|
||||
"
|
||||
" Args:
|
||||
" x: index tree
|
||||
" y: work tree
|
||||
"
|
||||
"Returns:
|
||||
" status key
|
||||
"
|
||||
" man git-status
|
||||
" X Y Meaning
|
||||
" -------------------------------------------------
|
||||
" [MD] not updated
|
||||
" M [ MD] updated in index
|
||||
" A [ MD] added to index
|
||||
" D [ M] deleted from index
|
||||
" R [ MD] renamed in index
|
||||
" C [ MD] copied in index
|
||||
" [MARC] index and work tree matches
|
||||
" [ MARC] M work tree changed since index
|
||||
" [ MARC] D deleted in work tree
|
||||
" -------------------------------------------------
|
||||
" D D unmerged, both deleted
|
||||
" A U unmerged, added by us
|
||||
" U D unmerged, deleted by them
|
||||
" U A unmerged, added by them
|
||||
" D U unmerged, deleted by us
|
||||
" A A unmerged, both added
|
||||
" U U unmerged, both modified
|
||||
" -------------------------------------------------
|
||||
" ? ? untracked
|
||||
" ! ! ignored
|
||||
" -------------------------------------------------
|
||||
function! s:getStatusKey(x, y) abort
|
||||
let l:xy = a:x . a:y
|
||||
if get(s:unmerged_status, l:xy, 0)
|
||||
return 'Unmerged'
|
||||
elseif l:xy ==# '??'
|
||||
return 'Untracked'
|
||||
elseif l:xy ==# '!!'
|
||||
return 'Ignored'
|
||||
elseif a:y ==# 'M'
|
||||
return 'Modified'
|
||||
elseif a:y ==# 'D'
|
||||
return 'Deleted'
|
||||
elseif a:y =~# '[RC]'
|
||||
return 'Renamed'
|
||||
elseif a:x ==# 'D'
|
||||
return 'Deleted'
|
||||
elseif a:x =~# '[MA]'
|
||||
return 'Staged'
|
||||
elseif a:x =~# '[RC]'
|
||||
return 'Renamed'
|
||||
else
|
||||
return 'Unknown'
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! gitstatus#util#ParseGitStatusLine(statusLine, opts) abort
|
||||
if get(a:opts, 'NERDTreeGitStatusPorcelainVersion', 2) ==# 2
|
||||
if a:statusLine[0] ==# '1'
|
||||
let l:statusKey = s:getStatusKey(a:statusLine[2], a:statusLine[3])
|
||||
let l:pathStr = a:statusLine[113:]
|
||||
elseif a:statusLine[0] ==# '2'
|
||||
let l:statusKey = 'Renamed'
|
||||
let l:pathStr = a:statusLine[113:]
|
||||
let l:pathStr = l:pathStr[stridx(l:pathStr, ' ')+1:]
|
||||
elseif a:statusLine[0] ==# 'u'
|
||||
let l:statusKey = 'Unmerged'
|
||||
let l:pathStr = a:statusLine[161:]
|
||||
elseif a:statusLine[0] ==# '?'
|
||||
let l:statusKey = 'Untracked'
|
||||
let l:pathStr = a:statusLine[2:]
|
||||
elseif a:statusLine[0] ==# '!'
|
||||
let l:statusKey = 'Ignored'
|
||||
let l:pathStr = a:statusLine[2:]
|
||||
else
|
||||
throw '[nerdtree_git_status] unknown status: ' . a:statusLine
|
||||
endif
|
||||
return [l:pathStr, l:statusKey]
|
||||
else
|
||||
let l:pathStr = a:statusLine[3:]
|
||||
let l:statusKey = s:getStatusKey(a:statusLine[0], a:statusLine[1])
|
||||
return [l:pathStr, l:statusKey]
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! gitstatus#util#UpdateParentDirsStatus(cache, root, pathStr, statusKey, opts) abort
|
||||
let l:dirtyPath = fnamemodify(a:pathStr, ':h')
|
||||
let l:dir_dirty_only = get(a:opts, 'NERDTreeGitStatusDirDirtyOnly', 1)
|
||||
while l:dirtyPath !=# a:root
|
||||
let l:key = get(a:cache, l:dirtyPath, '')
|
||||
if l:dir_dirty_only
|
||||
if l:key ==# ''
|
||||
let a:cache[l:dirtyPath] = 'Dirty'
|
||||
else
|
||||
return
|
||||
endif
|
||||
else
|
||||
if l:key ==# ''
|
||||
let a:cache[l:dirtyPath] = a:statusKey
|
||||
elseif l:key ==# 'Dirty' || l:key ==# a:statusKey
|
||||
return
|
||||
else
|
||||
let a:cache[l:dirtyPath] = 'Dirty'
|
||||
endif
|
||||
endif
|
||||
let l:dirtyPath = fnamemodify(l:dirtyPath, ':h')
|
||||
endwhile
|
||||
endfunction
|
||||
Reference in New Issue
Block a user