1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-04-14 07:09:11 +08:00

Change filetree gitstatus option name (#4465)

This commit is contained in:
Wang Shidong 2021-09-12 23:45:01 +08:00 committed by GitHub
parent 39bf31c4b2
commit caa54d32e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 1719 additions and 70 deletions

View File

@ -1196,7 +1196,19 @@ let g:spacevim_enable_vimfiler_welcome = 1
" Enable/Disable autocompletion of parentheses, default is 1 (enabled).
let g:spacevim_autocomplete_parens = 1
""
" Enable/Disable gitstatus column in vimfiler buffer, default is 0.
" @section enable_filetree_gitstatus, options-enable_filetree_gitstatus
" @parentsection options
" Enable/Disable gitstatus column in filetree buffer, default is false.
" >
" enable_filetree_gitstatus = false
" <
" NOTE: the `enable_vimfiler_gitstatus` option has been deprecated.
" *spacevim-options-enable_vimfiler_gitstatus*
" *g:spacevim_enable_vimfiler_gitstatus*
""
" Enable/Disable gitstatus column in filetree buffer, default is 0.
let g:spacevim_enable_filetree_gitstatus = 0
let g:spacevim_enable_vimfiler_gitstatus = 0
""
" Enable/Disable filetypeicon column in vimfiler buffer, default is 0.

View File

@ -30,6 +30,10 @@ function! SpaceVim#layers#core#plugins() abort
if g:spacevim_filemanager ==# 'nerdtree'
call add(plugins, [g:_spacevim_root_dir . 'bundle/nerdtree', { 'merged' : 0,
\ 'loadconf' : 1}])
if g:spacevim_enable_filetree_gitstatus
call add(plugins, [g:_spacevim_root_dir . 'bundle/nerdtree-git-plugin', { 'merged' : 0,
\ 'loadconf' : 1}])
endif
elseif g:spacevim_filemanager ==# 'vimfiler'
call add(plugins, [g:_spacevim_root_dir . 'bundle/vimfiler.vim',{
\ 'merged' : 0,

View File

@ -47,9 +47,6 @@ function! SpaceVim#layers#git#plugins() abort
else
call add(plugins, [g:_spacevim_root_dir . 'bundle/git.vim', { 'merged' : 0}])
endif
if g:spacevim_filemanager ==# 'nerdtree'
call add(plugins, ['Xuyuanp/nerdtree-git-plugin', {'merged' : 0}])
endif
return plugins
endfunction

View File

@ -0,0 +1,41 @@
---
name: "Bug Report"
about: "nerdtree-git-plugin is misbehaving? Tell us about it."
labels: bug
---
<!-- Attention! Please Read!
Please fill out ALL the information below so that the issue can be fully
understood. Omitting information will delay the resolution of your issue. It
will be labeled "Needs More Info", and may be closed until there is enough
information.
Keep in mind that others may have the same question in the future. The better
your information, the more likely they'll be able to help themselves. -->
#### Self-Diagnosis
<!-- Check the boxes after creating the issue, or use [x]. -->
- [ ] I have searched the [issues](https://github.com/Xuyuanp/nerdtree-git-plugin/issues) for an answer to my question.
- [ ] I have reviewed the NERDTree documentation(README.md).
- [ ] I have searched the web for an answer to my question.
#### Environment (for bug reports)
- [ ] Operating System:
- [ ] vimrc settings
```vim
" all settings about nerdtree and other plugins
```
- Other NERDTree-dependent Plugins
- [ ] jistr/vim-nerdtree-tabs
- [ ] ryanoasis/vim-devicons
- [ ] tiagofumo/vim-nerdtree-syntax-highlight
- [ ] Others (specify):
- [ ] I've verified the issue occurs with only `nerdtree-git-plugin` installed.
- [ ] Copy-Paste `call gitstatus#doctor#Say()` outputs
#### Steps to Reproduce the Issue
1.
#### Current Result (Include screenshots where appropriate.)
#### Expected Result

View File

@ -0,0 +1,8 @@
---
name: "Feature Request"
about: "What new feature are you requesting for nerdtree-git-plugin?"
labels: "feature request"
---
#### Description

View File

@ -0,0 +1,23 @@
---
name: "General Question"
about: "Having trouble setting up nerdtree-git-plugin? Need clarification on a setting? Ask your question here."
labels: "general question"
---
<!-- Attention! Please Read!
Please fill out ALL the information below so that the issue can be fully
understood. Omitting information will delay the resolution of your issue. It
will be labeled "Needs More Info", and may be closed until there is enough
information.
Keep in mind that others may have the same question in the future. The better
your information, the more likely they'll be able to help themselves. -->
#### Self-Diagnosis
<!-- Check the boxes after creating the issue, or use [x]. -->
- [ ] I have searched the [issues](https://github.com/Xuyuanp/nerdtree-git-plugin/issues) for an answer to my question.
- [ ] I have reviewed the NERDTree documentation(README.md).
- [ ] I have searched the web for an answer to my question.
#### State Your Question

View File

@ -0,0 +1,3 @@
### Description of Changes
Closes # <!-- Enter the issue number this PR addresses. If none, remove this line. -->

View File

@ -0,0 +1,17 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 30
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
- security
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

View File

@ -0,0 +1,57 @@
name: CI
on: [push, pull_request]
jobs:
vint:
name: Vint
strategy:
fail-fast: false
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Run vint with reviewdog
uses: reviewdog/action-vint@v1.0.1
with:
github_token: ${{ secrets.github_token }}
reporter: github-pr-review
test:
name: Unit tests
strategy:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
neovim: [true, false]
version: [stable, nightly]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Checkout themis.vim
uses: actions/checkout@v2
with:
repository: thinca/vim-themis
path: tmp/vim-themis
- name: Checkout nerdtree
uses: actions/checkout@v2
with:
repository: preservim/nerdtree
path: tmp/nerdtree
# Remove apt repos that are known to break from time to time
# See https://github.com/actions/virtual-environments/issues/323
- name: Remove broken apt repos [Ubuntu]
if: matrix.os == 'ubuntu-latest'
run: |
for apt_file in `grep -lr microsoft /etc/apt/sources.list.d/`; do sudo rm $apt_file; done
- name: Install Vim or Neovim
uses: rhysd/action-setup-vim@v1
id: vim
with:
neovim: ${{ matrix.neovim }}
version: ${{ matrix.version }}
- name: Run unit tests
env:
THEMIS_VIM: ${{ steps.vim.outputs.executable }}
THEMIS_PROFILE: profile.txt
run: |
echo $THEMIS_VIM
./tmp/vim-themis/bin/themis --runtimepath ./tmp/nerdtree --reporter spec tests/
# TODO: coverage

View File

@ -0,0 +1,14 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-added-large-files
- id: mixed-line-ending
- repo: https://github.com/Vimjas/vint
rev: v0.4a3
hooks:
- id: vint

View File

@ -0,0 +1,40 @@
cmdargs:
# Checking more strictly
severity: style_problem
# Enable coloring
color: false
# Enable Neovim syntax
env:
neovim: true
policies:
ProhibitAutocmdWithNoGroup:
enabled: true
ProhibitCommandRelyOnUser:
enabled: true
ProhibitCommandWithUnintendedSideEffect:
enabled: true
ProhibitEncodingOptionAfterScriptEncoding:
enabled: true
ProhibitEqualTildeOperator:
enabled: true
ProhibitImplicitScopeBuiltinVariable:
enabled: true
ProhibitMissingScriptEncoding:
enabled: true
ProhibitNoAbortFunction:
enabled: true
ProhibitSetNoCompatible:
enabled: true
ProhibitUnnecessaryDoubleQuote:
enabled: true
ProhibitUnusedVariable:
enabled: true
ProhibitUsingUndeclaredVariable:
enabled: true
ProhibitAbbreviationOption:
enabled: true
ProhibitImplicitScopeVariable:
enabled: true

View File

@ -0,0 +1,14 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

View File

@ -0,0 +1,112 @@
nerdtree-git-plugin
===================
[![Github Action](https://img.shields.io/github/workflow/status/Xuyuanp/nerdtree-git-plugin/CI)](https://github.com/Xuyuanp/nerdtree-git-plugin/actions?query=workflow%3ACI)
[![License: WTFPL](https://img.shields.io/github/license/Xuyuanp/nerdtree-git-plugin)](http://www.wtfpl.net/about/)
[![GitHub contributors](https://img.shields.io/github/contributors/Xuyuanp/nerdtree-git-plugin)](https://github.com/Xuyuanp/nerdtree-git-plugin/graphs/contributors)
A plugin of [NERDTree](https://github.com/preservim/nerdtree) showing git status flags.
The original project [git-nerdtree](https://github.com/Xuyuanp/git-nerdtree) will not be maintained any longer.
![Imgur](http://i.imgur.com/jSCwGjU.gif?1)
## Installation
Use your favorite package manager. Here is the example of using [vim-plug](https://github.com/junegunn/vim-plug)
```vim script
Plug 'preservim/nerdtree' |
\ Plug 'Xuyuanp/nerdtree-git-plugin'
```
## New project
[Yanil](https://github.com/Xuyuanp/yanil): Another nerdtree like plugin for neovim(>= 0.5.0) only. I'm focusing on this project.
## FAQ
> Got error message like `Error detected while processing function
177[2]..178[22]..181[7]..144[9]..142[36]..238[4]..NERDTreeGitStatusRefreshListener[2]..NERDTreeGitStatusRefresh:
line 6:
E484: Can't open file /tmp/vZEZ6gM/1` while nerdtree opening in fish, how to resolve this problem?
This was because that vim couldn't execute `system` function in `fish`. Add `set shell=sh` in your vimrc.
This issue has been fixed.
> How to config custom symbols?
Use this variable to change symbols.
```vim
let g:NERDTreeGitStatusIndicatorMapCustom = {
\ 'Modified' :'✹',
\ 'Staged' :'✚',
\ 'Untracked' :'✭',
\ 'Renamed' :'➜',
\ 'Unmerged' :'═',
\ 'Deleted' :'✖',
\ 'Dirty' :'✗',
\ 'Ignored' :'☒',
\ 'Clean' :'✔︎',
\ 'Unknown' :'?',
\ }
```
There is a predefined map used *nerdfonts*, to enable it
```vim
let g:NERDTreeGitStatusUseNerdFonts = 1 " you should install nerdfonts by yourself. default: 0
```
> How to show `ignored` status?
```vim
let g:NERDTreeGitStatusShowIgnored = 1 " a heavy feature may cost much more time. default: 0
```
> How to cooperate with [vim-devicons](https://github.com/ryanoasis/vim-devicons)
```vim
Plug 'preservim/nerdtree' |
\ Plug 'Xuyuanp/nerdtree-git-plugin' |
\ Plug 'ryanoasis/vim-devicons'
```
Make sure they are in the right order.
> How to indicate every single `untracked` file under an `untracked` dir?
```vim
let g:NERDTreeGitStatusUntrackedFilesMode = 'all' " a heavy feature too. default: normal
```
> How to set `git` executable file path?
```vim
let g:NERDTreeGitStatusGitBinPath = '/your/file/path' " default: git (auto find in path)
```
> How to show `Clean` indicator?
```vim
let g:NERDTreeGitStatusShowClean = 1 " default: 0
```
> How to hide the boring brackets(`[ ]`)?
```vim
let g:NERDTreeGitStatusConcealBrackets = 1 " default: 0
```
**NOTICE**: DO NOT enable this feature if you have also installed [vim-devicons](https://github.com/ryanoasis/vim-devicons).
## Shameless Self Promotion
[Yanil](https://github.com/Xuyuanp/yanil): Yet Another Nerdtree In Lua
## Credits
* [scrooloose](https://github.com/scrooloose): Open API for me.
* [git\_nerd](https://github.com/swerner/git_nerd): Where my idea comes from.
* [PickRelated](https://github.com/PickRelated): Add custom indicators & Review code.

View File

@ -0,0 +1,58 @@
" ============================================================================
" File: autoload/gitstatus/job.vim
" Description: git status indicator syntax highlighting
" 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 !get(g:, 'NERDTreeGitStatusEnable', 0)
finish
endif
function! s:getIndicator(status) abort
return gitstatus#getIndicator(a:status)
endfunction
if gitstatus#shouldConceal()
" Hide the backets
syntax match hideBracketsInNerdTreeL "\]" contained conceal containedin=NERDTreeFlags
syntax match hideBracketsInNerdTreeR "\[" contained conceal containedin=NERDTreeFlags
setlocal conceallevel=3
setlocal concealcursor=nvic
endif
function! s:highlightFromGroup(group) abort
let l:synid = synIDtrans(hlID(a:group))
let [l:ctermfg, l:guifg] = [synIDattr(l:synid, 'fg', 'cterm'), synIDattr(l:synid, 'fg', 'gui')]
return 'cterm=NONE ctermfg=' . l:ctermfg . ' ctermbg=NONE gui=NONE guifg=' . l:guifg . ' guibg=NONE'
endfunction
function! s:setHightlighting() abort
let l:synlist = [
\ ['Unmerged', 'Function'],
\ ['Modified', 'Special'],
\ ['Staged', 'Function'],
\ ['Renamed', 'Title'],
\ ['Unmerged', 'Label'],
\ ['Untracked', 'Comment'],
\ ['Dirty', 'Tag'],
\ ['Deleted', 'Operator'],
\ ['Ignored', 'SpecialKey'],
\ ['Clean', 'Method'],
\ ]
for [l:name, l:group] in l:synlist
let l:indicator = escape(s:getIndicator(l:name), '\#-*.$')
let l:synname = 'NERDTreeGitStatus' . l:name
execute 'silent! syntax match ' . l:synname . ' #\m\C\zs[' . l:indicator . ']\ze[^\]]*\]# containedin=NERDTreeFlags'
let l:hipat = get(get(g:, 'NERDTreeGitStatusHighlightingCustom', {}),
\ l:name,
\ s:highlightFromGroup(l:group))
execute 'silent! highlight ' . l:synname . ' ' . l:hipat
endfor
endfunction
silent! call s:setHightlighting()

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,136 @@
" ============================================================================
" 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()},
\ 'env': {'GIT_OPTIONAL_LOCKS': '0'},
\ })
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',
\ 'env': {'GIT_OPTIONAL_LOCKS': '0'},
\ }
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 bak = $GIT_OPTIONAL_LOCKS
let $GIT_OPTIONAL_LOCKS = 0
let output = substitute(system(join(a:cmd, ' ')), "\<C-A>", "\n", 'g')
let $GIT_OPTIONAL_LOCKS = bak
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
noautocmd exec l:altwinnr . 'wincmd w'
noautocmd 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,232 @@
" ============================================================================
" 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
if has_key(a:opts, 'NERDTreeGitStatusCwdOnly')
let l:cmd += ['.']
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
if get(a:cache, a:pathStr, '') ==# 'Ignored'
return
endif
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

View File

@ -0,0 +1,348 @@
" ============================================================================
" File: git_status.vim
" Description: plugin for NERD Tree that provides git status support
" 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.
" ============================================================================
scriptencoding utf-8
if exists('g:loaded_nerdtree_git_status')
finish
endif
let g:loaded_nerdtree_git_status = 1
let s:is_win = gitstatus#isWin()
" stolen from nerdtree
"Function: s:initVariable() function {{{2
"This function is used to initialise a given variable to a given value. The
"variable is only initialised if it does not exist prior
"
"Args:
"var: the name of the var to be initialised
"value: the value to initialise var to
"
"Returns:
"1 if the var is set, 0 otherwise
function! s:initVariable(var, value) abort
if !exists(a:var)
exec 'let ' . a:var . ' = ' . "'" . substitute(a:value, "'", "''", 'g') . "'"
return 1
endif
return 0
endfunction
let s:default_vals = {
\ 'g:NERDTreeGitStatusEnable': 1,
\ 'g:NERDTreeGitStatusUpdateOnWrite': 1,
\ 'g:NERDTreeGitStatusUpdateOnCursorHold': 1,
\ 'g:NERDTreeGitStatusShowIgnored': 0,
\ 'g:NERDTreeGitStatusUseNerdFonts': 0,
\ 'g:NERDTreeGitStatusDirDirtyOnly': 1,
\ 'g:NERDTreeGitStatusConcealBrackets': 0,
\ 'g:NERDTreeGitStatusAlignIfConceal': 1,
\ 'g:NERDTreeGitStatusShowClean': 0,
\ 'g:NERDTreeGitStatusLogLevel': 2,
\ 'g:NERDTreeGitStatusPorcelainVersion': 2,
\ 'g:NERDTreeGitStatusCwdOnly': 0,
\ 'g:NERDTreeGitStatusMapNextHunk': ']c',
\ 'g:NERDTreeGitStatusMapPrevHunk': '[c',
\ 'g:NERDTreeGitStatusUntrackedFilesMode': 'normal',
\ 'g:NERDTreeGitStatusGitBinPath': 'git',
\ }
for [s:var, s:value] in items(s:default_vals)
call s:initVariable(s:var, s:value)
endfor
let s:logger = gitstatus#log#NewLogger(g:NERDTreeGitStatusLogLevel)
function! s:deprecated(oldv, newv) abort
call s:logger.warning(printf("option '%s' is deprecated, please use '%s'", a:oldv, a:newv))
endfunction
function! s:migrateVariable(oldv, newv) abort
if exists(a:oldv)
call s:deprecated(a:oldv, a:newv)
exec 'let ' . a:newv . ' = ' . a:oldv
return 1
endif
return 0
endfunction
let s:need_migrate_vals = {
\ 'g:NERDTreeShowGitStatus': 'g:NERDTreeGitStatusEnable',
\ 'g:NERDTreeUpdateOnWrite': 'g:NERDTreeGitStatusUpdateOnWrite',
\ 'g:NERDTreeMapNextHunk': 'g:NERDTreeGitStatusMapNextHunk',
\ 'g:NERDTreeMapPrevHunk': 'g:NERDTreeGitStatusMapPrevHunk',
\ 'g:NERDTreeShowIgnoredStatus': 'g:NERDTreeGitStatusShowIgnored',
\ 'g:NERDTreeIndicatorMapCustom': 'g:NERDTreeGitStatusIndicatorMapCustom',
\ }
for [s:oldv, s:newv] in items(s:need_migrate_vals)
call s:migrateVariable(s:oldv, s:newv)
endfor
if !g:NERDTreeGitStatusEnable
finish
endif
if !executable(g:NERDTreeGitStatusGitBinPath)
call s:logger.error('git command not found')
finish
endif
" FUNCTION: path2str
" This function is used to format nerdtree.Path.
" For Windows, returns in format 'C:/path/to/file'
"
" ARGS:
" path: nerdtree.Path
"
" RETURNS:
" absolute path
function! s:path2str(path) abort
return gitstatus#util#FormatPath(a:path)
endfunction
" disable ProhibitUnusedVariable because these three functions used to callback
" vint: -ProhibitUnusedVariable
function! s:onGitWorkdirSuccessCB(job) abort
let g:NTGitWorkdir = split(join(a:job.chunks, ''), "\n")[0]
call s:logger.debug(printf("'%s' is in a git repo: '%s'", a:job.opts.cwd, g:NTGitWorkdir))
call s:enableLiveUpdate()
call s:refreshGitStatus('init', g:NTGitWorkdir)
endfunction
function! s:onGitWorkdirFailedCB(job) abort
let l:errormsg = join(a:job.err_chunks, '')
if l:errormsg =~# 'fatal: Not a git repository'
call s:logger.debug(printf("'%s' is not in a git repo", a:job.opts.cwd))
endif
call s:disableLiveUpdate()
unlet! g:NTGitWorkdir
endfunction
function! s:getGitWorkdir(ntRoot) abort
call gitstatus#job#Spawn('git-workdir',
\ s:buildGitWorkdirCommand(a:ntRoot),
\ {
\ 'on_success_cb': function('s:onGitWorkdirSuccessCB'),
\ 'on_failed_cb': function('s:onGitWorkdirFailedCB'),
\ 'cwd': a:ntRoot,
\ })
endfunction
" vint: +ProhibitUnusedVariable
function! s:buildGitWorkdirCommand(root) abort
return gitstatus#util#BuildGitWorkdirCommand(a:root, g:)
endfunction
function! s:buildGitStatusCommand(workdir) abort
return gitstatus#util#BuildGitStatusCommand(a:workdir, g:)
endfunction
function! s:refreshGitStatus(name, workdir) abort
let l:opts = {
\ 'on_failed_cb': function('s:onGitStatusFailedCB'),
\ 'on_success_cb': function('s:onGitStatusSuccessCB'),
\ 'cwd': a:workdir
\ }
let l:job = gitstatus#job#Spawn(a:name, s:buildGitStatusCommand(a:workdir), l:opts)
return l:job
endfunction
" vint: -ProhibitUnusedVariable
function! s:onGitStatusSuccessCB(job) abort
if !exists('g:NTGitWorkdir') || g:NTGitWorkdir !=# a:job.opts.cwd
call s:logger.debug(printf("git workdir has changed: '%s' -> '%s'", a:job.opts.cwd, get(g:, 'NTGitWorkdir', '')))
return
endif
let l:output = join(a:job.chunks, '')
let l:lines = split(l:output, "\n")
let l:cache = gitstatus#util#ParseGitStatusLines(a:job.opts.cwd, l:lines, g:)
try
call s:listener.SetNext(l:cache)
call s:listener.TryUpdateNERDTreeUI()
catch
endtry
endfunction
function! s:onGitStatusFailedCB(job) abort
let l:errormsg = join(a:job.err_chunks, '')
if l:errormsg =~# "error: option `porcelain' takes no value"
call s:logger.error(printf("'git status' command failed, please upgrade your git binary('v2.11.0' or higher) or set option 'g:NERDTreeGitStatusPorcelainVersion' to 1 in vimrc"))
call s:disableLiveUpdate()
unlet! g:NTGitWorkdir
elseif l:errormsg =~# '^warning: could not open .* Permission denied'
call s:onGitStatusSuccessCB(a:job)
else
call s:logger.error(printf('job[%s] failed: %s', a:job.name, l:errormsg))
endif
endfunction
" FUNCTION: s:onCursorHold(fname) {{{2
function! s:onCursorHold(fname)
" Do not update when a special buffer is selected
if !empty(&l:buftype)
return
endif
let l:fname = s:is_win ?
\ substitute(a:fname, '\', '/', 'g') :
\ a:fname
if !exists('g:NTGitWorkdir') || !s:hasPrefix(l:fname, g:NTGitWorkdir)
return
endif
let l:job = s:refreshGitStatus('cursor-hold', g:NTGitWorkdir)
call s:logger.debug('run cursor-hold job: ' . l:job.id)
endfunction
" FUNCTION: s:onFileUpdate(fname) {{{2
function! s:onFileUpdate(fname)
let l:fname = s:is_win ?
\ substitute(a:fname, '\', '/', 'g') :
\ a:fname
if !exists('g:NTGitWorkdir') || !s:hasPrefix(l:fname, g:NTGitWorkdir)
return
endif
let l:job = s:refreshGitStatus('file-update', g:NTGitWorkdir)
call s:logger.debug('run file-update job: ' . l:job.id)
endfunction
" vint: +ProhibitUnusedVariable
function! s:hasPrefix(text, prefix) abort
return len(a:text) >= len(a:prefix) && a:text[:len(a:prefix)-1] ==# a:prefix
endfunction
function! s:setupNERDTreeListeners(listener) abort
call g:NERDTreePathNotifier.AddListener('init', a:listener.OnInit)
call g:NERDTreePathNotifier.AddListener('refresh', a:listener.OnRefresh)
call g:NERDTreePathNotifier.AddListener('refreshFlags', a:listener.OnRefreshFlags)
endfunction
" FUNCTION: s:findHunk(node, direction)
" Args:
" node: the current node
" direction: next(>0) or prev(<0)
"
" Returns:
" lineNum if the hunk found, -1 otherwise
function! s:findHunk(node, direction) abort
let l:ui = b:NERDTree.ui
let l:rootLn = l:ui.getRootLineNum()
let l:totalLn = line('$')
let l:currLn = l:ui.getLineNum(a:node)
let l:currLn = l:currLn <= l:rootLn ? l:rootLn+1 : l:currLn
let l:step = a:direction > 0 ? 1 : -1
let l:lines = a:direction > 0 ?
\ range(l:currLn+1, l:totalLn, l:step) + range(l:rootLn+1, l:currLn-1, l:step) :
\ range(l:currLn-1, l:rootLn+1, l:step) + range(l:totalLn, l:currLn+1, l:step)
for l:ln in l:lines
let l:path = s:path2str(l:ui.getPath(l:ln))
if s:listener.HasPath(l:path)
return l:ln
endif
endfor
return -1
endfunction
" vint: -ProhibitUnusedVariable
" FUNCTION: s:jumpToNextHunk(node) {{{2
function! s:jumpToNextHunk(node)
let l:ln = s:findHunk(a:node, 1)
if l:ln > 0
exec '' . l:ln
call s:logger.info('Jump to next hunk')
endif
endfunction
" FUNCTION: s:jumpToPrevHunk(node) {{{2
function! s:jumpToPrevHunk(node)
let l:ln = s:findHunk(a:node, -1)
if l:ln > 0
exec '' . l:ln
call s:logger.info('Jump to prev hunk')
endif
endfunction
" vint: +ProhibitUnusedVariable
" 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:setupNERDTreeKeyMappings {{{2
function! s:setupNERDTreeKeyMappings()
let l:s = '<SNR>' . s:SID() . '_'
call NERDTreeAddKeyMap({
\ 'key': g:NERDTreeGitStatusMapNextHunk,
\ 'scope': 'Node',
\ 'callback': l:s.'jumpToNextHunk',
\ 'quickhelpText': 'Jump to next git hunk' })
call NERDTreeAddKeyMap({
\ 'key': g:NERDTreeGitStatusMapPrevHunk,
\ 'scope': 'Node',
\ 'callback': l:s.'jumpToPrevHunk',
\ 'quickhelpText': 'Jump to prev git hunk' })
endfunction
" I don't know why, but vint said they are unused.
" vint: -ProhibitUnusedVariable
function! s:onNERDTreeDirChanged(path) abort
call s:getGitWorkdir(a:path)
endfunction
function! s:onNERDTreeInit(path) abort
call s:getGitWorkdir(a:path)
endfunction
" vint: +ProhibitUnusedVariable
function! s:enableLiveUpdate() abort
augroup nerdtreegitplugin_liveupdate
autocmd!
if g:NERDTreeGitStatusUpdateOnWrite
autocmd BufWritePost * silent! call s:onFileUpdate(expand('%:p'))
endif
if g:NERDTreeGitStatusUpdateOnCursorHold
autocmd CursorHold * silent! call s:onCursorHold(expand('%:p'))
endif
" TODO: is it necessary to pass the buffer name?
autocmd User FugitiveChanged silent! call s:onFileUpdate(expand('%:p'))
autocmd BufEnter NERD_tree_* if exists('b:NERDTree') |
\ call s:onNERDTreeInit(s:path2str(b:NERDTree.root.path)) | endif
augroup end
endfunction
function! s:disableLiveUpdate() abort
augroup nerdtreegitplugin_liveupdate
autocmd!
augroup end
endfunction
augroup nerdtreegitplugin
autocmd!
autocmd User NERDTreeInit call s:onNERDTreeInit(s:path2str(b:NERDTree.root.path))
autocmd User NERDTreeNewRoot call s:onNERDTreeDirChanged(s:path2str(b:NERDTree.root.path))
augroup end
call s:setupNERDTreeKeyMappings()
let s:listener = gitstatus#listener#New(g:)
call s:setupNERDTreeListeners(s:listener)

View File

@ -0,0 +1,4 @@
if exists('$THEMIS_PROFILE')
execute 'profile' 'start' $THEMIS_PROFILE
profile! file ./autoload/*
endif

View File

@ -0,0 +1,109 @@
let s:suite = themis#suite('Test for nerdtree-git-plugin')
let s:assert = themis#helper('assert')
call themis#helper('command').with(s:)
function! s:suite.Initializing() abort
NERDTreeFocus
call s:assert.exists('g:NERDTree')
call s:assert.exists('g:loaded_nerdtree_git_status')
call g:NERDTree.CursorToTreeWin()
call s:assert.exists('b:NERDTree')
endfunction
function! s:suite.BuildGitWorkdirCommand() abort
let l:cmd = gitstatus#util#BuildGitWorkdirCommand('/workdir', {})
call s:assert.equal(l:cmd, ['git', '-C', '/workdir', 'rev-parse', '--show-toplevel'])
let l:cmd = gitstatus#util#BuildGitWorkdirCommand('/workdir', {'NERDTreeGitStatusGitBinPath': '/path/to/git'})
call s:assert.equal(l:cmd, ['/path/to/git', '-C', '/workdir', 'rev-parse', '--show-toplevel'])
endfunction
function! s:suite.BuildGitStatusCommand() abort
let l:cmd = gitstatus#util#BuildGitStatusCommand('/workdir', {})
call s:assert.equal(l:cmd, ['git', '-C', '/workdir', 'status', '--porcelain=v2', '-z'])
let l:cmd = gitstatus#util#BuildGitStatusCommand('/workdir', {
\ 'NERDTreeGitStatusPorcelainVersion': 1
\ })
call s:assert.equal(l:cmd, ['git', '-C', '/workdir', 'status', '--porcelain', '-z'])
let l:cmd = gitstatus#util#BuildGitStatusCommand('/workdir', {
\ 'NERDTreeGitStatusUntrackedFilesMode': 'all'
\ })
call s:assert.equal(l:cmd, ['git', '-C', '/workdir', 'status', '--porcelain=v2', '-z', '--untracked-files=all'])
let l:cmd = gitstatus#util#BuildGitStatusCommand('/workdir', {
\ 'NERDTreeGitStatusShowIgnored': 1
\ })
call s:assert.equal(l:cmd, ['git', '-C', '/workdir', 'status', '--porcelain=v2', '-z', '--ignored=traditional'])
let l:cmd = gitstatus#util#BuildGitStatusCommand('/workdir', {
\ 'NERDTreeGitStatusShowIgnored': 0
\ })
call s:assert.equal(l:cmd, ['git', '-C', '/workdir', 'status', '--porcelain=v2', '-z'])
let l:cmd = gitstatus#util#BuildGitStatusCommand('/workdir', {
\ 'NERDTreeGitStatusIgnoreSubmodules': 'dirty'
\ })
call s:assert.equal(l:cmd, ['git', '-C', '/workdir', 'status', '--porcelain=v2', '-z', '--ignore-submodules=dirty'])
let l:cmd = gitstatus#util#BuildGitStatusCommand('/workdir', {
\ 'NERDTreeGitStatusPorcelainVersion': 1,
\ 'NERDTreeGitStatusUntrackedFilesMode': 'all',
\ 'NERDTreeGitStatusShowIgnored': 1,
\ 'NERDTreeGitStatusIgnoreSubmodules': 'dirty'
\ })
call s:assert.equal(l:cmd, ['git', '-C', '/workdir', 'status', '--porcelain', '-z',
\ '--untracked-files=all',
\ '--ignored=traditional',
\ '--ignore-submodules=dirty'])
endfunction
function! s:suite.Logger() abort
let l:logger = gitstatus#log#NewLogger(1) " info
let l:messages = execute('messages')
call l:logger.debug('debug')
call s:assert.equal(execute('messages'), l:messages)
call l:logger.error('error')
call s:assert.equal(execute('messages'), l:messages . "\n[nerdtree-git-status] error")
endfunction
function! s:suite.CustomIndicator() abort
let g:NERDTreeGitStatusIndicatorMapCustom = {'Untracked': '~'}
let l:staged = gitstatus#getIndicator('Staged')
call s:assert.equal(gitstatus#getIndicator('Staged'), l:staged)
call s:assert.equal(gitstatus#getIndicator('Untracked'), '~')
" Vim(return):E716: Key not present in Dictionary
Throws /E716/ gitstatus#getIndicator('no such status')
endfunction
function! s:suite.UpdateParentDirsStatus() abort
let l:opts = {'NERDTreeGitStatusDirDirtyOnly': 1}
let l:root = '/root'
let l:cache = {}
let l:pathStr = '/root/dir1/dir2/dir3'
let l:cache[l:pathStr] = 'Untracked'
call gitstatus#util#UpdateParentDirsStatus(l:cache, l:root, l:pathStr, 'Untracked', l:opts)
call s:assert.equal({'/root/dir1': 'Dirty', '/root/dir1/dir2': 'Dirty', '/root/dir1/dir2/dir3': 'Untracked'}, l:cache)
let l:pathStr = '/root/dir1/dir2/file0'
let l:cache[l:pathStr] = 'Staged'
call gitstatus#util#UpdateParentDirsStatus(l:cache, l:root, l:pathStr, 'Staged', l:opts)
call s:assert.equal({'/root/dir1': 'Dirty', '/root/dir1/dir2': 'Dirty', '/root/dir1/dir2/dir3': 'Untracked', '/root/dir1/dir2/file0': 'Staged'}, l:cache)
let l:opts = {'NERDTreeGitStatusDirDirtyOnly': 0}
let l:cache = {}
let l:pathStr = '/root/dir1/dir2/dir3'
let l:cache[l:pathStr] = 'Untracked'
call gitstatus#util#UpdateParentDirsStatus(l:cache, l:root, l:pathStr, 'Untracked', l:opts)
call s:assert.equal({'/root/dir1': 'Untracked', '/root/dir1/dir2': 'Untracked', '/root/dir1/dir2/dir3': 'Untracked'}, l:cache)
let l:cache['/root/dir1/file1'] = 'Staged'
call gitstatus#util#UpdateParentDirsStatus(l:cache, l:root, '/root/dir1/file1', 'Staged', l:opts)
call s:assert.equal({'/root/dir1': 'Dirty','/root/dir1/file1': 'Staged', '/root/dir1/dir2': 'Untracked', '/root/dir1/dir2/dir3': 'Untracked'}, l:cache)
endfunction

View File

@ -19,11 +19,11 @@ else
endif
function! s:setcolum() abort
if g:spacevim_enable_vimfiler_filetypeicon && !g:spacevim_enable_vimfiler_gitstatus
if g:spacevim_enable_vimfiler_filetypeicon && !g:spacevim_enable_filetree_gitstatus
return 'indent:icons:filename:type'
elseif !g:spacevim_enable_vimfiler_filetypeicon && g:spacevim_enable_vimfiler_gitstatus
elseif !g:spacevim_enable_vimfiler_filetypeicon && g:spacevim_enable_filetree_gitstatus
return 'indent:git:filename:type'
elseif g:spacevim_enable_vimfiler_filetypeicon && g:spacevim_enable_vimfiler_gitstatus
elseif g:spacevim_enable_vimfiler_filetypeicon && g:spacevim_enable_filetree_gitstatus
return 'indent:git:icons:filename:type'
else
return 'mark:indent:icon:filename:type'

View File

@ -35,11 +35,11 @@ else
endif
function! s:setcolum() abort
if g:spacevim_enable_vimfiler_filetypeicon && !g:spacevim_enable_vimfiler_gitstatus
if g:spacevim_enable_vimfiler_filetypeicon && !g:spacevim_enable_filetree_gitstatus
return 'filetypeicon'
elseif !g:spacevim_enable_vimfiler_filetypeicon && g:spacevim_enable_vimfiler_gitstatus
elseif !g:spacevim_enable_vimfiler_filetypeicon && g:spacevim_enable_filetree_gitstatus
return 'gitstatus'
elseif g:spacevim_enable_vimfiler_filetypeicon && g:spacevim_enable_vimfiler_gitstatus
elseif g:spacevim_enable_vimfiler_filetypeicon && g:spacevim_enable_filetree_gitstatus
return 'filetypeicon:gitstatus'
else
return ''

View File

@ -31,54 +31,56 @@ CONTENTS *SpaceVim-contents*
11. enable_cursorcolumn...........|SpaceVim-options-enable_cursorcolumn|
12. enable_cursorline...............|SpaceVim-options-enable_cursorline|
13. enable_debug.........................|SpaceVim-options-enable_debug|
14. enable_googlesuggest.........|SpaceVim-options-enable_googlesuggest|
15. enable_guicolors.................|SpaceVim-options-enable_guicolors|
16. enable_key_frequency.........|SpaceVim-options-enable_key_frequency|
17. enable_projects_cache.......|SpaceVim-options-enable_projects_cache|
18. enable_statusline_bfpath.|SpaceVim-options-enable_statusline_bfpath|
19. enable_statusline_mode.....|SpaceVim-options-enable_statusline_mode|
20. enable_statusline_tag.......|SpaceVim-options-enable_statusline_tag|
21. enable_tabline_ft_icon.....|SpaceVim-options-enable_tabline_ft_icon|
22. enable_vimfiler_welcome...|SpaceVim-options-enable_vimfiler_welcome|
23. enable_ycm.............................|SpaceVim-options-enable_ycm|
24. error_symbol.........................|SpaceVim-options-error_symbol|
25. escape_key_binding.............|SpaceVim-options-escape_key_binding|
26. file_searching_tools.........|SpaceVim-options-file_searching_tools|
27. filemanager...........................|SpaceVim-options-filemanager|
28. filetree_direction.............|SpaceVim-options-filetree_direction|
29. guifont...................................|SpaceVim-options-guifont|
30. home_files_number...............|SpaceVim-options-home_files_number|
31. info_symbol...........................|SpaceVim-options-info_symbol|
32. keep_server_alive...............|SpaceVim-options-keep_server_alive|
33. language.................................|SpaceVim-options-language|
34. lint_engine...........................|SpaceVim-options-lint_engine|
35. lint_on_the_fly...................|SpaceVim-options-lint_on_the_fly|
36. max_column.............................|SpaceVim-options-max_column|
37. plugin_bundle_dir...............|SpaceVim-options-plugin_bundle_dir|
38. plugin_manager_processes.|SpaceVim-options-plugin_manager_processes|
39. project_rooter_automatically
14. enable_filetree_gitstatus
..................................|SpaceVim-options-enable_filetree_gitstatus|
15. enable_googlesuggest.........|SpaceVim-options-enable_googlesuggest|
16. enable_guicolors.................|SpaceVim-options-enable_guicolors|
17. enable_key_frequency.........|SpaceVim-options-enable_key_frequency|
18. enable_projects_cache.......|SpaceVim-options-enable_projects_cache|
19. enable_statusline_bfpath.|SpaceVim-options-enable_statusline_bfpath|
20. enable_statusline_mode.....|SpaceVim-options-enable_statusline_mode|
21. enable_statusline_tag.......|SpaceVim-options-enable_statusline_tag|
22. enable_tabline_ft_icon.....|SpaceVim-options-enable_tabline_ft_icon|
23. enable_vimfiler_welcome...|SpaceVim-options-enable_vimfiler_welcome|
24. enable_ycm.............................|SpaceVim-options-enable_ycm|
25. error_symbol.........................|SpaceVim-options-error_symbol|
26. escape_key_binding.............|SpaceVim-options-escape_key_binding|
27. file_searching_tools.........|SpaceVim-options-file_searching_tools|
28. filemanager...........................|SpaceVim-options-filemanager|
29. filetree_direction.............|SpaceVim-options-filetree_direction|
30. guifont...................................|SpaceVim-options-guifont|
31. home_files_number...............|SpaceVim-options-home_files_number|
32. info_symbol...........................|SpaceVim-options-info_symbol|
33. keep_server_alive...............|SpaceVim-options-keep_server_alive|
34. language.................................|SpaceVim-options-language|
35. lint_engine...........................|SpaceVim-options-lint_engine|
36. lint_on_the_fly...................|SpaceVim-options-lint_on_the_fly|
37. max_column.............................|SpaceVim-options-max_column|
38. plugin_bundle_dir...............|SpaceVim-options-plugin_bundle_dir|
39. plugin_manager_processes.|SpaceVim-options-plugin_manager_processes|
40. project_rooter_automatically
...............................|SpaceVim-options-project_rooter_automatically|
40. project_rooter_outermost.|SpaceVim-options-project_rooter_outermost|
41. project_rooter_patterns...|SpaceVim-options-project_rooter_patterns|
42. projects_cache_num.............|SpaceVim-options-projects_cache_num|
43. realtime_leader_guide.......|SpaceVim-options-realtime_leader_guide|
44. relativenumber.....................|SpaceVim-options-relativenumber|
45. retry_cnt...............................|SpaceVim-options-retry_cnt|
46. search_tools.........................|SpaceVim-options-search_tools|
47. sidebar_width.......................|SpaceVim-options-sidebar_width|
48. snippet_engine.....................|SpaceVim-options-snippet_engine|
49. statusline_iseparator.......|SpaceVim-options-statusline_iseparator|
50. statusline_left_sections.|SpaceVim-options-statusline_left_sections|
51. statusline_separator.........|SpaceVim-options-statusline_separator|
52. statusline_unicode_symbols
41. project_rooter_outermost.|SpaceVim-options-project_rooter_outermost|
42. project_rooter_patterns...|SpaceVim-options-project_rooter_patterns|
43. projects_cache_num.............|SpaceVim-options-projects_cache_num|
44. realtime_leader_guide.......|SpaceVim-options-realtime_leader_guide|
45. relativenumber.....................|SpaceVim-options-relativenumber|
46. retry_cnt...............................|SpaceVim-options-retry_cnt|
47. search_tools.........................|SpaceVim-options-search_tools|
48. sidebar_width.......................|SpaceVim-options-sidebar_width|
49. snippet_engine.....................|SpaceVim-options-snippet_engine|
50. statusline_iseparator.......|SpaceVim-options-statusline_iseparator|
51. statusline_left_sections.|SpaceVim-options-statusline_left_sections|
52. statusline_separator.........|SpaceVim-options-statusline_separator|
53. statusline_unicode_symbols
.................................|SpaceVim-options-statusline_unicode_symbols|
53. terminal_cursor_shape.......|SpaceVim-options-terminal_cursor_shape|
54. vim_help_language...............|SpaceVim-options-vim_help_language|
55. vimcompatible.......................|SpaceVim-options-vimcompatible|
56. warning_symbol.....................|SpaceVim-options-warning_symbol|
57. windows_index_type.............|SpaceVim-options-windows_index_type|
58. windows_leader.....................|SpaceVim-options-windows_leader|
59. windows_smartclose.............|SpaceVim-options-windows_smartclose|
54. terminal_cursor_shape.......|SpaceVim-options-terminal_cursor_shape|
55. vim_help_language...............|SpaceVim-options-vim_help_language|
56. vimcompatible.......................|SpaceVim-options-vimcompatible|
57. warning_symbol.....................|SpaceVim-options-warning_symbol|
58. windows_index_type.............|SpaceVim-options-windows_index_type|
59. windows_leader.....................|SpaceVim-options-windows_leader|
60. windows_smartclose.............|SpaceVim-options-windows_smartclose|
3. Configuration...........................................|SpaceVim-config|
4. Commands..............................................|SpaceVim-commands|
5. Functions............................................|SpaceVim-functions|
@ -356,6 +358,17 @@ Enable/Disable debug mode for SpaceVim. Default is false.
enable_debug = true
<
==============================================================================
ENABLE_FILETREE_GITSTATUS *SpaceVim-options-enable_filetree_gitstatus*
Enable/Disable gitstatus column in filetree buffer, default is false.
>
enable_filetree_gitstatus = false
<
NOTE: the `enable_vimfiler_gitstatus` option has been deprecated.
*spacevim-options-enable_vimfiler_gitstatus*
*g:spacevim_enable_vimfiler_gitstatus*
==============================================================================
ENABLE_GOOGLESUGGEST *SpaceVim-options-enable_googlesuggest*
@ -1294,8 +1307,8 @@ vim to start up slowly if there are too many files in the current directory.
*g:spacevim_autocomplete_parens*
Enable/Disable autocompletion of parentheses, default is 1 (enabled).
*g:spacevim_enable_vimfiler_gitstatus*
Enable/Disable gitstatus column in vimfiler buffer, default is 0.
*g:spacevim_enable_filetree_gitstatus*
Enable/Disable gitstatus column in filetree buffer, default is 0.
*g:spacevim_enable_vimfiler_filetypeicon*
Enable/Disable filetypeicon column in vimfiler buffer, default is 0.

View File

@ -758,21 +758,22 @@ call SpaceVim#layers#core#statusline#register_sections('test', function('s:test_
### 文件树
SpaceVim 使用 vimfiler 作为默认的文件树插件,默认的快捷键是 `F3`, SpaceVim 也提供了另外一组快捷键 `SPC f t``SPC f T` 来打开文件树。
SpaceVim 使用 nerdtree 作为默认的文件树插件,默认的快捷键是 `F3`,
SpaceVim 也提供了另外一组快捷键 `SPC f t``SPC f T` 来打开文件树。
如果需要修改默认文件树插件,需要在 `~/.SpaceVim.d/init.toml``[options]` 片段中修改选项 `filemanager`
```toml
[options]
# 文件树插件可选值包括:
# - vimfiler (默认)
# - nerdtree
# - defx
filemanager = "defx"
# - nerdtree (默认)
# - vimfiler: 需要编译 vimproc.vim, 在目录 bundle/vimproc.vim 下
# - defx: 需要 +py3 支持
filemanager = "nerdtree"
```
SpaceVim 的文件树提供了版本控制信息的接口,但是这一特性需要分析文件夹内容,
会使得文件树插件比较慢,因此默认没有打开,如果需要使用这一特性,
可向配置文件中加入 `enable_vimfiler_gitstatus = true`,启用后的截图如下:
可向配置文件中加入 `enable_filetree_gitstatus = true`,启用后的截图如下:
![file-tree](https://user-images.githubusercontent.com/13142418/80496111-5065b380-899b-11ea-95c7-02af4d304aaf.png)

View File

@ -824,7 +824,7 @@ Key bindings within the tab manager window:
### File tree
SpaceVim uses vimfiler as the default file tree, the default key binding is `<F3>`.
SpaceVim uses `nerdtree` as the default file tree, the default key binding is `<F3>`.
SpaceVim also provides `SPC f t` and `SPC f T` to open the file tree.
To change the filemanager plugin:
@ -833,13 +833,15 @@ To change the filemanager plugin:
[options]
# file manager plugins supported in SpaceVim:
# - nerdtree (default)
# - vimfiler
# - defx
filemanager = "defx"
# - vimfiler: you need to build the vimproc.vim in bundle/vimproc.vim directory
# - defx: requires +py3 feature
filemanager = "nerdtree"
```
VCS integration is supported, there will be a column status, this feature may make vimfiler slow, so it is not enabled by default.
To enable this feature, add `enable_vimfiler_gitstatus = true` to your custom configuration file. Here is a picture of this feature:
VCS integration is supported, there will be a column status,
this feature may make filetree slow, so it is not enabled by default.
To enable this feature, add `enable_filetree_gitstatus = true`
to your custom configuration file. Here is a picture of this feature:
![file-tree](https://user-images.githubusercontent.com/13142418/80496111-5065b380-899b-11ea-95c7-02af4d304aaf.png)