Initial checkin

This commit is contained in:
2024-10-08 17:58:44 +09:00
commit 847d7d3c59
260 changed files with 50744 additions and 0 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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