├── .github └── workflows │ ├── neovim.yml │ ├── reviewdog.yml │ └── vim.yml ├── .gitignore ├── LICENSE ├── README.md ├── autoload ├── gina.vim ├── gina │ ├── action.vim │ ├── action │ │ ├── blame.vim │ │ ├── branch.vim │ │ ├── browse.vim │ │ ├── changes.vim │ │ ├── chaperon.vim │ │ ├── commit.vim │ │ ├── compare.vim │ │ ├── diff.vim │ │ ├── edit.vim │ │ ├── export.vim │ │ ├── index.vim │ │ ├── ls.vim │ │ ├── patch.vim │ │ ├── show.vim │ │ ├── stash.vim │ │ ├── tag.vim │ │ └── yank.vim │ ├── command.vim │ ├── command │ │ ├── _events.vim │ │ ├── _raw.vim │ │ ├── _shell.vim │ │ ├── blame.vim │ │ ├── blame │ │ │ ├── formatter.vim │ │ │ └── pipe.vim │ │ ├── branch.vim │ │ ├── browse.vim │ │ ├── cd.vim │ │ ├── changes.vim │ │ ├── chaperon.vim │ │ ├── commit.vim │ │ ├── compare.vim │ │ ├── diff.vim │ │ ├── edit.vim │ │ ├── grep.vim │ │ ├── lcd.vim │ │ ├── log.vim │ │ ├── ls.vim │ │ ├── patch.vim │ │ ├── qrep.vim │ │ ├── reflog.vim │ │ ├── show.vim │ │ ├── stash.vim │ │ ├── stash │ │ │ ├── list.vim │ │ │ └── show.vim │ │ ├── status.vim │ │ ├── tag.vim │ │ └── tag │ │ │ └── edit.vim │ ├── complete │ │ ├── commit.vim │ │ ├── common.vim │ │ ├── filename.vim │ │ ├── range.vim │ │ ├── stash.vim │ │ └── tag.vim │ ├── component │ │ ├── repo.vim │ │ ├── status.vim │ │ └── traffic.vim │ ├── core.vim │ ├── core │ │ ├── args.vim │ │ ├── askpass.vim │ │ ├── buffer.vim │ │ ├── console.vim │ │ ├── diffjump.vim │ │ ├── emitter.vim │ │ ├── locator.vim │ │ ├── meta.vim │ │ ├── options.vim │ │ ├── path.vim │ │ ├── repo.vim │ │ ├── revelator.vim │ │ ├── spinner.vim │ │ ├── timestamper.vim │ │ ├── tracker.vim │ │ ├── treeish.vim │ │ └── writer.vim │ ├── custom.vim │ ├── custom │ │ ├── action.vim │ │ ├── command.vim │ │ └── mapping.vim │ ├── process.vim │ ├── process │ │ └── pipe.vim │ └── util.vim └── vital │ ├── __gina__ │ ├── Action.vim │ ├── Argument.vim │ ├── Git.vim │ ├── Options.vim │ └── System │ │ └── Store.vim │ ├── _gina.vim │ ├── _gina │ ├── App │ │ ├── Emitter.vim │ │ └── Revelator.vim │ ├── Bitwise.vim │ ├── Config.vim │ ├── Data │ │ ├── Dict.vim │ │ ├── List.vim │ │ ├── String.vim │ │ └── String │ │ │ └── Formatter.vim │ ├── DateTime.vim │ ├── Prelude.vim │ ├── Process.vim │ ├── System │ │ ├── Cache │ │ │ ├── Base.vim │ │ │ └── Memory.vim │ │ ├── File.vim │ │ ├── Filepath.vim │ │ ├── Job.vim │ │ └── Job │ │ │ ├── Neovim.vim │ │ │ └── Vim.vim │ ├── Text │ │ └── INI.vim │ └── Vim │ │ ├── Buffer.vim │ │ ├── Buffer │ │ ├── ANSI.vim │ │ ├── Group.vim │ │ ├── Opener.vim │ │ └── Writer.vim │ │ ├── BufferManager.vim │ │ ├── Console.vim │ │ ├── Guard.vim │ │ ├── Highlight.vim │ │ ├── Type.vim │ │ └── Window.vim │ ├── gina.vim │ └── gina.vital ├── doc ├── Vital │ └── Argument.txt ├── gina-develop.txt └── gina.txt ├── ftplugin ├── gina-blame.vim ├── gina-branch.vim ├── gina-changes.vim ├── gina-commit.vim ├── gina-grep.vim ├── gina-log.vim ├── gina-ls.vim ├── gina-reflog.vim ├── gina-stash-list.vim ├── gina-stash-show.vim ├── gina-status.vim ├── gina-tag.vim └── gitrebase │ └── gina.vim ├── plugin └── gina.vim ├── scripts ├── askpass.mac └── askpass.zenity ├── syntax ├── gina-_events.vim ├── gina-blame.vim ├── gina-branch.vim ├── gina-changes.vim ├── gina-commit.vim ├── gina-grep.vim ├── gina-log.vim ├── gina-reflog.vim ├── gina-stash-show.vim ├── gina-status.vim └── gina-tag.vim └── test ├── .themisrc ├── gina ├── _testdata │ ├── cp1250-encoding.txt │ ├── cp932-encoding.txt │ ├── euc-jp-encoding.txt │ ├── pluscmd.txt │ ├── sjis-encoding.txt │ └── utf-8-encoding.txt ├── action │ └── index.vimspec ├── command │ ├── branch.vimspec │ ├── browse.vimspec │ ├── cd.vimspec │ ├── changes.vimspec │ ├── chaperon.vimspec │ ├── commit.vimspec │ ├── compare.vimspec │ ├── diff.vimspec │ ├── edit.vimspec │ ├── grep.vimspec │ ├── lcd.vimspec │ ├── log.vimspec │ ├── ls.vimspec │ ├── patch.vimspec │ ├── qrep.vimspec │ ├── reflog.vimspec │ ├── show.vimspec │ ├── stash │ │ ├── list.vimspec │ │ └── show.vimspec │ ├── status.vimspec │ └── tag.vimspec ├── core.vimspec ├── core │ ├── args.vimspec │ ├── buffer.vimspec │ ├── diffjump.vimspec │ ├── emitter.vimspec │ ├── meta.vimspec │ ├── path.vimspec │ ├── repo.vimspec │ └── treeish.vimspec ├── custom.vimspec ├── custom │ ├── action.vimspec │ ├── command.vimspec │ └── mapping.vimspec ├── process.vimspec └── util.vimspec ├── run_themis.sh ├── slit.vim └── vital ├── Action.vimspec ├── Argument.vimspec ├── Git.vimspec ├── System └── Store.vimspec ├── Vim └── Highlight.vimspec └── _testdata └── Vim └── Buffer └── Writer ├── cp1250-encoding.txt ├── cp932-encoding.txt ├── euc-jp-encoding.txt ├── sjis-encoding.txt └── utf-8-encoding.txt /.github/workflows/neovim.yml: -------------------------------------------------------------------------------- 1 | name: neovim 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build: 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | os: 17 | - macos-latest 18 | - windows-latest 19 | - ubuntu-latest 20 | version: 21 | - nightly 22 | - stable 23 | - v0.4.3 24 | runs-on: ${{ matrix.os }} 25 | steps: 26 | - uses: actions/checkout@v2 27 | - name: Install Neovim 28 | uses: rhysd/action-setup-vim@v1 29 | id: vim 30 | with: 31 | neovim: true 32 | version: ${{ matrix.version }} 33 | - name: Download test runner 34 | uses: actions/checkout@v2 35 | with: 36 | repository: thinca/vim-themis 37 | path: vim-themis 38 | - name: Prepare environment 39 | run: | 40 | git config --global user.name "github-action" 41 | git config --global user.email "github-action@example.com" 42 | - name: Run tests 43 | env: 44 | CI: 1 45 | THEMIS_VIM: ${{ steps.vim.outputs.executable }} 46 | # XXX: 47 | # Overwrite %TMP% to point a correct temp directory. 48 | # Note that %TMP% only affects value of 'tempname()' in Windows. 49 | # https://github.community/t5/GitHub-Actions/TEMP-is-broken-on-Windows/m-p/30432#M427 50 | TMP: 'C:\Users\runneradmin\AppData\Local\Temp' 51 | run: | 52 | ./vim-themis/bin/themis 53 | -------------------------------------------------------------------------------- /.github/workflows/reviewdog.yml: -------------------------------------------------------------------------------- 1 | name: reviewdog 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | vimlint: 13 | name: runner / vint 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: vint 18 | uses: reviewdog/action-vint@v1 19 | with: 20 | github_token: ${{ secrets.github_token }} 21 | level: error 22 | reporter: github-pr-review 23 | -------------------------------------------------------------------------------- /.github/workflows/vim.yml: -------------------------------------------------------------------------------- 1 | name: vim 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build: 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | os: 17 | - macos-latest 18 | - windows-latest 19 | - ubuntu-latest 20 | version: 21 | - nightly 22 | - v8.2.0235 23 | - v8.1.2424 24 | runs-on: ${{ matrix.os }} 25 | steps: 26 | - uses: actions/checkout@v2 27 | - name: Install Vim 28 | uses: rhysd/action-setup-vim@v1 29 | id: vim 30 | with: 31 | neovim: false 32 | version: ${{ matrix.version }} 33 | - name: Download test runner 34 | uses: actions/checkout@v2 35 | with: 36 | repository: thinca/vim-themis 37 | path: vim-themis 38 | - name: Prepare environment 39 | run: | 40 | git config --global user.name "github-action" 41 | git config --global user.email "github-action@example.com" 42 | - name: Run tests 43 | env: 44 | CI: 1 45 | THEMIS_VIM: ${{ steps.vim.outputs.executable }} 46 | # XXX: 47 | # Overwrite %TMP% to point a correct temp directory. 48 | # Note that %TMP% only affects value of 'tempname()' in Windows. 49 | # https://github.community/t5/GitHub-Actions/TEMP-is-broken-on-Windows/m-p/30432#M427 50 | TMP: 'C:\Users\runneradmin\AppData\Local\Temp' 51 | run: | 52 | ./vim-themis/bin/themis 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | doc/tags 2 | release 3 | coverage.xml 4 | .coverage.covimerage 5 | .profile 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2016 Alisue, hashnote.net 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /autoload/gina.vim: -------------------------------------------------------------------------------- 1 | let s:Config = vital#gina#import('Config') 2 | let s:Path = vital#gina#import('System.Filepath') 3 | 4 | 5 | function! gina#config(sfile, options) abort 6 | return s:Config.define(s:translate(a:sfile), a:options) 7 | endfunction 8 | 9 | 10 | " Private -------------------------------------------------------------------- 11 | function! s:translate(sfile) abort 12 | let path = s:Path.unixpath(a:sfile) 13 | let name = matchstr(path, 'autoload/\zs\%(gina\.vim\|gina/.*\)$') 14 | let name = substitute(name, '\.vim$', '', '') 15 | let name = substitute(name, '/', '#', 'g') 16 | let name = substitute(name, '\%(^#\|#$\)', '', 'g') 17 | return 'g:' . name 18 | endfunction 19 | 20 | 21 | " Default variable ----------------------------------------------------------- 22 | call gina#config(expand(''), { 23 | \ 'test': 0, 24 | \ 'debug': -1, 25 | \ 'develop': 1, 26 | \ 'complete_threshold': 30, 27 | \}) 28 | -------------------------------------------------------------------------------- /autoload/gina/action.vim: -------------------------------------------------------------------------------- 1 | scriptencoding utf-8 2 | let s:Action = vital#gina#import('Action') 3 | let s:Action.name = 'gina' 4 | let s:Highlight = vital#gina#import('Vim.Highlight') 5 | 6 | 7 | 8 | function! gina#action#get(...) abort 9 | return call(s:Action.get, a:000, s:Action) 10 | endfunction 11 | 12 | function! gina#action#attach(...) abort 13 | return call(s:Action.attach, a:000, s:Action) 14 | endfunction 15 | 16 | function! gina#action#include(scheme) abort 17 | let binder = s:Action.get() 18 | if binder is# v:null 19 | " TODO: raise an exception 20 | return 21 | endif 22 | let scheme = substitute(a:scheme, '-', '_', 'g') 23 | try 24 | return call( 25 | \ printf('gina#action#%s#define', scheme), 26 | \ [binder] 27 | \) 28 | catch /^Vim\%((\a\+)\)\=:E117: [^:]\+: gina#action#[^#]\+#define/ 29 | call gina#core#console#debug(v:exception) 30 | call gina#core#console#debug(v:throwpoint) 31 | endtry 32 | throw gina#core#revelator#error(printf( 33 | \ 'No action script "gina/action/%s.vim" is found', 34 | \ a:scheme, 35 | \)) 36 | endfunction 37 | 38 | function! gina#action#alias(...) abort 39 | let binder = s:Action.get() 40 | if binder is# v:null 41 | " TODO: raise an exception 42 | return 43 | endif 44 | return gina#core#revelator#call(binder.alias, a:000, binder) 45 | endfunction 46 | 47 | function! gina#action#shorten(action_scheme, ...) abort 48 | let excludes = get(a:000, 0, []) 49 | let binder = s:Action.get() 50 | if binder is# v:null 51 | " TODO: raise an exception 52 | return 53 | endif 54 | let action_scheme = substitute(a:action_scheme, '-', '_', 'g') 55 | let names = filter( 56 | \ keys(binder.actions), 57 | \ 'v:val =~# ''^'' . action_scheme . '':''' 58 | \) 59 | for name in filter(names, 'index(excludes, v:val) == -1') 60 | call binder.alias(matchstr(name, '^' . action_scheme . ':\zs.*'), name) 61 | endfor 62 | endfunction 63 | 64 | function! gina#action#call(...) abort 65 | let binder = s:Action.get() 66 | if binder is# v:null 67 | " TODO: raise an exception 68 | return 69 | endif 70 | return gina#core#revelator#call(binder.call, a:000, binder) 71 | endfunction 72 | 73 | function! gina#action#candidates(...) abort 74 | let binder = s:Action.get() 75 | if binder is# v:null 76 | " TODO: raise an exception 77 | return 78 | endif 79 | return gina#core#revelator#call(binder._get_candidates, a:000, binder) 80 | endfunction 81 | 82 | 83 | " Config --------------------------------------------------------------------- 84 | call gina#config(expand(''), { 85 | \ 'mark_sign_text': '|', 86 | \}) 87 | 88 | 89 | " Highlight ------------------------------------------------------------------ 90 | function! s:define_highlihghts() abort 91 | silent let bg = s:Highlight.get('SignColumn') 92 | silent let fg = s:Highlight.get('Title') 93 | call s:Highlight.set({ 94 | \ 'name': 'GinaActionMarkSelected', 95 | \ 'attrs': { 96 | \ 'cterm': 'bold', 97 | \ 'ctermfg': get(fg.attrs, 'ctermfg', '1'), 98 | \ 'ctermbg': get(bg.attrs, 'ctermbg', 'NONE'), 99 | \ 'gui': 'bold', 100 | \ 'guifg': get(fg.attrs, 'guifg', '#ff0000'), 101 | \ 'guibg': get(bg.attrs, 'guibg', 'NONE'), 102 | \ } 103 | \}, { 104 | \ 'default': 1, 105 | \}) 106 | highlight link VitalActionMarkSelectedLine Search 107 | highlight link VitalActionMarkSelected GinaActionMarkSelected 108 | let s:Action.mark_sign_text = g:gina#action#mark_sign_text 109 | endfunction 110 | 111 | augroup gina_action_internal 112 | autocmd! * 113 | autocmd ColorScheme * call s:define_highlihghts() 114 | augroup END 115 | 116 | call s:define_highlihghts() 117 | -------------------------------------------------------------------------------- /autoload/gina/action/browse.vim: -------------------------------------------------------------------------------- 1 | function! gina#action#browse#define(binder) abort 2 | call a:binder.define('browse', function('s:on_browse'), { 3 | \ 'description': 'Open a remote url in a system browser', 4 | \ 'mapping_mode': 'n', 5 | \ 'requirements': [], 6 | \ 'options': {}, 7 | \ 'use_marks': 0, 8 | \ 'clear_marks': 0, 9 | \}) 10 | call a:binder.define('browse:exact', function('s:on_browse'), { 11 | \ 'hidden': 1, 12 | \ 'description': 'Open a remote url in a system browser', 13 | \ 'mapping_mode': 'n', 14 | \ 'requirements': [], 15 | \ 'options': { 'exact': 1 }, 16 | \ 'use_marks': 0, 17 | \ 'clear_marks': 0, 18 | \}) 19 | call a:binder.define('browse:yank', function('s:on_browse'), { 20 | \ 'description': 'Copy a remote url', 21 | \ 'mapping_mode': 'n', 22 | \ 'requirements': [], 23 | \ 'options': { 'yank': 1 }, 24 | \ 'use_marks': 0, 25 | \ 'clear_marks': 0, 26 | \}) 27 | call a:binder.define('browse:yank:exact', function('s:on_browse'), { 28 | \ 'hidden': 1, 29 | \ 'description': 'Copy a remote url', 30 | \ 'mapping_mode': 'n', 31 | \ 'requirements': [], 32 | \ 'options': { 'yank': 1, 'exact': 1 }, 33 | \ 'use_marks': 0, 34 | \ 'clear_marks': 0, 35 | \}) 36 | endfunction 37 | 38 | 39 | " Private -------------------------------------------------------------------- 40 | function! s:on_browse(candidates, options) abort 41 | if empty(a:candidates) 42 | return 43 | endif 44 | let options = extend({ 45 | \ 'exact': 0, 46 | \ 'yank': 0, 47 | \}, a:options) 48 | let candidate = a:candidates[0] 49 | let treeish = gina#core#treeish#build( 50 | \ gina#util#get(candidate, 'rev'), 51 | \ gina#util#get(candidate, 'path', v:null), 52 | \) 53 | execute printf( 54 | \ '%s Gina browse %s %s %s', 55 | \ options.mods, 56 | \ options.exact ? '--exact' : '', 57 | \ options.yank ? '--yank' : '', 58 | \ gina#util#shellescape(treeish), 59 | \) 60 | endfunction 61 | -------------------------------------------------------------------------------- /autoload/gina/action/chaperon.vim: -------------------------------------------------------------------------------- 1 | function! gina#action#chaperon#define(binder) abort 2 | let params = { 3 | \ 'description': 'Open three buffers to solve conflict', 4 | \ 'mapping_mode': 'nv', 5 | \ 'requirements': ['path', 'sign'], 6 | \} 7 | call a:binder.define('chaperon', function('s:on_chaperon'), extend({ 8 | \ 'options': {}, 9 | \}, params)) 10 | call a:binder.define('chaperon:split', function('s:on_chaperon'), extend({ 11 | \ 'hidden': 1, 12 | \ 'options': {'opener': 'new'}, 13 | \}, params)) 14 | call a:binder.define('chaperon:vsplit', function('s:on_chaperon'), extend({ 15 | \ 'hidden': 1, 16 | \ 'options': {'opener': 'vnew'}, 17 | \}, params)) 18 | call a:binder.define('chaperon:tab', function('s:on_chaperon'), extend({ 19 | \ 'hidden': 1, 20 | \ 'options': {'opener': 'tabedit'}, 21 | \}, params)) 22 | " Alias 23 | call a:binder.alias('chaperon:above', 'leftabove chaperon:split') 24 | call a:binder.alias('chaperon:below', 'belowright chaperon:split') 25 | call a:binder.alias('chaperon:left', 'leftabove chaperon:vsplit') 26 | call a:binder.alias('chaperon:right', 'belowright chaperon:vsplit') 27 | call a:binder.alias('chaperon:top', 'topleft chaperon:split') 28 | call a:binder.alias('chaperon:bottom', 'botright chaperon:split') 29 | call a:binder.alias('chaperon:leftest', 'topleft chaperon:vsplit') 30 | call a:binder.alias('chaperon:rightest', 'botright chaperon:vsplit') 31 | endfunction 32 | 33 | 34 | " Private -------------------------------------------------------------------- 35 | function! s:on_chaperon(candidates, options) abort 36 | let candidates = filter(copy(a:candidates), 'v:val.sign ==# ''UU''') 37 | if empty(candidates) 38 | return 39 | endif 40 | let options = extend({ 41 | \ 'opener': '', 42 | \}, a:options) 43 | for candidate in candidates 44 | execute printf( 45 | \ '%s Gina chaperon %s %s %s %s', 46 | \ options.mods, 47 | \ gina#util#shellescape(options.opener, '--opener='), 48 | \ gina#util#shellescape(get(candidate, 'line'), '--line='), 49 | \ gina#util#shellescape(get(candidate, 'col'), '--col='), 50 | \ gina#util#shellescape(candidate.path), 51 | \) 52 | endfor 53 | endfunction 54 | -------------------------------------------------------------------------------- /autoload/gina/action/compare.vim: -------------------------------------------------------------------------------- 1 | function! gina#action#compare#define(binder) abort 2 | let params = { 3 | \ 'description': 'Open two buffers to compare differences', 4 | \ 'mapping_mode': 'nv', 5 | \ 'requirements': ['path'], 6 | \} 7 | call a:binder.define('compare', function('s:on_compare'), extend({ 8 | \ 'options': {}, 9 | \}, params)) 10 | call a:binder.define('compare:split', function('s:on_compare'), extend({ 11 | \ 'hidden': 1, 12 | \ 'options': {'opener': 'new'}, 13 | \}, params)) 14 | call a:binder.define('compare:vsplit', function('s:on_compare'), extend({ 15 | \ 'hidden': 1, 16 | \ 'options': {'opener': 'vnew'}, 17 | \}, params)) 18 | call a:binder.define('compare:tab', function('s:on_compare'), extend({ 19 | \ 'hidden': 1, 20 | \ 'options': {'opener': 'tabedit'}, 21 | \}, params)) 22 | " Alias 23 | call a:binder.alias('compare:above', 'leftabove compare:split') 24 | call a:binder.alias('compare:below', 'belowright compare:split') 25 | call a:binder.alias('compare:left', 'leftabove compare:vsplit') 26 | call a:binder.alias('compare:right', 'belowright compare:vsplit') 27 | call a:binder.alias('compare:top', 'topleft compare:split') 28 | call a:binder.alias('compare:bottom', 'botright compare:split') 29 | call a:binder.alias('compare:leftest', 'topleft compare:vsplit') 30 | call a:binder.alias('compare:rightest', 'botright compare:vsplit') 31 | endfunction 32 | 33 | 34 | " Private -------------------------------------------------------------------- 35 | function! s:on_compare(candidates, options) abort 36 | if empty(a:candidates) 37 | return 38 | endif 39 | let options = extend({ 40 | \ 'opener': '', 41 | \}, a:options) 42 | for candidate in a:candidates 43 | let cached = gina#util#get(candidate, 'sign', '!!') !~# '^\%(??\|!!\|.\w\)$' 44 | let treeish = gina#core#treeish#build( 45 | \ gina#util#get(candidate, 'rev'), 46 | \ candidate.path, 47 | \) 48 | execute printf( 49 | \ '%s Gina compare %s %s %s %s %s', 50 | \ options.mods, 51 | \ cached ? '--cached' : '', 52 | \ gina#util#shellescape(options.opener, '--opener='), 53 | \ gina#util#shellescape(get(candidate, 'line'), '--line='), 54 | \ gina#util#shellescape(get(candidate, 'col'), '--col='), 55 | \ gina#util#shellescape(treeish), 56 | \) 57 | endfor 58 | endfunction 59 | -------------------------------------------------------------------------------- /autoload/gina/action/diff.vim: -------------------------------------------------------------------------------- 1 | function! gina#action#diff#define(binder) abort 2 | let params = { 3 | \ 'description': 'Open an unified-diff', 4 | \ 'mapping_mode': 'nv', 5 | \ 'requirements': [], 6 | \} 7 | call a:binder.define('diff', function('s:on_diff'), extend({ 8 | \ 'options': {}, 9 | \}, params)) 10 | call a:binder.define('diff:split', function('s:on_diff'), extend({ 11 | \ 'hidden': 1, 12 | \ 'options': {'opener': 'new'}, 13 | \}, params)) 14 | call a:binder.define('diff:vsplit', function('s:on_diff'), extend({ 15 | \ 'hidden': 1, 16 | \ 'options': {'opener': 'vnew'}, 17 | \}, params)) 18 | call a:binder.define('diff:tab', function('s:on_diff'), extend({ 19 | \ 'hidden': 1, 20 | \ 'options': {'opener': 'tabedit'}, 21 | \}, params)) 22 | call a:binder.define('diff:preview', function('s:on_diff'), extend({ 23 | \ 'hidden': 1, 24 | \ 'options': {'opener': 'pedit'}, 25 | \}, params)) 26 | " Alias 27 | call a:binder.alias('diff:above', 'leftabove diff:split') 28 | call a:binder.alias('diff:below', 'belowright diff:split') 29 | call a:binder.alias('diff:left', 'leftabove diff:vsplit') 30 | call a:binder.alias('diff:right', 'belowright diff:vsplit') 31 | call a:binder.alias('diff:top', 'topleft diff:split') 32 | call a:binder.alias('diff:bottom', 'botright diff:split') 33 | call a:binder.alias('diff:leftest', 'topleft diff:vsplit') 34 | call a:binder.alias('diff:rightest', 'botright diff:vsplit') 35 | call a:binder.alias('diff:preview:top', 'topleft diff:preview') 36 | call a:binder.alias('diff:preview:bottom', 'botright diff:preview') 37 | endfunction 38 | 39 | 40 | " Private -------------------------------------------------------------------- 41 | function! s:on_diff(candidates, options) abort 42 | if empty(a:candidates) 43 | return 44 | endif 45 | let options = extend({ 46 | \ 'opener': '', 47 | \}, a:options) 48 | for candidate in a:candidates 49 | let cached = get(candidate, 'sign', '!!') !~# '^\%(??\|!!\|.\w\)$' 50 | let treeish = gina#core#treeish#build( 51 | \ gina#util#get(candidate, 'rev'), 52 | \ gina#util#get(candidate, 'path', v:null), 53 | \) 54 | execute printf( 55 | \ '%s Gina diff %s %s %s -- %s', 56 | \ options.mods, 57 | \ cached ? '--cached' : '', 58 | \ gina#util#shellescape(options.opener, '--opener='), 59 | \ gina#util#shellescape(treeish), 60 | \ gina#util#shellescape(gina#util#get(candidate, 'residual')), 61 | \) 62 | endfor 63 | endfunction 64 | -------------------------------------------------------------------------------- /autoload/gina/action/edit.vim: -------------------------------------------------------------------------------- 1 | function! gina#action#edit#define(binder) abort 2 | let params = { 3 | \ 'description': 'Edit a content in a working tree', 4 | \ 'mapping_mode': 'nv', 5 | \ 'requirements': ['path'], 6 | \} 7 | call a:binder.define('edit', function('s:on_edit'), extend({ 8 | \ 'options': {}, 9 | \}, params)) 10 | call a:binder.define('edit:split', function('s:on_edit'), extend({ 11 | \ 'hidden': 1, 12 | \ 'options': {'opener': 'new'}, 13 | \}, params)) 14 | call a:binder.define('edit:vsplit', function('s:on_edit'), extend({ 15 | \ 'hidden': 1, 16 | \ 'options': {'opener': 'vnew'}, 17 | \}, params)) 18 | call a:binder.define('edit:tab', function('s:on_edit'), extend({ 19 | \ 'hidden': 1, 20 | \ 'options': {'opener': 'tabedit'}, 21 | \}, params)) 22 | call a:binder.define('edit:preview', function('s:on_edit'), extend({ 23 | \ 'hidden': 1, 24 | \ 'options': {'opener': 'pedit'}, 25 | \}, params)) 26 | " Alias 27 | call a:binder.alias('edit:above', 'leftabove edit:split') 28 | call a:binder.alias('edit:below', 'belowright edit:split') 29 | call a:binder.alias('edit:left', 'leftabove edit:vsplit') 30 | call a:binder.alias('edit:right', 'belowright edit:vsplit') 31 | call a:binder.alias('edit:top', 'topleft edit:split') 32 | call a:binder.alias('edit:bottom', 'botright edit:split') 33 | call a:binder.alias('edit:leftest', 'topleft edit:vsplit') 34 | call a:binder.alias('edit:rightest', 'botright edit:vsplit') 35 | call a:binder.alias('edit:preview:top', 'topleft edit:preview') 36 | call a:binder.alias('edit:preview:bottom', 'botright edit:preview') 37 | endfunction 38 | 39 | 40 | " Private -------------------------------------------------------------------- 41 | function! s:on_edit(candidates, options) abort 42 | if empty(a:candidates) 43 | return 44 | endif 45 | let options = extend({ 46 | \ 'opener': '', 47 | \}, a:options) 48 | for candidate in a:candidates 49 | execute printf( 50 | \ '%s Gina edit %s %s %s %s', 51 | \ options.mods, 52 | \ gina#util#shellescape(options.opener, '--opener='), 53 | \ gina#util#shellescape(get(candidate, 'line'), '--line='), 54 | \ gina#util#shellescape(get(candidate, 'col'), '--col='), 55 | \ gina#util#shellescape(candidate.path), 56 | \) 57 | endfor 58 | endfunction 59 | -------------------------------------------------------------------------------- /autoload/gina/action/export.vim: -------------------------------------------------------------------------------- 1 | let s:Path = vital#gina#import('System.Filepath') 2 | let s:String = vital#gina#import('Data.String') 3 | 4 | 5 | function! gina#action#export#define(binder) abort 6 | call a:binder.define('export:quickfix', function('s:on_quickfix'), { 7 | \ 'description': 'Create a new quickfix list with selected candidates', 8 | \ 'mapping_mode': 'nv', 9 | \ 'requirements': ['path', 'word'], 10 | \ 'options': {}, 11 | \}) 12 | call a:binder.define('export:quickfix:add', function('s:on_quickfix'), { 13 | \ 'hidden': 1, 14 | \ 'description': 'Add selected candidates to an existing quickfix list', 15 | \ 'mapping_mode': 'nv', 16 | \ 'requirements': ['path', 'word'], 17 | \ 'options': { 'action': 'a' }, 18 | \}) 19 | call a:binder.define('export:quickfix:replace', function('s:on_quickfix'), { 20 | \ 'hidden': 1, 21 | \ 'description': 'Replace an existing quickfix list with selected candidates', 22 | \ 'mapping_mode': 'nv', 23 | \ 'requirements': ['path', 'word'], 24 | \ 'options': { 'action': 'r' }, 25 | \}) 26 | endfunction 27 | 28 | 29 | " Private -------------------------------------------------------------------- 30 | function! s:on_quickfix(candidates, options) abort dict 31 | let options = extend({ 32 | \ 'action': ' ', 33 | \}, a:options) 34 | let git = gina#core#get_or_fail() 35 | call setqflist( 36 | \ map(copy(a:candidates), 's:to_quickfix(git, v:val)'), 37 | \ options.action, 38 | \) 39 | endfunction 40 | 41 | function! s:to_quickfix(git, candidate) abort 42 | let abspath = gina#core#repo#abspath(a:git, a:candidate.path) 43 | return { 44 | \ 'filename': s:Path.realpath(abspath), 45 | \ 'lnum': get(a:candidate, 'line', 1), 46 | \ 'col': get(a:candidate, 'col', 1), 47 | \ 'text': s:String.remove_ansi_sequences(a:candidate.word), 48 | \} 49 | endfunction 50 | -------------------------------------------------------------------------------- /autoload/gina/action/ls.vim: -------------------------------------------------------------------------------- 1 | function! gina#action#ls#define(binder) abort 2 | let params = { 3 | \ 'description': 'List files/directories of the repository on a particular commit', 4 | \ 'mapping_mode': 'nv', 5 | \ 'requirements': ['rev'], 6 | \} 7 | call a:binder.define('ls', function('s:on_ls'), extend({ 8 | \ 'options': {}, 9 | \}, params)) 10 | call a:binder.define('ls:split', function('s:on_ls'), extend({ 11 | \ 'hidden': 1, 12 | \ 'options': {'opener': 'new'}, 13 | \}, params)) 14 | call a:binder.define('ls:vsplit', function('s:on_ls'), extend({ 15 | \ 'hidden': 1, 16 | \ 'options': {'opener': 'vnew'}, 17 | \}, params)) 18 | call a:binder.define('ls:tab', function('s:on_ls'), extend({ 19 | \ 'hidden': 1, 20 | \ 'options': {'opener': 'tabedit'}, 21 | \}, params)) 22 | call a:binder.define('ls:preview', function('s:on_ls'), extend({ 23 | \ 'hidden': 1, 24 | \ 'options': {'opener': 'pedit'}, 25 | \}, params)) 26 | " Alias 27 | call a:binder.alias('ls:above', 'leftabove ls:split') 28 | call a:binder.alias('ls:below', 'belowright ls:split') 29 | call a:binder.alias('ls:left', 'leftabove ls:vsplit') 30 | call a:binder.alias('ls:right', 'belowright ls:vsplit') 31 | call a:binder.alias('ls:top', 'topleft ls:split') 32 | call a:binder.alias('ls:bottom', 'botright ls:split') 33 | call a:binder.alias('ls:leftest', 'topleft ls:vsplit') 34 | call a:binder.alias('ls:rightest', 'botright ls:vsplit') 35 | endfunction 36 | 37 | 38 | " Private -------------------------------------------------------------------- 39 | function! s:on_ls(candidates, options) abort 40 | if empty(a:candidates) 41 | return 42 | endif 43 | let options = extend({ 44 | \ 'opener': '', 45 | \}, a:options) 46 | for candidate in a:candidates 47 | execute printf( 48 | \ '%s Gina ls %s %s', 49 | \ options.mods, 50 | \ gina#util#shellescape(options.opener, '--opener='), 51 | \ gina#util#shellescape(candidate.rev), 52 | \) 53 | endfor 54 | endfunction 55 | 56 | -------------------------------------------------------------------------------- /autoload/gina/action/patch.vim: -------------------------------------------------------------------------------- 1 | function! gina#action#patch#define(binder) abort 2 | let params = { 3 | \ 'description': 'Open three buffers to patch changes to an index', 4 | \ 'mapping_mode': 'nv', 5 | \ 'requirements': ['path'], 6 | \} 7 | call a:binder.define('patch', function('s:on_patch'), extend({ 8 | \ 'options': {}, 9 | \}, params)) 10 | call a:binder.define('patch:split', function('s:on_patch'), extend({ 11 | \ 'hidden': 1, 12 | \ 'options': {'opener': 'new'}, 13 | \}, params)) 14 | call a:binder.define('patch:vsplit', function('s:on_patch'), extend({ 15 | \ 'hidden': 1, 16 | \ 'options': {'opener': 'vnew'}, 17 | \}, params)) 18 | call a:binder.define('patch:tab', function('s:on_patch'), extend({ 19 | \ 'hidden': 1, 20 | \ 'options': {'opener': 'tabedit'}, 21 | \}, params)) 22 | call a:binder.define('patch:oneside', function('s:on_patch'), extend({ 23 | \ 'options': {'oneside': 1}, 24 | \}, params)) 25 | call a:binder.define('patch:oneside:split', function('s:on_patch'), extend({ 26 | \ 'hidden': 1, 27 | \ 'options': {'oneside': 1, 'opener': 'new'}, 28 | \}, params)) 29 | call a:binder.define('patch:oneside:vsplit', function('s:on_patch'), extend({ 30 | \ 'hidden': 1, 31 | \ 'options': {'oneside': 1, 'opener': 'vnew'}, 32 | \}, params)) 33 | call a:binder.define('patch:oneside:tab', function('s:on_patch'), extend({ 34 | \ 'hidden': 1, 35 | \ 'options': {'oneside': 1, 'opener': 'tabedit'}, 36 | \}, params)) 37 | " Alias 38 | call a:binder.alias('patch:above', 'leftabove patch:split') 39 | call a:binder.alias('patch:below', 'belowright patch:split') 40 | call a:binder.alias('patch:left', 'leftabove patch:vsplit') 41 | call a:binder.alias('patch:right', 'belowright patch:vsplit') 42 | call a:binder.alias('patch:top', 'topleft patch:split') 43 | call a:binder.alias('patch:bottom', 'botright patch:split') 44 | call a:binder.alias('patch:leftest', 'topleft patch:vsplit') 45 | call a:binder.alias('patch:rightest', 'botright patch:vsplit') 46 | call a:binder.alias('patch:oneside:above', 'leftabove patch:oneside:split') 47 | call a:binder.alias('patch:oneside:below', 'belowright patch:oneside:split') 48 | call a:binder.alias('patch:oneside:left', 'leftabove patch:oneside:vsplit') 49 | call a:binder.alias('patch:oneside:right', 'belowright patch:oneside:vsplit') 50 | call a:binder.alias('patch:oneside:top', 'topleft patch:oneside:split') 51 | call a:binder.alias('patch:oneside:bottom', 'botright patch:oneside:split') 52 | call a:binder.alias('patch:oneside:leftest', 'topleft patch:oneside:vsplit') 53 | call a:binder.alias('patch:oneside:rightest', 'botright patch:oneside:vsplit') 54 | endfunction 55 | 56 | 57 | " Private -------------------------------------------------------------------- 58 | function! s:on_patch(candidates, options) abort 59 | if empty(a:candidates) 60 | return 61 | endif 62 | let options = extend({ 63 | \ 'opener': '', 64 | \ 'oneside': 0, 65 | \}, a:options) 66 | for candidate in a:candidates 67 | execute printf( 68 | \ '%s Gina patch %s %s %s %s %s', 69 | \ options.mods, 70 | \ options.oneside ? '--oneside' : '', 71 | \ gina#util#shellescape(options.opener, '--opener='), 72 | \ gina#util#shellescape(get(candidate, 'line'), '--line='), 73 | \ gina#util#shellescape(get(candidate, 'col'), '--col='), 74 | \ gina#util#shellescape(candidate.path), 75 | \) 76 | endfor 77 | endfunction 78 | -------------------------------------------------------------------------------- /autoload/gina/action/yank.vim: -------------------------------------------------------------------------------- 1 | function! gina#action#yank#define(binder) abort 2 | call a:binder.define('yank:rev', function('s:on_yank_rev'), { 3 | \ 'description': 'Yank the revision of a candidate under the cursor', 4 | \ 'mapping_mode': 'nv', 5 | \ 'requirements': ['rev'], 6 | \ 'use_marks': 0, 7 | \ 'clear_marks': 0, 8 | \}) 9 | call a:binder.define('yank:path', function('s:on_yank_path'), { 10 | \ 'description': 'Yank the path of a candidate under the cursor', 11 | \ 'mapping_mode': 'nv', 12 | \ 'requirements': ['path'], 13 | \ 'use_marks': 0, 14 | \ 'clear_marks': 0, 15 | \}) 16 | call a:binder.define('yank:treeish', function('s:on_yank_treeish'), { 17 | \ 'description': 'Yank the treeish (revision and path) of a candidate under the cursor', 18 | \ 'mapping_mode': 'nv', 19 | \ 'requirements': ['rev', 'path'], 20 | \ 'use_marks': 0, 21 | \ 'clear_marks': 0, 22 | \}) 23 | endfunction 24 | 25 | 26 | " Private -------------------------------------------------------------------- 27 | function! s:on_yank_rev(candidates, options) abort dict 28 | if empty(a:candidates) 29 | return 30 | endif 31 | let options = extend({}, a:options) 32 | let candidates = map( 33 | \ copy(a:candidates), 34 | \ 's:get_rev(v:val)' 35 | \) 36 | call gina#util#yank(join(candidates, "\n")) 37 | endfunction 38 | 39 | function! s:on_yank_path(candidates, options) abort dict 40 | if empty(a:candidates) 41 | return 42 | endif 43 | let options = extend({}, a:options) 44 | let candidates = map( 45 | \ copy(a:candidates), 46 | \ 's:get_path(v:val)' 47 | \) 48 | call gina#util#yank(join(candidates, "\n")) 49 | endfunction 50 | 51 | function! s:on_yank_treeish(candidates, options) abort dict 52 | if empty(a:candidates) 53 | return 54 | endif 55 | let options = extend({}, a:options) 56 | let candidates = map( 57 | \ copy(a:candidates), 58 | \ 's:get_treeish(v:val)' 59 | \) 60 | call gina#util#yank(join(candidates, "\n")) 61 | endfunction 62 | 63 | function! s:get_rev(candidate) abort 64 | let rev = gina#util#get(a:candidate, 'rev', v:null) 65 | return rev is v:null 66 | \ ? gina#core#buffer#param(bufname('%'), 'rev') 67 | \ : rev 68 | endfunction 69 | 70 | function! s:get_path(candidate) abort 71 | let path = gina#util#get(a:candidate, 'path', v:null) 72 | return path is v:null 73 | \ ? gina#core#buffer#param(bufname('%'), 'path') 74 | \ : path 75 | endfunction 76 | 77 | function! s:get_treeish(candidate) abort 78 | let rev = s:get_rev(a:candidate) 79 | let path = s:get_path(a:candidate) 80 | return gina#core#treeish#build(rev, path) 81 | endfunction 82 | -------------------------------------------------------------------------------- /autoload/gina/command.vim: -------------------------------------------------------------------------------- 1 | function! gina#command#call(bang, range, rargs, mods) abort 2 | if a:bang ==# '!' && a:rargs[0] ==# '!' 3 | return gina#command#call('', a:range, '_shell ' . a:rargs[1:], a:mods) 4 | elseif a:bang ==# '!' 5 | return gina#command#call('', a:range, '_raw ' . a:rargs, a:mods) 6 | endif 7 | let args = gina#core#args#new(a:rargs) 8 | if empty(args.params.scheme) 9 | " The scheme becomes empty when Gina-xxxxx is given 10 | call gina#core#console#error(printf( 11 | \ 'The "Gina%s" is not correct gina command. You may want ":Gina %s"', 12 | \ a:rargs, 13 | \ a:rargs[1:], 14 | \)) 15 | return 16 | endif 17 | try 18 | call gina#core#revelator#call( 19 | \ printf('gina#command#%s#call', args.params.scheme), 20 | \ [a:range, args, a:mods], 21 | \) 22 | return 23 | catch /^Vim\%((\a\+)\)\=:E117: [^:]\+: gina#command#[^#]\+#call/ 24 | call gina#core#console#debug(v:exception) 25 | call gina#core#console#debug(v:throwpoint) 26 | endtry 27 | return gina#command#call('', a:range, '_raw ' . a:rargs, a:mods) 28 | endfunction 29 | 30 | function! gina#command#complete(arglead, cmdline, cursorpos) abort 31 | if a:cmdline =~# '^.\{-}Gina!' 32 | return gina#command#complete( 33 | \ a:arglead, 34 | \ substitute(a:cmdline, '^\(.\{-}\)Gina!', '\1Gina _raw', ''), 35 | \ a:cursorpos, 36 | \) 37 | elseif a:cmdline =~# printf('^.\{-}Gina\s\+%s$', a:arglead) 38 | return gina#complete#common#command(a:arglead, a:cmdline, a:cursorpos) 39 | endif 40 | let cmdline = matchstr(a:cmdline, '^.\{-}Gina\s\+\zs.*') 41 | let scheme = matchstr(cmdline, '^\S\+') 42 | let scheme = substitute(scheme, '!$', '', '') 43 | let scheme = substitute(scheme, '\W', '_', 'g') 44 | try 45 | return gina#core#revelator#call( 46 | \ printf('gina#command#%s#complete', scheme), 47 | \ [a:arglead, cmdline, a:cursorpos], 48 | \)[:g:gina#complete_threshold] 49 | catch /^Vim\%((\a\+)\)\=:E117: [^:]\+: gina#command#[^#]\+#complete/ 50 | call gina#core#console#debug(v:exception) 51 | call gina#core#console#debug(v:throwpoint) 52 | endtry 53 | return gina#command#complete( 54 | \ a:arglead, 55 | \ substitute(a:cmdline, '^\(.\{-}\)Gina', '\1Gina _raw', ''), 56 | \ a:cursorpos, 57 | \)[:g:gina#complete_threshold] 58 | endfunction 59 | 60 | function! gina#command#scheme(sfile) abort 61 | let name = fnamemodify(a:sfile, ':t') 62 | let name = matchstr(name, '.*\ze\.vim') 63 | let scheme = substitute(name, '_', '-', 'g') 64 | return scheme 65 | endfunction 66 | -------------------------------------------------------------------------------- /autoload/gina/command/_shell.vim: -------------------------------------------------------------------------------- 1 | function! gina#command#_shell#call(range, args, mods) abort 2 | let git = gina#core#get() 3 | let args = gina#process#build_raw_args(git, s:build_args(git, a:args)) 4 | let cmdline = join(map(args, 's:shellescape(v:val)')) 5 | if has('nvim') 6 | tabnew 7 | execute ':terminal' cmdline 8 | augroup gina_command__shell_internal 9 | autocmd! * 10 | autocmd TermClose call gina#core#emitter#emit('modified:delay') 11 | augroup END 12 | else 13 | execute ':!' cmdline 14 | call gina#core#emitter#emit('modified:delay') 15 | endif 16 | endfunction 17 | 18 | 19 | " Private -------------------------------------------------------------------- 20 | function! s:build_args(git, args) abort 21 | let args = a:args.clone() 22 | if args.get(0) ==# '_shell' 23 | " Remove leading '_shell' if exists 24 | call args.pop(0) 25 | endif 26 | return args.lock() 27 | endfunction 28 | 29 | function! s:shellescape(val) abort 30 | return a:val =~# '\s' ? shellescape(a:val) : a:val 31 | endfunction 32 | -------------------------------------------------------------------------------- /autoload/gina/command/blame/pipe.vim: -------------------------------------------------------------------------------- 1 | let s:KEYWORDS = [ 2 | \ 'author-mail', 3 | \ 'author-time', 4 | \ 'author-tz', 5 | \ 'author', 6 | \ 'committer-mail', 7 | \ 'committer-time', 8 | \ 'committer-tz', 9 | \ 'committer', 10 | \ 'summary', 11 | \ 'previous', 12 | \ 'filename', 13 | \ 'boundary', 14 | \] 15 | call map( 16 | \ s:KEYWORDS, 17 | \ '[v:val, len(v:val), substitute(v:val, ''-'', ''_'', ''g'')]', 18 | \) 19 | 20 | function! gina#command#blame#pipe#incremental() abort 21 | let parser_pipe = deepcopy(s:parser_pipe) 22 | let parser_pipe.revisions = {} 23 | let parser_pipe.chunks = [] 24 | let parser_pipe._stdout = [''] 25 | let parser_pipe._stderr = [''] 26 | let parser_pipe._chunk = {} 27 | return parser_pipe 28 | endfunction 29 | 30 | 31 | " Parser pipe ---------------------------------------------------------------- 32 | function! s:_parser_pipe_on_stdout(data) abort dict 33 | let self._stdout[-1] .= a:data[0] 34 | call extend(self._stdout, a:data[1:]) 35 | endfunction 36 | 37 | function! s:_parser_pipe_on_stderr(data) abort dict 38 | let self._stderr[-1] .= a:data[0] 39 | call extend(self._stderr, a:data[1:]) 40 | endfunction 41 | 42 | function! s:_parser_pipe_on_exit(exitval) abort dict 43 | call call(s:original_pipe.on_exit, [a:exitval], self) 44 | if a:exitval 45 | throw gina#process#errormsg({ 46 | \ 'args': self.args, 47 | \ 'content': self._stderr, 48 | \}) 49 | endif 50 | " Parse records to create chunks 51 | call map(filter(self._stdout, '!empty(v:val)'), 's:parse(self, v:val)') 52 | " Sort chunks and assign indices 53 | call sort(self.chunks, { a, b -> a.lnum - b.lnum }) 54 | call map(self.chunks, 'extend(v:val, {''index'': v:key})') 55 | endfunction 56 | 57 | let s:original_pipe = gina#process#pipe#default() 58 | let s:parser_pipe = extend(deepcopy(s:original_pipe), { 59 | \ 'on_stdout': function('s:_parser_pipe_on_stdout'), 60 | \ 'on_stderr': function('s:_parser_pipe_on_stderr'), 61 | \ 'on_exit': function('s:_parser_pipe_on_exit'), 62 | \}) 63 | 64 | 65 | " Private -------------------------------------------------------- 66 | function! s:parse(pipe, record) abort 67 | let chunk = a:pipe._chunk 68 | let revisions = a:pipe.revisions 69 | call extend(chunk, s:parse_record(a:record)) 70 | if !has_key(chunk, 'filename') 71 | return 72 | endif 73 | if !has_key(revisions, chunk.revision) 74 | let revisions[chunk.revision] = chunk 75 | let chunk = { 76 | \ 'filename': chunk.filename, 77 | \ 'revision': chunk.revision, 78 | \ 'lnum_from': chunk.lnum_from, 79 | \ 'lnum': chunk.lnum, 80 | \ 'nlines': chunk.nlines, 81 | \} 82 | endif 83 | call add(a:pipe.chunks, chunk) 84 | let a:pipe._chunk = {} 85 | endfunction 86 | 87 | function! s:parse_record(record) abort 88 | for [prefix, length, vname] in s:KEYWORDS 89 | if a:record[:length-1] ==# prefix 90 | return {vname : a:record[length+1:]} 91 | endif 92 | endfor 93 | let terms = split(a:record) 94 | let nterms = len(terms) 95 | if nterms >= 3 96 | return { 97 | \ 'revision': terms[0], 98 | \ 'lnum_from': terms[1] + 0, 99 | \ 'lnum': terms[2] + 0, 100 | \ 'nlines': nterms == 3 ? 1 : (terms[3] + 0), 101 | \} 102 | endif 103 | throw gina#core#revelator#critical(printf( 104 | \ 'Failed to parse a record "%s"', 105 | \ a:record, 106 | \)) 107 | endfunction 108 | -------------------------------------------------------------------------------- /autoload/gina/command/cd.vim: -------------------------------------------------------------------------------- 1 | let s:Path = vital#gina#import('System.Filepath') 2 | 3 | let s:SCHEME = gina#command#scheme(expand('')) 4 | 5 | 6 | function! gina#command#cd#call(range, args, mods) abort 7 | call gina#core#options#help_if_necessary(a:args, s:get_options()) 8 | call gina#process#register(s:SCHEME, 1) 9 | try 10 | call s:call(a:range, a:args, a:mods) 11 | finally 12 | call gina#process#unregister(s:SCHEME, 1) 13 | endtry 14 | endfunction 15 | 16 | function! gina#command#cd#complete(arglead, cmdline, cursorpos) abort 17 | let args = gina#core#args#new(matchstr(a:cmdline, '^.*\ze .*')) 18 | if a:arglead[0] ==# '-' || !empty(args.get(1)) 19 | let options = s:get_options() 20 | return options.complete(a:arglead, a:cmdline, a:cursorpos) 21 | endif 22 | return gina#complete#filename#directory(a:arglead, a:cmdline, a:cursorpos) 23 | endfunction 24 | 25 | 26 | " Private -------------------------------------------------------------------- 27 | function! s:get_options() abort 28 | let options = gina#core#options#new() 29 | call options.define( 30 | \ '-h|--help', 31 | \ 'Show this help.', 32 | \) 33 | call options.define( 34 | \ '--local', 35 | \ 'Use "lcd" command instead of "cd" command.', 36 | \) 37 | return options 38 | endfunction 39 | 40 | function! s:build_args(git, args) abort 41 | let args = a:args.clone() 42 | let args.params.local = args.get('--local') 43 | call gina#core#args#extend_path(a:git, args, args.pop(1, v:null)) 44 | return args.lock() 45 | endfunction 46 | 47 | function! s:call(range, args, mods) abort 48 | let git = gina#core#get_or_fail() 49 | let args = s:build_args(git, a:args) 50 | let path = gina#util#get(args.params, 'path', '') 51 | let abspath = gina#core#repo#abspath(git, path) 52 | let command = args.params.local ? 'lcd' : 'cd' 53 | execute command gina#util#fnameescape(s:Path.realpath(abspath)) 54 | call gina#core#emitter#emit('command:called', s:SCHEME) 55 | endfunction 56 | 57 | -------------------------------------------------------------------------------- /autoload/gina/command/edit.vim: -------------------------------------------------------------------------------- 1 | let s:Path = vital#gina#import('System.Filepath') 2 | 3 | let s:SCHEME = gina#command#scheme(expand('')) 4 | 5 | 6 | function! gina#command#edit#call(range, args, mods) abort 7 | call gina#core#options#help_if_necessary(a:args, s:get_options()) 8 | call gina#process#register(s:SCHEME, 1) 9 | try 10 | call s:call(a:range, a:args, a:mods) 11 | finally 12 | call gina#process#unregister(s:SCHEME, 1) 13 | endtry 14 | endfunction 15 | 16 | function! gina#command#edit#complete(arglead, cmdline, cursorpos) abort 17 | let args = gina#core#args#new(matchstr(a:cmdline, '^.*\ze .*')) 18 | if a:arglead[0] ==# '-' 19 | let options = s:get_options() 20 | return options.complete(a:arglead, a:cmdline, a:cursorpos) 21 | endif 22 | return gina#complete#filename#any(a:arglead, a:cmdline, a:cursorpos) 23 | endfunction 24 | 25 | 26 | " Private -------------------------------------------------------------------- 27 | function! s:get_options() abort 28 | let options = gina#core#options#new() 29 | call options.define( 30 | \ '-h|--help', 31 | \ 'Show this help.', 32 | \) 33 | call options.define( 34 | \ '--opener=', 35 | \ 'A Vim command to open a new buffer.', 36 | \ ['edit', 'split', 'vsplit', 'tabedit', 'pedit'], 37 | \) 38 | call options.define( 39 | \ '--group=', 40 | \ 'A window group name used for the buffer.', 41 | \) 42 | call options.define( 43 | \ '--line', 44 | \ 'An initial line number.', 45 | \) 46 | call options.define( 47 | \ '--col', 48 | \ 'An initial column number.', 49 | \) 50 | return options 51 | endfunction 52 | 53 | function! s:build_args(git, args) abort 54 | let args = a:args.clone() 55 | let args.params.group = args.pop('--group', '') 56 | let args.params.opener = args.pop('--opener', '') 57 | call gina#core#args#extend_path(a:git, args, args.pop(1)) 58 | call gina#core#args#extend_line(a:git, args, args.pop('--line')) 59 | call gina#core#args#extend_col(a:git, args, args.pop('--col')) 60 | return args.lock() 61 | endfunction 62 | 63 | function! s:call(range, args, mods) abort 64 | let git = gina#core#get() 65 | let args = s:build_args(git, a:args) 66 | let abspath = gina#core#repo#abspath(git, args.params.path) 67 | let bufname = s:Path.realpath(abspath) 68 | call gina#core#buffer#open(bufname, { 69 | \ 'mods': a:mods, 70 | \ 'group': args.params.group, 71 | \ 'opener': args.params.opener, 72 | \ 'cmdarg': args.params.cmdarg, 73 | \ 'line': args.params.line, 74 | \ 'col': args.params.col, 75 | \}) 76 | call gina#core#emitter#emit('command:called', s:SCHEME) 77 | endfunction 78 | -------------------------------------------------------------------------------- /autoload/gina/command/lcd.vim: -------------------------------------------------------------------------------- 1 | let s:SCHEME = gina#command#scheme(expand('')) 2 | 3 | 4 | function! gina#command#lcd#call(range, args, mods) abort 5 | let args = a:args.clone() 6 | call args.set('--local', 1) 7 | call gina#command#cd#call(a:range, args, a:mods) 8 | endfunction 9 | 10 | function! gina#command#lcd#complete(arglead, cmdline, cursorpos) abort 11 | let args = gina#core#args#new(matchstr(a:cmdline, '^.*\ze .*')) 12 | if empty(args.get(1)) 13 | return gina#complete#filename#directory(a:arglead, a:cmdline, a:cursorpos) 14 | endif 15 | return [] 16 | endfunction 17 | -------------------------------------------------------------------------------- /autoload/gina/command/stash.vim: -------------------------------------------------------------------------------- 1 | let s:SCHEME = gina#command#scheme(expand('')) 2 | 3 | 4 | function! gina#command#stash#call(range, args, mods) abort 5 | call gina#core#options#help_if_necessary(a:args, s:get_options_save()) 6 | 7 | let git = gina#core#get_or_fail() 8 | let command = a:args.get(1, 'save') 9 | if command ==# 'show' 10 | return gina#command#stash#show#call(a:range, a:args, a:mods) 11 | elseif command ==# 'list' 12 | return gina#command#stash#list#call(a:range, a:args, a:mods) 13 | endif 14 | return gina#command#_raw#call(a:range, a:args, a:mods) 15 | endfunction 16 | 17 | function! gina#command#stash#complete(arglead, cmdline, cursorpos) abort 18 | let args = gina#core#args#new(matchstr(a:cmdline, '^.*\ze .*')) 19 | if args.get(1) ==# 'list' 20 | return gina#command#stash#list#complete(a:arglead, a:cmdline, a:cursorpos) 21 | elseif args.get(1) ==# 'show' 22 | return gina#command#stash#show#complete(a:arglead, a:cmdline, a:cursorpos) 23 | elseif args.get(1) =~# '^\%(save\|\)$' 24 | if a:arglead =~# '^-' 25 | let options = s:get_options_save() 26 | return options.complete(a:arglead, a:cmdline, a:cursorpos) 27 | endif 28 | elseif args.get(1) ==# 'drop' 29 | return gina#complete#stash#any(a:arglead, a:cmdline, a:cursorpos) 30 | elseif args.get(1) =~# '^\%(pop\|apply\)$' 31 | if a:arglead =~# '^-' || !empty(args.get(2)) 32 | let options = s:get_options_pop() 33 | return options.complete(a:arglead, a:cmdline, a:cursorpos) 34 | endif 35 | return gina#complete#stash#any(a:arglead, a:cmdline, a:cursorpos) 36 | elseif args.get(1) ==# 'branch' 37 | if empty(args.get(2)) 38 | return gina#complete#commit#branch(a:arglead, a:cmdline, a:cursorpos) 39 | endif 40 | return gina#complete#stash#any(a:arglead, a:cmdline, a:cursorpos) 41 | endif 42 | return gina#util#filter(a:arglead, [ 43 | \ 'list', 44 | \ 'show', 45 | \ 'drop', 46 | \ 'pop', 47 | \ 'apply', 48 | \ 'branch', 49 | \ 'save', 50 | \ 'clear', 51 | \ 'create', 52 | \ 'store', 53 | \]) 54 | endfunction 55 | 56 | 57 | " Private -------------------------------------------------------------------- 58 | function! s:get_options_save() abort 59 | let options = gina#core#options#new() 60 | call options.define( 61 | \ '-h|--help', 62 | \ 'Show this help.', 63 | \) 64 | call options.define( 65 | \ '-k|--keep-index', 66 | \ 'Left intact all changes already added to the index', 67 | \) 68 | call options.define( 69 | \ '--no-keep-index', 70 | \ 'Do not left intact all changes already added to the index', 71 | \) 72 | call options.define( 73 | \ '-a|--all', 74 | \ 'The ignored files and untracked files are stashed and cleaned', 75 | \) 76 | call options.define( 77 | \ '--include-untracked', 78 | \ 'The untracked files are stashed and cleaned', 79 | \) 80 | return options 81 | endfunction 82 | 83 | function! s:get_options_pop() abort 84 | let options = gina#core#options#new() 85 | call options.define( 86 | \ '-h|--help', 87 | \ 'Show this help.', 88 | \) 89 | call options.define( 90 | \ '--index', 91 | \ 'Tries to reinstate not only the working tree but also index', 92 | \) 93 | return options 94 | endfunction 95 | -------------------------------------------------------------------------------- /autoload/gina/complete/commit.vim: -------------------------------------------------------------------------------- 1 | let s:Git = vital#gina#import('Git') 2 | let s:Path = vital#gina#import('System.Filepath') 3 | let s:Store = vital#gina#import('System.Store') 4 | 5 | 6 | function! gina#complete#commit#any(arglead, cmdline, cursorpos) abort 7 | let candidates = [''] 8 | let candidates += gina#complete#commit#branch(a:arglead, a:cmdline, a:cursorpos) 9 | if !empty(a:arglead) 10 | let candidates += gina#complete#commit#hashref(a:arglead, a:cmdline, a:cursorpos) 11 | endif 12 | return s:filter(a:arglead, candidates) 13 | endfunction 14 | 15 | function! gina#complete#commit#branch(arglead, cmdline, cursorpos) abort 16 | let git = gina#core#get_or_fail() 17 | let slug = eval(s:Store.get_slug_expr()) 18 | let store = s:Store.of(s:Git.resolve(git, 'config')) 19 | let candidates = store.get(slug, []) 20 | if empty(candidates) 21 | let candidates = s:get_available_branches(git, ['--all']) 22 | call store.set(slug, candidates) 23 | endif 24 | return s:filter(a:arglead, candidates) 25 | endfunction 26 | 27 | function! gina#complete#commit#local_branch(arglead, cmdline, cursorpos) abort 28 | let git = gina#core#get_or_fail() 29 | let slug = eval(s:Store.get_slug_expr()) 30 | let store = s:Store.of(s:Git.resolve(git, 'config')) 31 | let candidates = store.get(slug, []) 32 | if empty(candidates) 33 | let candidates = s:get_available_branches(git, []) 34 | call store.set(slug, candidates) 35 | endif 36 | return s:filter(a:arglead, candidates) 37 | endfunction 38 | 39 | function! gina#complete#commit#remote_branch(arglead, cmdline, cursorpos) abort 40 | let git = gina#core#get_or_fail() 41 | let slug = eval(s:Store.get_slug_expr()) 42 | let store = s:Store.of(s:Git.resolve(git, 'config')) 43 | let candidates = store.get(slug, []) 44 | if empty(candidates) 45 | let candidates = s:get_available_branches(git, ['--remotes']) 46 | call store.set(slug, candidates) 47 | endif 48 | return s:filter(a:arglead, candidates) 49 | endfunction 50 | 51 | function! gina#complete#commit#hashref(arglead, cmdline, cursorpos) abort 52 | let git = gina#core#get_or_fail() 53 | let slug = eval(s:Store.get_slug_expr()) 54 | let store = s:Store.of(s:Git.resolve(git, 'config')) 55 | let candidates = store.get(slug, []) 56 | if empty(candidates) 57 | let candidates = s:get_available_commits(git, []) 58 | call store.set(slug, candidates) 59 | endif 60 | return s:filter(a:arglead, candidates) 61 | endfunction 62 | 63 | 64 | " Public --------------------------------------------------------------------- 65 | function! s:filter(arglead, candidates) abort 66 | return gina#util#filter(a:arglead, a:candidates) 67 | endfunction 68 | 69 | function! s:get_available_commits(git, args) abort 70 | let args = ['log', '--pretty=%h'] + a:args 71 | let result = gina#process#call(a:git, args) 72 | if result.status 73 | return [] 74 | endif 75 | return result.stdout 76 | endfunction 77 | 78 | function! s:get_available_branches(git, args) abort 79 | let args = ['branch', '--no-color', '--list'] + a:args 80 | let result = gina#process#call(a:git, args) 81 | if result.status 82 | return [] 83 | endif 84 | let candidates = filter(copy(result.stdout), 'v:val !~# ''^.* -> .*$''') 85 | call map(candidates, 'matchstr(v:val, ''^..\zs.*$'')') 86 | call map(candidates, 'substitute(v:val, ''^remotes/'', '''', '''')') 87 | return ['HEAD'] + filter(candidates, '!empty(v:val)') 88 | endfunction 89 | -------------------------------------------------------------------------------- /autoload/gina/complete/common.vim: -------------------------------------------------------------------------------- 1 | let s:Cache = vital#gina#import('System.Cache.Memory') 2 | let s:Path = vital#gina#import('System.Filepath') 3 | 4 | 5 | function! gina#complete#common#opener(arglead, cmdline, cursorpos) abort 6 | if a:arglead !~# '^--opener=' 7 | return [] 8 | endif 9 | let candidates = [ 10 | \ 'split', 11 | \ 'vsplit', 12 | \ 'tabedit', 13 | \ 'pedit', 14 | \] 15 | let prefix = '--opener=' 16 | return gina#util#filter(a:arglead, map(candidates, 'prefix . v:val')) 17 | endfunction 18 | 19 | function! gina#complete#common#treeish(arglead, cmdline, cursorpos) abort 20 | if a:arglead =~# '^[^:]*:' 21 | let revision = matchstr(a:arglead, '^[^:]*\ze:') 22 | let candidates = gina#complete#filename#tracked( 23 | \ matchstr(a:arglead, '^[^:]*:\zs.*'), 24 | \ a:cmdline, 25 | \ a:cursorpos, 26 | \ revision, 27 | \) 28 | return map(candidates, 'revision . '':'' . v:val') 29 | else 30 | let candidates = gina#complete#range#any(a:arglead, a:cmdline, a:cursorpos) 31 | return map(candidates, 'v:val . '':''') 32 | endif 33 | endfunction 34 | 35 | function! gina#complete#common#command(arglead, cmdline, cursorpos) abort 36 | let cache = s:get_cache() 37 | if !cache.has('command_names') 38 | call cache.set('command_names', s:get_command_names()) 39 | endif 40 | let command_names = cache.get('command_names') 41 | return gina#util#filter(a:arglead, command_names, '^_') 42 | endfunction 43 | 44 | function! gina#complete#common#raw_command(arglead, cmdline, cursorpos) abort 45 | return gina#util#filter(a:arglead, [ 46 | \ 'add', 47 | \ 'bisect', 48 | \ 'branch', 49 | \ 'checkout', 50 | \ 'clone', 51 | \ 'commit', 52 | \ 'diff', 53 | \ 'fetch', 54 | \ 'grep', 55 | \ 'init', 56 | \ 'log', 57 | \ 'merge', 58 | \ 'mv', 59 | \ 'pull', 60 | \ 'push', 61 | \ 'rebase', 62 | \ 'reset', 63 | \ 'restore', 64 | \ 'rm', 65 | \ 'show', 66 | \ 'status', 67 | \ 'switch', 68 | \ 'tag', 69 | \]) 70 | endfunction 71 | 72 | function! gina#complete#common#remote(arglead, cmdline, cursorpos) abort 73 | let git = gina#core#get_or_fail() 74 | let result = gina#process#call(git, ['remote']) 75 | if result.status 76 | return [] 77 | endif 78 | return gina#util#filter(a:arglead, result.stdout) 79 | endfunction 80 | 81 | " Private -------------------------------------------------------------------- 82 | function! s:get_cache() abort 83 | if exists('s:cache') 84 | return s:cache 85 | endif 86 | let s:cache = s:Cache.new() 87 | return s:cache 88 | endfunction 89 | 90 | function! s:get_command_names() abort 91 | let suffix = s:Path.realpath('autoload/gina/command/*.vim') 92 | let command_names = [] 93 | for runtimepath in split(&runtimepath, ',') 94 | let names = map( 95 | \ glob(s:Path.join(runtimepath, suffix), 0, 1), 96 | \ 'matchstr(fnamemodify(v:val, '':t''), ''^.\+\ze\.vim$'')', 97 | \) 98 | call extend(command_names, names) 99 | endfor 100 | return sort(command_names) 101 | endfunction 102 | -------------------------------------------------------------------------------- /autoload/gina/complete/range.vim: -------------------------------------------------------------------------------- 1 | 2 | function! gina#complete#range#any(arglead, cmdline, cursorpos) abort 3 | return s:complete( 4 | \ function('gina#complete#commit#any'), 5 | \ a:arglead, a:cmdline, a:cursorpos, 6 | \) 7 | endfunction 8 | 9 | function! gina#complete#range#branch(arglead, cmdline, cursorpos) abort 10 | return s:complete( 11 | \ function('gina#complete#commit#branch'), 12 | \ a:arglead, a:cmdline, a:cursorpos, 13 | \) 14 | endfunction 15 | 16 | function! gina#complete#range#local_branch(arglead, cmdline, cursorpos) abort 17 | return s:complete( 18 | \ function('gina#complete#commit#branch'), 19 | \ a:arglead, a:cmdline, a:cursorpos, 20 | \) 21 | endfunction 22 | 23 | function! gina#complete#range#remote_branch(arglead, cmdline, cursorpos) abort 24 | return s:complete( 25 | \ function('gina#complete#commit#branch'), 26 | \ a:arglead, a:cmdline, a:cursorpos, 27 | \) 28 | endfunction 29 | 30 | function! gina#complete#range#hashref(arglead, cmdline, cursorpos) abort 31 | return s:complete( 32 | \ function('gina#complete#commit#branch'), 33 | \ a:arglead, a:cmdline, a:cursorpos, 34 | \) 35 | endfunction 36 | 37 | 38 | " Private -------------------------------------------------------------------- 39 | function! s:complete(fn, arglead, cmdline, cursorpos) abort 40 | if a:arglead =~# '^[^.]*\.\.\.\?' 41 | let lhs = matchstr(a:arglead, '^[^.]*\.\.\.\?') 42 | let rhs = matchstr(a:arglead, '^[^.]*\.\.\.\?\zs.*') 43 | let candidates = a:fn(rhs, a:cmdline, a:cursorpos) 44 | return map(candidates, 'lhs . v:val') 45 | else 46 | return a:fn(a:arglead, a:cmdline, a:cursorpos) 47 | endif 48 | endfunction 49 | -------------------------------------------------------------------------------- /autoload/gina/complete/stash.vim: -------------------------------------------------------------------------------- 1 | let s:Git = vital#gina#import('Git') 2 | let s:Store = vital#gina#import('System.Store') 3 | 4 | 5 | function! gina#complete#stash#any(arglead, cmdline, cursorpos) abort 6 | let git = gina#core#get_or_fail() 7 | let slug = eval(s:Store.get_slug_expr()) 8 | let store = s:Store.of([ 9 | \ s:Git.resolve(git, 'HEAD'), 10 | \ s:Git.resolve(git, 'index'), 11 | \]) 12 | let candidates = store.get(slug, []) 13 | if empty(candidates) 14 | let candidates = s:get_available_stashes(git, []) 15 | call store.set(slug, candidates) 16 | endif 17 | return s:filter(a:arglead, candidates) 18 | endfunction 19 | 20 | " Public --------------------------------------------------------------------- 21 | function! s:filter(arglead, candidates) abort 22 | return gina#util#filter(a:arglead, a:candidates) 23 | endfunction 24 | 25 | function! s:get_available_stashes(git, args) abort 26 | let args = ['stash', 'list', '--format=%gd'] + a:args 27 | let result = gina#process#call(a:git, args) 28 | if result.status 29 | return [] 30 | endif 31 | let candidates = copy(result.stdout) 32 | return filter(candidates, '!empty(v:val)') 33 | endfunction 34 | -------------------------------------------------------------------------------- /autoload/gina/complete/tag.vim: -------------------------------------------------------------------------------- 1 | let s:Git = vital#gina#import('Git') 2 | let s:Store = vital#gina#import('System.Store') 3 | 4 | 5 | function! gina#complete#tag#any(arglead, cmdline, cursorpos) abort 6 | let git = gina#core#get_or_fail() 7 | let slug = eval(s:Store.get_slug_expr()) 8 | let store = s:Store.of([ 9 | \ s:Git.resolve(git, 'index'), 10 | \]) 11 | let candidates = store.get(slug, []) 12 | if empty(candidates) 13 | let candidates = s:get_available_tags(git, []) 14 | call store.set(slug, candidates) 15 | endif 16 | return s:filter(a:arglead, candidates) 17 | endfunction 18 | 19 | " Public --------------------------------------------------------------------- 20 | function! s:filter(arglead, candidates) abort 21 | return gina#util#filter(a:arglead, a:candidates) 22 | endfunction 23 | 24 | function! s:get_available_tags(git, args) abort 25 | let args = ['tag', '--list', '--format=%(refname:strip=2)'] + a:args 26 | let result = gina#process#call(a:git, args) 27 | if result.status 28 | return [] 29 | endif 30 | let candidates = result.stdout 31 | return filter(candidates, '!empty(v:val)') 32 | endfunction 33 | 34 | 35 | -------------------------------------------------------------------------------- /autoload/gina/component/repo.vim: -------------------------------------------------------------------------------- 1 | scriptencoding utf-8 2 | 3 | let s:Git = vital#gina#import('Git') 4 | let s:Path = vital#gina#import('System.Filepath') 5 | let s:Store = vital#gina#import('System.Store') 6 | 7 | 8 | function! gina#component#repo#name() abort 9 | let git = gina#core#get() 10 | if empty(git) 11 | return '' 12 | endif 13 | return fnamemodify(git.worktree, ':t') 14 | endfunction 15 | 16 | function! gina#component#repo#branch() abort 17 | let git = gina#core#get() 18 | if empty(git) 19 | return '' 20 | endif 21 | let slug = eval(s:Store.get_slug_expr()) 22 | let store = s:Store.of([ 23 | \ s:Git.resolve(git, 'HEAD'), 24 | \ s:Git.resolve(git, 'config'), 25 | \]) 26 | let branch = store.get(slug, '') 27 | if !empty(branch) 28 | return branch 29 | endif 30 | let content = get(readfile(s:Git.resolve(git, 'HEAD')), 0, '') 31 | if content =~# '^ref:\s\+refs/heads' 32 | let branch = matchstr(content, '^ref:\s\+refs/heads/\zs.\+') 33 | elseif content =~# '^ref:' 34 | let branch = matchstr(content, '^ref:\s\+refs/\zs.\+') 35 | elseif g:gina#component#repo#commit_length > 0 36 | let branch = content[:(g:gina#component#repo#commit_length - 1)] 37 | else 38 | let branch = content 39 | endif 40 | call store.set(slug, branch) 41 | return branch 42 | endfunction 43 | 44 | function! gina#component#repo#track() abort 45 | let git = gina#core#get() 46 | if empty(git) 47 | return '' 48 | endif 49 | let slug = eval(s:Store.get_slug_expr()) 50 | let store = s:Store.of([ 51 | \ s:Git.resolve(git, 'HEAD'), 52 | \ s:Git.resolve(git, 'config'), 53 | \]) 54 | let branch = store.get(slug, '') 55 | if !empty(branch) 56 | return branch 57 | endif 58 | if !exists('s:track_job') 59 | let pipe = gina#process#pipe#store() 60 | let pipe.__on_exit = pipe.on_exit 61 | let pipe.on_exit = funcref('s:track_on_exit', [store, slug]) 62 | let s:track_job = gina#process#open(git, [ 63 | \ 'rev-parse', 64 | \ '--abbrev-ref', 65 | \ '--symbolic-full-name', 66 | \ '@{upstream}', 67 | \], pipe) 68 | endif 69 | return '' 70 | endfunction 71 | 72 | function! gina#component#repo#preset(...) abort 73 | let git = gina#core#get() 74 | if empty(git) 75 | return '' 76 | endif 77 | let kind = get(a:000, 0, 'ascii') 78 | return call('s:preset_' . kind, []) 79 | endfunction 80 | 81 | 82 | " Private -------------------------------------------------------------------- 83 | function! s:track_on_exit(store, slug, exitval) abort dict 84 | call self.__on_exit(a:exitval) 85 | silent! unlet! s:track_job 86 | if a:exitval 87 | return 88 | endif 89 | call a:store.set(a:slug, get(self.stdout, 0)) 90 | endfunction 91 | 92 | function! s:preset_ascii() abort 93 | let name = gina#component#repo#name() 94 | let branch = gina#component#repo#branch() 95 | let track = gina#component#repo#track() 96 | if empty(track) 97 | return printf('%s [%s]', name, branch) 98 | endif 99 | return printf('%s [%s -> %s]', name, branch, track) 100 | endfunction 101 | 102 | function! s:preset_fancy() abort 103 | let name = gina#component#repo#name() 104 | let branch = gina#component#repo#branch() 105 | let track = gina#component#repo#track() 106 | if empty(track) 107 | return printf('%s [%s]', name, branch) 108 | endif 109 | return printf('%s [%s → %s]', name, branch, track) 110 | endfunction 111 | 112 | 113 | call gina#config(expand(''), { 114 | \ 'commit_length': 0, 115 | \}) 116 | -------------------------------------------------------------------------------- /autoload/gina/core/askpass.vim: -------------------------------------------------------------------------------- 1 | let s:Path = vital#gina#import('System.Filepath') 2 | 3 | let s:is_windows = has('win32') || has('win64') 4 | let s:is_darwin = has('mac') || has('macunix') 5 | let s:repository_root = expand(':p:h:h:h:h') 6 | let s:askpass_script = s:Path.join(s:repository_root, 'scripts', 'askpass') 7 | 8 | if s:is_windows 9 | " Using an official credential helper 'wincred' helps. 10 | " See https://github.com/lambdalisue/gina.vim/pull/11#issuecomment-279541140 11 | let s:askpass_script = '' 12 | elseif s:is_darwin 13 | " AFAI, no usable GUI ssh-askpass exist 14 | let s:askpass_script .= '.mac' 15 | else 16 | if executable('zenity') 17 | let s:askpass_script .= '.zenity' 18 | else 19 | " ssh-askpass-gnome would help in this case 20 | let s:askpass_script = '' 21 | endif 22 | endif 23 | 24 | if s:is_windows 25 | " While Windows has an official credential which raise a GUI prompt, gina 26 | " won't touch askpass for Windows 27 | function! gina#core#askpass#wrap(git, args) abort 28 | return a:args 29 | endfunction 30 | else 31 | function! gina#core#askpass#wrap(git, args) abort 32 | if empty(a:git) 33 | return a:args 34 | endif 35 | let prefix = ['env', 'GIT_TERMINAL_PROMPT=0'] 36 | let askpass = s:askpass(a:git) 37 | if !empty(askpass) 38 | " NOTE: 39 | " '$GIT_ASKPASS' has a higest priority so use this instead of 40 | " '-c core.askpass=...' in Mac/Linux environment while 'env' 41 | " is available. 42 | let prefix += ['GIT_ASKPASS=' . askpass] 43 | endif 44 | return extend(a:args, prefix, 0) 45 | endfunction 46 | endif 47 | 48 | 49 | function! s:askpass(git) abort 50 | let config = gina#core#repo#config(a:git) 51 | let askpass = get(config, 'core.askpass', '') 52 | if !empty(g:gina#core#askpass#askpass_program) 53 | return g:gina#core#askpas#askpass_program 54 | elseif g:gina#core#askpass#force_internal 55 | return s:askpass_script 56 | elseif exists('$GIT_ASKPASS') 57 | return $GIT_ASKPASS 58 | elseif !empty(askpass) 59 | return askpass 60 | elseif exists('$SSH_ASKPASS') 61 | return $SSH_ASKPASS 62 | endif 63 | return s:askpass_script 64 | endfunction 65 | 66 | 67 | call gina#config(expand(''), { 68 | \ 'askpass_program': '', 69 | \ 'force_internal': 0, 70 | \}) 71 | -------------------------------------------------------------------------------- /autoload/gina/core/console.vim: -------------------------------------------------------------------------------- 1 | let s:Console = vital#gina#import('Vim.Console') 2 | let s:Console.prefix = '[gina] ' 3 | 4 | 5 | if has('nvim') 6 | function! gina#core#console#message(msg) abort 7 | return s:message(a:msg) 8 | endfunction 9 | else 10 | " NOTE: 11 | " Vim 8.0.0329 will not echo entire message which was invoked in timer/job. 12 | " While echo pipe is used to inform the result of the process to a user, it 13 | " is kind critical so use autocmd to forcedly invoke message. 14 | let s:message_queue = [] 15 | function! gina#core#console#message(msg) abort 16 | augroup gina_core_console_message_internal 17 | autocmd! * 18 | autocmd CursorMoved * call s:message_callback() 19 | autocmd CursorHold * call s:message_callback() 20 | autocmd InsertEnter * call s:message_callback() 21 | augroup END 22 | call add(s:message_queue, a:msg) 23 | endfunction 24 | 25 | function! s:message_callback() abort 26 | while !empty(s:message_queue) 27 | let msg = remove(s:message_queue, 0) 28 | call s:message(msg) 29 | endwhile 30 | augroup gina_core_console_message_internal 31 | autocmd! * 32 | augroup END 33 | endfunction 34 | endif 35 | 36 | function! gina#core#console#echo(...) abort 37 | return call(s:Console.echo, a:000, s:Console) 38 | endfunction 39 | 40 | function! gina#core#console#echon(...) abort 41 | return call(s:Console.echon, a:000, s:Console) 42 | endfunction 43 | 44 | function! gina#core#console#echomsg(...) abort 45 | return call(s:Console.echomsg, a:000, s:Console) 46 | endfunction 47 | 48 | function! gina#core#console#debug(...) abort 49 | return call(s:Console.debug, a:000, s:Console) 50 | endfunction 51 | 52 | function! gina#core#console#info(...) abort 53 | return call(s:Console.info, a:000, s:Console) 54 | endfunction 55 | 56 | function! gina#core#console#warn(...) abort 57 | return call(s:Console.warn, a:000, s:Console) 58 | endfunction 59 | 60 | function! gina#core#console#error(...) abort 61 | return call(s:Console.error, a:000, s:Console) 62 | endfunction 63 | 64 | function! gina#core#console#ask(...) abort 65 | return call(s:Console.ask, a:000, s:Console) 66 | endfunction 67 | 68 | function! gina#core#console#confirm(...) abort 69 | return call(s:Console.confirm, a:000, s:Console) 70 | endfunction 71 | 72 | function! gina#core#console#ask_or_cancel(...) abort 73 | let result = call(s:Console.ask, a:000, s:Console) 74 | if empty(result) 75 | throw gina#core#revelator#info('Canceled') 76 | endif 77 | return result 78 | endfunction 79 | 80 | function! s:message(msg) abort 81 | if g:gina#core#console#enable_message_history 82 | return gina#core#console#echomsg(a:msg) 83 | endif 84 | return gina#core#console#echo(a:msg) 85 | endfunction 86 | 87 | call gina#config(expand(''), { 88 | \ 'enable_message_history': 0, 89 | \}) 90 | -------------------------------------------------------------------------------- /autoload/gina/core/diffjump.vim: -------------------------------------------------------------------------------- 1 | function! s:find_path_and_lnum(git, src_prefix, dst_prefix) abort 2 | if getline('.') =~# '^-' 3 | return s:find_path_and_lnum_lhs(a:git, a:src_prefix) 4 | elseif getline('.') =~# '^[ +]' 5 | return s:find_path_and_lnum_rhs(a:git, a:dst_prefix) 6 | else 7 | return v:null 8 | endif 9 | endfunction 10 | 11 | function! s:find_path_and_lnum_lhs(git, prefix) abort 12 | let lnum = search('^@@', 'bnW') 13 | let m = matchlist( 14 | \ getline(lnum), 15 | \ '^@@ -\(\d\+\)\%(,\(\d\+\)\)\? +\(\d\+\),\(\d\+\) @@\(.*\)$' 16 | \) 17 | if empty(m) 18 | return v:null 19 | endif 20 | let path = matchstr( 21 | \ getline(search('^--- ', 'bnW')), 22 | \ printf('^--- %s\zs.*$', a:prefix), 23 | \) 24 | if empty(path) 25 | return v:null 26 | endif 27 | let n = len(filter( 28 | \ map(range(lnum, line('.')), { _, v -> getline(v) }), 29 | \ { _, v -> v !~# '^+' } 30 | \)) 31 | return {'path': path, 'lnum': m[1] + n - 2, 'side': 0} 32 | endfunction 33 | 34 | function! s:find_path_and_lnum_rhs(git, prefix) abort 35 | if getline('.') !~# '^[ -+]' 36 | return v:null 37 | endif 38 | let lnum = search('^@@', 'bnW') 39 | let m = matchlist( 40 | \ getline(lnum), 41 | \ '^@@ -\(\d\+\)\%(,\(\d\+\)\)\? +\(\d\+\),\(\d\+\) @@\(.*\)$' 42 | \) 43 | if empty(m) 44 | return v:null 45 | endif 46 | let path = matchstr( 47 | \ getline(search('^+++ ', 'bnW')), 48 | \ printf('^+++ %s\zs.*$', a:prefix) 49 | \) 50 | if empty(path) 51 | return v:null 52 | endif 53 | let n = len(filter( 54 | \ map(range(lnum, line('.')), { _, v -> getline(v) }), 55 | \ { _, v -> v !~# '^-' } 56 | \)) 57 | return {'path': path, 'lnum': m[3] + n - 2, 'side': 1} 58 | endfunction 59 | 60 | function! s:jump(opener) abort 61 | let git = gina#core#get_or_fail() 62 | let args = gina#core#meta#get_or_fail('args') 63 | let config = gina#core#repo#config(git) 64 | 65 | if get(config, 'diff.noprefix', '') =~? 'true' || args.get('--no-prefix') 66 | let src_prefix = '' 67 | let dst_prefix = '' 68 | else 69 | let src_prefix = args.get('--src-prefix', '[aic]/') 70 | let dst_prefix = args.get('--dst-prefix', '[bwi]/') 71 | endif 72 | 73 | let pathinfo = s:find_path_and_lnum(git, src_prefix, dst_prefix) 74 | if pathinfo is v:null 75 | return 1 76 | endif 77 | 78 | let rev = pathinfo.side ? args.params.rev2 : args.params.rev1 79 | if gina#core#args#is_worktree(rev) && pathinfo.side == 1 80 | call gina#core#console#debug(printf( 81 | \ 'Gina edit --line=%d --opener=%s %s', 82 | \ pathinfo.lnum, 83 | \ a:opener, 84 | \ pathinfo.path, 85 | \)) 86 | execute printf( 87 | \ 'Gina edit --line=%d --opener=%s %s', 88 | \ pathinfo.lnum, 89 | \ a:opener, 90 | \ pathinfo.path, 91 | \) 92 | else 93 | call gina#core#console#debug(printf( 94 | \ 'Gina show --line=%d --opener=%s %s:%s', 95 | \ pathinfo.lnum, 96 | \ a:opener, 97 | \ rev, 98 | \ pathinfo.path, 99 | \)) 100 | execute printf( 101 | \ 'Gina show --line=%d --opener=%s %s:%s', 102 | \ pathinfo.lnum, 103 | \ a:opener, 104 | \ rev, 105 | \ pathinfo.path, 106 | \) 107 | endif 108 | endfunction 109 | 110 | function! gina#core#diffjump#jump(...) abort 111 | let opener = a:0 ? a:1 : '' 112 | let opener = empty(opener) ? 'edit' : opener 113 | return gina#core#revelator#call( 114 | \ function('s:jump'), 115 | \ [opener], 116 | \) 117 | endfunction 118 | -------------------------------------------------------------------------------- /autoload/gina/core/emitter.vim: -------------------------------------------------------------------------------- 1 | let s:Emitter = vital#gina#import('App.Emitter') 2 | let s:modified_timer = v:null 3 | 4 | 5 | function! gina#core#emitter#emit(name, ...) abort 6 | call call(s:Emitter.emit, [a:name] + a:000, s:Emitter) 7 | endfunction 8 | 9 | function! gina#core#emitter#subscribe(name, listener, ...) abort 10 | call call(s:Emitter.subscribe, [a:name, a:listener] + a:000, s:Emitter) 11 | endfunction 12 | 13 | function! gina#core#emitter#unsubscribe(name, listener, ...) abort 14 | call call(s:Emitter.unsubscribe, [a:name, a:listener] + a:000, s:Emitter) 15 | endfunction 16 | 17 | function! gina#core#emitter#add_middleware(middleware) abort 18 | call call(s:Emitter.add_middleware, [a:middleware] + a:000, s:Emitter) 19 | endfunction 20 | 21 | function! gina#core#emitter#remove_middleware(...) abort 22 | call call(s:Emitter.remove_middleware, a:000, s:Emitter) 23 | endfunction 24 | 25 | 26 | " Subscribe ------------------------------------------------------------------ 27 | function! s:on_modified(...) abort 28 | if !empty(gina#process#runnings()) 29 | " DO NOT update if there are some running process 30 | call gina#core#emitter#emit('modified:delay') 31 | return 32 | endif 33 | let winid_saved = win_getid() 34 | for winnr in range(1, winnr('$')) 35 | let bufnr = winbufnr(winnr) 36 | if !getbufvar(bufnr, '&modified') 37 | \ && getbufvar(bufnr, '&autoread') 38 | \ && bufname(bufnr) =~# '^gina://' 39 | call win_gotoid(bufwinid(bufnr)) 40 | edit 41 | endif 42 | endfor 43 | call win_gotoid(winid_saved) 44 | endfunction 45 | 46 | function! s:on_modified_delay() abort 47 | if s:modified_timer isnot# v:null 48 | " Do not emit 'modified' for previous 'modified:delay' 49 | silent! call timer_stop(s:modified_timer) 50 | endif 51 | let s:modified_timer = timer_start( 52 | \ g:gina#core#emitter#modified_delay, 53 | \ function('s:emit_modified') 54 | \) 55 | endfunction 56 | 57 | function! s:emit_modified(...) abort 58 | call gina#core#emitter#emit('modified') 59 | endfunction 60 | 61 | if !exists('s:subscribed') 62 | let s:subscribed = 1 63 | call gina#core#emitter#subscribe( 64 | \ 'modified', 65 | \ function('s:on_modified') 66 | \) 67 | 68 | call gina#core#emitter#subscribe( 69 | \ 'modified:delay', 70 | \ function('s:on_modified_delay') 71 | \) 72 | endif 73 | 74 | 75 | call gina#config(expand(''), { 76 | \ 'modified_delay': 10, 77 | \}) 78 | -------------------------------------------------------------------------------- /autoload/gina/core/locator.vim: -------------------------------------------------------------------------------- 1 | function! gina#core#locator#find(origin) abort 2 | let nwinnr = winnr('$') 3 | if nwinnr == 1 4 | return 1 5 | endif 6 | let origin = a:origin == 0 ? winnr() : a:origin 7 | let former = range(origin, winnr('$')) 8 | let latter = reverse(range(1, origin - 1)) 9 | for winnr in (former + latter) 10 | if gina#core#locator#is_suitable(winnr) 11 | return winnr 12 | endif 13 | endfor 14 | return 0 15 | endfunction 16 | 17 | function! gina#core#locator#focus(origin) abort 18 | let winnr = gina#core#locator#find(a:origin) 19 | if winnr == 0 || winnr == winnr() 20 | return 1 21 | endif 22 | call win_gotoid(win_getid(winnr)) 23 | endfunction 24 | 25 | function! gina#core#locator#attach() abort 26 | augroup gina_core_locator_local_internal 27 | autocmd! * 28 | autocmd WinLeave call s:on_WinLeave() 29 | augroup END 30 | endfunction 31 | 32 | function! gina#core#locator#detach() abort 33 | augroup gina_core_locator_local_internal 34 | autocmd! * 35 | augroup END 36 | endfunction 37 | 38 | function! gina#core#locator#is_suitable(winnr) abort 39 | if getbufvar(winbufnr(a:winnr), '&previewwindow') 40 | \ || winwidth(a:winnr) < g:gina#core#locator#winwidth_threshold 41 | \ || winheight(a:winnr) < g:gina#core#locator#winheight_threshold 42 | return 0 43 | endif 44 | return 1 45 | endfunction 46 | 47 | function! s:on_WinLeave() abort 48 | let s:info = { 49 | \ 'nwin': winnr('$'), 50 | \ 'previous': win_getid(winnr('#')) 51 | \} 52 | endfunction 53 | 54 | function! s:on_WinEnter() abort 55 | if exists('s:info') && winnr('$') < s:info.nwin 56 | call gina#core#locator#focus(win_id2win(s:info.previous) || winnr()) 57 | endif 58 | silent! unlet! s:info 59 | endfunction 60 | 61 | augroup gina_core_locator_internal 62 | autocmd! * 63 | autocmd WinEnter * nested call s:on_WinEnter() 64 | augroup END 65 | 66 | 67 | call gina#config(expand(''), { 68 | \ 'winwidth_threshold': &columns / 4, 69 | \ 'winheight_threshold': &lines / 3, 70 | \}) 71 | -------------------------------------------------------------------------------- /autoload/gina/core/meta.vim: -------------------------------------------------------------------------------- 1 | let s:Cache = vital#gina#import('System.Cache.Memory') 2 | 3 | 4 | function! gina#core#meta#get(...) abort 5 | let meta = s:meta('%') 6 | return call(meta.get, a:000, meta) 7 | endfunction 8 | 9 | function! gina#core#meta#set(...) abort 10 | let meta = s:meta('%') 11 | return call(meta.set, a:000, meta) 12 | endfunction 13 | 14 | function! gina#core#meta#has(...) abort 15 | let meta = s:meta('%') 16 | return call(meta.has, a:000, meta) 17 | endfunction 18 | 19 | function! gina#core#meta#remove(...) abort 20 | let meta = s:meta('%') 21 | return call(meta.remove, a:000, meta) 22 | endfunction 23 | 24 | function! gina#core#meta#clear(...) abort 25 | let meta = s:meta('%') 26 | return call(meta.clear, a:000, meta) 27 | endfunction 28 | 29 | function! gina#core#meta#get_or_fail(name) abort 30 | let meta = s:meta('%') 31 | if !meta.has(a:name) 32 | throw gina#core#revelator#critical(printf( 33 | \ 'A required meta value "%s" does not exist on "%s"', 34 | \ a:name, 35 | \ bufname('%'), 36 | \)) 37 | endif 38 | return meta.get(a:name) 39 | endfunction 40 | 41 | 42 | " Private -------------------------------------------------------------------- 43 | function! s:meta(expr) abort 44 | let bufnr = bufnr(a:expr) 45 | if !bufexists(bufnr) 46 | " Always return a fresh cache instance 47 | return s:Cache.new() 48 | endif 49 | let meta = getbufvar(bufnr, 'gina_meta', {}) 50 | if empty(meta) 51 | let meta = s:Cache.new() 52 | call setbufvar(bufnr, 'gina_meta', meta) 53 | endif 54 | return meta 55 | endfunction 56 | -------------------------------------------------------------------------------- /autoload/gina/core/options.vim: -------------------------------------------------------------------------------- 1 | let s:Options = vital#gina#import('Options') 2 | 3 | function! gina#core#options#new() abort 4 | return s:Options.new() 5 | endfunction 6 | 7 | function! gina#core#options#help_if_necessary(args, options) abort 8 | if a:args.get('-h|--help') 9 | call a:options.help() 10 | throw gina#core#revelator#cancel() 11 | endif 12 | endfunction 13 | -------------------------------------------------------------------------------- /autoload/gina/core/path.vim: -------------------------------------------------------------------------------- 1 | let s:Git = vital#gina#import('Git') 2 | let s:Path = vital#gina#import('System.Filepath') 3 | let s:String = vital#gina#import('Data.String') 4 | let s:is_windows = has('win32') || has('win64') 5 | 6 | 7 | if s:is_windows 8 | function! gina#core#path#expand(expr) abort 9 | return s:Path.unixpath(s:expand(s:realpath(a:expr))) 10 | endfunction 11 | 12 | function! gina#core#path#abspath(path, ...) abort 13 | let path = s:realpath(a:path) 14 | return s:Path.unixpath(call('s:abspath', [path] + a:000)) 15 | endfunction 16 | 17 | function! gina#core#path#relpath(path, ...) abort 18 | let path = s:realpath(a:path) 19 | return s:Path.unixpath(call('s:relpath', [path] + a:000)) 20 | endfunction 21 | 22 | function! s:realpath(path) abort 23 | if a:path =~# '^\w\+://' 24 | return s:Path.unixpath(a:path) 25 | endif 26 | return s:Path.realpath(a:path) 27 | endfunction 28 | else 29 | function! gina#core#path#expand(expr) abort 30 | return s:expand(a:expr) 31 | endfunction 32 | 33 | function! gina#core#path#abspath(path, ...) abort 34 | return call('s:abspath', [a:path] + a:000) 35 | endfunction 36 | 37 | function! gina#core#path#relpath(path, ...) abort 38 | return call('s:relpath', [a:path] + a:000) 39 | endfunction 40 | endif 41 | 42 | 43 | function! s:expand(expr) abort 44 | if empty(a:expr) || a:expr[0] ==# ':' 45 | return a:expr 46 | elseif a:expr[:6] ==# 'gina://' 47 | let git = gina#core#get_or_fail({'expr': a:expr}) 48 | let path = gina#core#buffer#param(a:expr, 'path') 49 | return empty(path) ? '' : s:Path.join(git.worktree, path) 50 | elseif a:expr[0] =~# '[%#<]' 51 | let m = matchlist(a:expr, '^\([%#]\|<\w\+>\)\(.*\)') 52 | let expr = expand(m[1]) 53 | let modifiers = m[2] 54 | return fnamemodify(s:expand(expr), modifiers) 55 | endif 56 | return a:expr 57 | endfunction 58 | 59 | function! s:abspath(path, ...) abort 60 | if s:Path.is_absolute(a:path) || a:path[0] ==# ':' 61 | return a:path 62 | endif 63 | let root = s:Path.remove_last_separator(a:0 == 0 ? getcwd() : a:1) 64 | return s:Path.join(root, a:path) 65 | endfunction 66 | 67 | function! s:relpath(path, ...) abort 68 | if s:Path.is_relative(a:path) || a:path[0] ==# ':' 69 | return a:path 70 | endif 71 | let root = s:Path.remove_last_separator(a:0 == 0 ? getcwd() : a:1) 72 | let pattern = s:String.escape_pattern(root . s:Path.separator()) 73 | return a:path =~# '^' . pattern 74 | \ ? matchstr(a:path, '^' . pattern . '\zs.*') 75 | \ : a:path 76 | endfunction 77 | -------------------------------------------------------------------------------- /autoload/gina/core/repo.vim: -------------------------------------------------------------------------------- 1 | let s:Git = vital#gina#import('Git') 2 | let s:Path = vital#gina#import('System.Filepath') 3 | let s:Store = vital#gina#import('System.Store') 4 | 5 | 6 | function! gina#core#repo#abspath(git, expr) abort 7 | return gina#core#path#abspath(a:expr, a:git.worktree) 8 | endfunction 9 | 10 | function! gina#core#repo#relpath(git, expr) abort 11 | let path = gina#core#path#expand(a:expr) 12 | if s:Path.is_relative(s:Path.realpath(path)) 13 | return path 14 | endif 15 | let path = gina#core#path#relpath(path, a:git.worktree) 16 | if path ==# path && path !=# resolve(path) 17 | return gina#core#path#relpath(resolve(path), a:git.worktree) 18 | endif 19 | return path 20 | endfunction 21 | 22 | function! gina#core#repo#config(git) abort 23 | let slug = eval(s:Store.get_slug_expr()) 24 | let store = s:Store.of(s:Git.resolve(a:git, 'config')) 25 | let config = store.get(slug, {}) 26 | if !empty(config) 27 | return config 28 | endif 29 | let result = gina#process#call(a:git, ['config', '--list']) 30 | if result.status 31 | throw gina#process#errormsg(result) 32 | endif 33 | let config = {} 34 | for record in filter(copy(result.stdout), '!empty(v:val)') 35 | call s:extend_config(config, record) 36 | endfor 37 | call store.set(slug, config) 38 | return config 39 | endfunction 40 | 41 | 42 | " Private -------------------------------------------------------------------- 43 | function! s:extend_config(config, record) abort 44 | let m = matchlist(a:record, '^\(.\+\)=\(.*\)$') 45 | if empty(m) 46 | return 47 | endif 48 | let a:config[tolower(m[1])] = m[2] 49 | endfunction 50 | -------------------------------------------------------------------------------- /autoload/gina/core/revelator.vim: -------------------------------------------------------------------------------- 1 | let s:Revelator = vital#gina#import('App.Revelator') 2 | 3 | 4 | function! gina#core#revelator#cancel() abort 5 | return call(s:Revelator.message, ['CANCEL', ''], s:Revelator) 6 | endfunction 7 | 8 | function! gina#core#revelator#info(msg) abort 9 | return call(s:Revelator.info, [a:msg], s:Revelator) 10 | endfunction 11 | 12 | function! gina#core#revelator#warning(msg) abort 13 | return call(s:Revelator.warning, [a:msg], s:Revelator) 14 | endfunction 15 | 16 | function! gina#core#revelator#error(msg) abort 17 | return call(s:Revelator.error, [a:msg], s:Revelator) 18 | endfunction 19 | 20 | function! gina#core#revelator#critical(msg) abort 21 | return call(s:Revelator.critical, [a:msg], s:Revelator) 22 | endfunction 23 | 24 | function! gina#core#revelator#call(funcref, args, ...) abort 25 | return call(s:Revelator.call, [a:funcref, a:args] + a:000, s:Revelator) 26 | endfunction 27 | 28 | 29 | " Private -------------------------------------------------------------------- 30 | function! s:receiver(revelation) abort 31 | if a:revelation.category ==# 'CANCEL' 32 | return 1 33 | elseif a:revelation.category ==# 'INFO' 34 | redraw 35 | call gina#core#console#info(a:revelation.message) 36 | call gina#core#console#debug(a:revelation.throwpoint) 37 | return 1 38 | elseif a:revelation.category ==# 'WARNING' 39 | redraw 40 | call gina#core#console#warn(a:revelation.message) 41 | call gina#core#console#debug(a:revelation.throwpoint) 42 | return 1 43 | elseif a:revelation.category ==# 'ERROR' 44 | redraw 45 | call gina#core#console#error(a:revelation.message) 46 | call gina#core#console#debug(a:revelation.throwpoint) 47 | return 1 48 | elseif a:revelation.category ==# 'CRITICAL' 49 | redraw 50 | call gina#core#console#error(a:revelation.message) 51 | call gina#core#console#error(a:revelation.throwpoint) 52 | return 1 53 | endif 54 | endfunction 55 | 56 | call s:Revelator.unregister(s:Revelator.get_default_receiver()) 57 | call s:Revelator.register(function('s:receiver')) 58 | -------------------------------------------------------------------------------- /autoload/gina/core/spinner.vim: -------------------------------------------------------------------------------- 1 | scriptencoding utf-8 2 | 3 | if $LANG ==# 'C' 4 | let s:frames = ['-', '\', '|', '/'] 5 | else 6 | let s:frames = ['⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷'] 7 | endif 8 | 9 | function! gina#core#spinner#new(expr, ...) abort 10 | let options = extend({ 11 | \ 'frames': g:gina#core#spinner#frames, 12 | \ 'message': g:gina#core#spinner#message, 13 | \ 'delaytime': g:gina#core#spinner#delaytime, 14 | \ 'updatetime': g:gina#core#spinner#updatetime, 15 | \}, a:0 ? a:1 : {}) 16 | let spinner = deepcopy(s:spinner) 17 | let spinner._bufnr = bufnr(a:expr) 18 | let spinner._frames = options.frames 19 | let spinner._message = options.message 20 | let spinner._delaytime = options.delaytime 21 | let spinner._updatetime = options.updatetime 22 | return spinner 23 | endfunction 24 | 25 | function! gina#core#spinner#start(expr, ...) abort 26 | let spinner = gina#core#spinner#new(a:expr, a:0 ? a:1 : {}) 27 | call spinner.start_delay() 28 | return spinner 29 | endfunction 30 | 31 | 32 | 33 | function! s:_spinner_next() abort dict 34 | let index = self._index + 1 35 | let self._index = index >= len(self._frames) ? 0 : index 36 | return self._index 37 | endfunction 38 | 39 | function! s:_spinner_text() abort dict 40 | let face = self._frames[self._index] 41 | return ' ' . face . ' ' . self._message 42 | endfunction 43 | 44 | function! s:_spinner_start_delay() abort dict 45 | if self._timer isnot# v:null || self._timer_delay isnot# v:null || self._delaytime < 0 46 | return 47 | endif 48 | let self._timer_delay = timer_start( 49 | \ self._delaytime, 50 | \ self.start, 51 | \) 52 | endfunction 53 | 54 | function! s:_spinner_start(...) abort dict 55 | if self._timer isnot# v:null || self._delaytime < 0 56 | return 57 | endif 58 | silent! call timer_stop(self._timer_delay) 59 | let self._statusline = getbufvar(self._bufnr, '&statusline') 60 | let self._timer = timer_start( 61 | \ self._updatetime, 62 | \ function('s:update_spinner', [self]), 63 | \ { 'repeat': -1 } 64 | \) 65 | let self._timer_delay = v:null 66 | endfunction 67 | 68 | function! s:_spinner_stop() abort dict 69 | if self._timer_delay isnot# v:null 70 | call timer_stop(self._timer_delay) 71 | let self._timer_delay = v:null 72 | endif 73 | if self._timer is# v:null 74 | return 75 | endif 76 | call timer_stop(self._timer) 77 | call setbufvar(self._bufnr, '&statusline', self._statusline) 78 | let self._timer = v:null 79 | endfunction 80 | 81 | function! s:update_spinner(spinner, timer) abort 82 | if !bufexists(a:spinner._bufnr) 83 | call a:spinner.stop() 84 | elseif bufwinnr(a:spinner._bufnr) >= 0 85 | call a:spinner.next() 86 | call setbufvar(a:spinner._bufnr, '&statusline', a:spinner.text()) 87 | endif 88 | endfunction 89 | 90 | let s:spinner = { 91 | \ '_timer': v:null, 92 | \ '_timer_delay': v:null, 93 | \ '_bufnr': 0, 94 | \ '_index': 0, 95 | \ 'next': function('s:_spinner_next'), 96 | \ 'text': function('s:_spinner_text'), 97 | \ 'start': function('s:_spinner_start'), 98 | \ 'start_delay': function('s:_spinner_start_delay'), 99 | \ 'stop': function('s:_spinner_stop'), 100 | \} 101 | 102 | 103 | call gina#config(expand(''), { 104 | \ 'frames': s:frames, 105 | \ 'message': 'Loading ...', 106 | \ 'delaytime': 500, 107 | \ 'updatetime': 100, 108 | \}) 109 | -------------------------------------------------------------------------------- /autoload/gina/core/timestamper.vim: -------------------------------------------------------------------------------- 1 | let s:DateTime = vital#gina#import('DateTime') 2 | 3 | 4 | function! gina#core#timestamper#new(...) abort 5 | let timestamper = extend({ 6 | \ 'now': s:DateTime.now(), 7 | \ 'months': 3, 8 | \ 'format1': '%d %b', 9 | \ 'format2': '%d %b, %Y', 10 | \}, get(a:000, 0, {}) 11 | \) 12 | let timestamper = extend(timestamper, s:timestamper, 'keep') 13 | let timestamper._cache_timezone = {} 14 | let timestamper._cache_datetime = {} 15 | let timestamper._cache_timestamp = {} 16 | return timestamper 17 | endfunction 18 | 19 | 20 | " Timestamper ---------------------------------------------------------------- 21 | let s:timestamper = {} 22 | 23 | function! s:timestamper.timezone(timezone) abort 24 | if has_key(self._cache_timezone, a:timezone) 25 | return self._cache_timezone[a:timezone] 26 | endif 27 | let timezone = s:DateTime.timezone(a:timezone) 28 | let self._cache_timezone[a:timezone] = timezone 29 | return timezone 30 | endfunction 31 | 32 | function! s:timestamper.datetime(epoch, timezone) abort 33 | let cname = a:epoch . a:timezone 34 | if has_key(self._cache_datetime, cname) 35 | return self._cache_datetime[cname] 36 | endif 37 | let timezone = self.timezone(a:timezone) 38 | let datetime = s:DateTime.from_unix_time(a:epoch, timezone) 39 | let self._cache_datetime[cname] = datetime 40 | return datetime 41 | endfunction 42 | 43 | function! s:timestamper.format(epoch, timezone) abort 44 | let cname = a:epoch . a:timezone 45 | if has_key(self._cache_timestamp, cname) 46 | return self._cache_timestamp[cname] 47 | endif 48 | let datetime = self.datetime(a:epoch, a:timezone) 49 | let timedelta = datetime.delta(self.now) 50 | if timedelta.duration().months() < self.months 51 | let timestamp = timedelta.about() 52 | elseif datetime.year() == self.now.year() 53 | let timestamp = datetime.strftime(self.format1) 54 | else 55 | let timestamp = datetime.strftime(self.format2) 56 | endif 57 | let self._cache_timestamp[cname] = timestamp 58 | return timestamp 59 | endfunction 60 | -------------------------------------------------------------------------------- /autoload/gina/core/tracker.vim: -------------------------------------------------------------------------------- 1 | function! gina#core#tracker#track(git, path, lnum, ...) abort 2 | let options = extend({ 3 | \ 'lhs': '', 4 | \ 'rhs': 'HEAD', 5 | \ 'cache': 0, 6 | \}, a:0 ? a:1 : {} 7 | \) 8 | let result = s:investigate( 9 | \ a:git, a:path, 10 | \ options.lhs, 11 | \ options.rhs, 12 | \ options.cache 13 | \) 14 | let offset = 0 15 | for ref in result.refs 16 | if a:lnum > ref.ll && a:lnum < (ref.ll + ref.ls) 17 | let offset = a:lnum - ref.ll 18 | return offset <= ref.rs ? ref.rl + offset : ref.rl + ref.rs 19 | elseif a:lnum > ref.ll 20 | let offset += (ref.rs - ref.ls) 21 | else 22 | break 23 | endif 24 | endfor 25 | return a:lnum + offset 26 | endfunction 27 | 28 | 29 | function! s:investigate(git, path, lhs, rhs, cache) abort 30 | let result = gina#process#call_or_fail(a:git, filter([ 31 | \ 'diff', 32 | \ '--unified=0', 33 | \ '--dst-prefix=', 34 | \ '--find-renames', 35 | \ '--inter-hunk-context=0', 36 | \ a:cache ? '--cache' : '', 37 | \ printf('%s..%s', a:lhs, a:rhs), 38 | \ '--', 39 | \ a:path, 40 | \], '!empty(v:val)')) 41 | let path = get(filter(copy(result.stdout), 'v:val =~# ''^+++'''), 0, a:path) 42 | let path = substitute(path, '^+++ ', '', '') 43 | let refs = map( 44 | \ filter(copy(result.stdout), 'v:val =~# ''^@@'''), 45 | \ 's:extract_ranges(v:val)' 46 | \) 47 | return { 48 | \ 'path': path, 49 | \ 'refs': refs, 50 | \} 51 | endfunction 52 | 53 | function! s:extract_ranges(record) abort 54 | let m = matchlist( 55 | \ a:record, 56 | \ '^@@ -\(\d\+\%(,\d\+\)\?\) +\(\d\+\%(,\d\+\)\?\) @@', 57 | \) 58 | let lhs = split(m[1], ',') 59 | let rhs = split(m[2], ',') 60 | return { 61 | \ 'll': abs(0 + lhs[0]), 62 | \ 'ls': 0 + get(lhs, 1, 1), 63 | \ 'rl': abs(0 + rhs[0]), 64 | \ 'rs': 0 + get(rhs, 1, 1), 65 | \} 66 | endfunction 67 | -------------------------------------------------------------------------------- /autoload/gina/core/treeish.vim: -------------------------------------------------------------------------------- 1 | let s:Path = vital#gina#import('System.Filepath') 2 | let s:Git = vital#gina#import('Git') 3 | 4 | 5 | function! gina#core#treeish#parse(treeish) abort 6 | " Ref: https://git-scm.com/docs/gitrevisions 7 | if a:treeish =~# '^:/' || a:treeish =~# '^[^:]*^{/' || a:treeish !~# ':' 8 | return [a:treeish, v:null] 9 | endif 10 | let m = matchlist(a:treeish, '^\(:[0-3]\|[^:]*\)\%(:\(.*\)\)\?$') 11 | return [m[1], m[2]] 12 | endfunction 13 | 14 | function! gina#core#treeish#build(rev, path) abort 15 | let rev = a:rev is# v:null ? ':0' : a:rev 16 | if a:path is# v:null 17 | return rev 18 | endif 19 | return printf('%s:%s', rev, s:Path.unixpath(a:path)) 20 | endfunction 21 | 22 | function! gina#core#treeish#split(rev) abort 23 | if a:rev =~# '^.\{-}\.\.\..*$' 24 | let [lhs, rhs] = matchlist(a:rev, '^\(.\{-}\)\.\.\.\(.*\)$')[1 : 2] 25 | let lhs = empty(lhs) ? 'HEAD' : lhs 26 | let rhs = empty(rhs) ? 'HEAD' : rhs 27 | return [lhs . '...' . rhs, rhs] 28 | elseif a:rev =~# '^.\{-}\.\..*$' 29 | let [lhs, rhs] = matchlist(a:rev, '^\(.\{-}\)\.\.\(.*\)$')[1 : 2] 30 | let lhs = empty(lhs) ? 'HEAD' : lhs 31 | let rhs = empty(rhs) ? 'HEAD' : rhs 32 | return [lhs, rhs] 33 | else 34 | return [a:rev, ''] 35 | endif 36 | endfunction 37 | 38 | function! gina#core#treeish#sha1(git, rev) abort 39 | let ref = s:Git.ref(a:git, a:rev) 40 | if !empty(ref) 41 | return ref.hash 42 | endif 43 | " Fallback to rev-parse (e.g. HEAD@{2.days.ago}) 44 | let result = gina#process#call_or_fail(a:git, ['rev-parse', a:rev]) 45 | return get(result.stdout, 0, '') 46 | endfunction 47 | 48 | function! gina#core#treeish#resolve(git, rev, ...) abort 49 | let aggressive = a:0 ? a:1 : 0 50 | if a:rev =~# '^.\{-}\.\.\..*$' 51 | let [lhs, rhs] = matchlist(a:rev, '^\(.\{-}\)\.\.\.\(.*\)$')[1 : 2] 52 | let lhs = empty(lhs) ? 'HEAD' : lhs 53 | let rhs = empty(rhs) ? 'HEAD' : rhs 54 | return s:find_common_ancestor(a:git, lhs, rhs) 55 | elseif a:rev =~# '^.\{-}\.\..*$' 56 | let [lhs, _] = matchlist(a:rev, '^\(.\{-}\)\.\.\(.*\)$')[1 : 2] 57 | let lhs = empty(lhs) ? 'HEAD' : lhs 58 | if aggressive 59 | let ref = s:Git.ref(a:git, lhs) 60 | return get(ref, 'name', lhs) 61 | else 62 | return lhs 63 | endif 64 | elseif aggressive 65 | let ref = s:Git.ref(a:git, a:rev) 66 | return get(ref, 'name', a:rev) 67 | else 68 | return a:rev 69 | endif 70 | endfunction 71 | 72 | function! gina#core#treeish#validate(git, rev, path, ...) abort 73 | let treeish = gina#core#treeish#build(a:rev, a:path) 74 | let result = gina#process#call(a:git, ['rev-parse', treeish]) 75 | if result.status 76 | throw gina#core#revelator#warning(a:0 ? a:1 : join(result.stderr, "\n")) 77 | endif 78 | endfunction 79 | 80 | 81 | " Private -------------------------------------------------------------------- 82 | function! s:find_common_ancestor(git, rev1, rev2) abort 83 | let lhs = empty(a:rev1) ? 'HEAD' : a:rev1 84 | let rhs = empty(a:rev2) ? 'HEAD' : a:rev2 85 | let result = gina#process#call_or_fail(a:git, [ 86 | \ 'merge-base', lhs, rhs 87 | \]) 88 | return get(result.stdout, 0, '') 89 | endfunction 90 | -------------------------------------------------------------------------------- /autoload/gina/core/writer.vim: -------------------------------------------------------------------------------- 1 | let s:Writer = vital#gina#import('Vim.Buffer.Writer') 2 | 3 | function! gina#core#writer#new(...) abort 4 | let options = extend({ 5 | \ 'updatetime': g:gina#core#writer#updatetime, 6 | \}, a:0 ? a:1 : {}, 7 | \) 8 | return s:Writer.new(options) 9 | endfunction 10 | 11 | function! gina#core#writer#replace(...) abort 12 | return call(s:Writer.replace, a:000) 13 | endfunction 14 | 15 | 16 | call gina#config(expand(''), { 17 | \ 'updatetime': 100, 18 | \}) 19 | -------------------------------------------------------------------------------- /autoload/gina/custom.vim: -------------------------------------------------------------------------------- 1 | let s:preferences = {} 2 | let s:pattern_preferences = {} 3 | 4 | function! gina#custom#preference(scheme, ...) abort 5 | let readonly = a:0 ? a:1 : 1 6 | let preferences = a:scheme =~# '^/' 7 | \ ? s:pattern_preferences 8 | \ : s:preferences 9 | let preferences[a:scheme] = get(preferences, a:scheme, {}) 10 | let preference = extend(preferences[a:scheme], { 11 | \ 'action': { 12 | \ 'aliases': [], 13 | \ 'shortens': [], 14 | \ }, 15 | \ 'mapping': { 16 | \ 'mappings': [], 17 | \ }, 18 | \ 'command': { 19 | \ 'options': [], 20 | \ 'origin': a:scheme, 21 | \ 'raw': 0, 22 | \ }, 23 | \ 'executes': [], 24 | \}, 'keep' 25 | \) 26 | return readonly ? deepcopy(preference) : preference 27 | endfunction 28 | 29 | function! gina#custom#preferences(scheme) abort 30 | let preferences = [] 31 | for [pattern, preference] in items(s:pattern_preferences) 32 | if a:scheme =~# pattern[1:] 33 | call add(preferences, preference) 34 | endif 35 | endfor 36 | return extend( 37 | \ deepcopy(preferences), 38 | \ [gina#custom#preference(a:scheme)] 39 | \) 40 | endfunction 41 | 42 | function! gina#custom#clear() abort 43 | let s:preferences = {} 44 | let s:pattern_preferences = {} 45 | endfunction 46 | 47 | function! gina#custom#execute(scheme, expr) abort 48 | let value = get(a:000, 0, 1) 49 | let preference = gina#custom#preference(a:scheme, 0) 50 | call add(preference.executes, [a:expr]) 51 | endfunction 52 | 53 | 54 | " Private -------------------------------------------------------------------- 55 | function! s:apply_preference(preference) abort 56 | for [expr] in a:preference.executes 57 | execute expr 58 | endfor 59 | endfunction 60 | 61 | function! s:FileType() abort 62 | let scheme = gina#core#buffer#param('%', 'scheme') 63 | if empty(scheme) 64 | return 65 | endif 66 | for preference in gina#custom#preferences(scheme) 67 | call s:apply_preference(preference) 68 | endfor 69 | endfunction 70 | 71 | 72 | " Autocmd -------------------------------------------------------------------- 73 | augroup gina_custom_internal 74 | autocmd! * 75 | autocmd FileType * call s:FileType() 76 | augroup END 77 | -------------------------------------------------------------------------------- /autoload/gina/custom/action.vim: -------------------------------------------------------------------------------- 1 | function! gina#custom#action#alias(scheme, alias, origin) abort 2 | let preference = gina#custom#preference(a:scheme, 0) 3 | call add(preference.action.aliases, [a:alias, a:origin]) 4 | endfunction 5 | 6 | function! gina#custom#action#shorten(scheme, action_scheme, ...) abort 7 | let excludes = get(a:000, 0, []) 8 | let preference = gina#custom#preference(a:scheme, 0) 9 | call add(preference.action.shortens, [a:action_scheme, excludes]) 10 | endfunction 11 | 12 | 13 | " Private -------------------------------------------------------------------- 14 | function! s:apply_preference(preference) abort 15 | for [alias, origin] in a:preference.action.aliases 16 | call gina#action#alias(alias, origin) 17 | endfor 18 | for [action_scheme, excludes] in a:preference.action.shortens 19 | call gina#action#shorten(action_scheme, excludes) 20 | endfor 21 | endfunction 22 | 23 | function! s:FileType() abort 24 | let scheme = gina#core#buffer#param('%', 'scheme') 25 | if empty(scheme) 26 | return 27 | endif 28 | for preference in gina#custom#preferences(scheme) 29 | call s:apply_preference(preference) 30 | endfor 31 | endfunction 32 | 33 | 34 | " Autocmd -------------------------------------------------------------------- 35 | augroup gina_custom_action_internal 36 | autocmd! * 37 | autocmd FileType * call s:FileType() 38 | augroup END 39 | -------------------------------------------------------------------------------- /autoload/gina/custom/command.vim: -------------------------------------------------------------------------------- 1 | let s:t_number = type(0) 2 | 3 | function! gina#custom#command#option(scheme, query, ...) abort 4 | if a:query !~# '^--\?\S\+\%(|--\?\S\+\)*$' 5 | throw gina#core#revelator#error( 6 | \ 'Invalid query. See :h gina#custom#command#option' 7 | \) 8 | endif 9 | let value = get(a:000, 0, 1) 10 | let remover = type(value) == s:t_number ? s:build_remover(a:query) : '' 11 | let preference = gina#custom#preference(a:scheme, 0) 12 | call add(preference.command.options, [a:query, value, remover]) 13 | endfunction 14 | 15 | function! gina#custom#command#alias(scheme, alias, ...) abort 16 | if a:scheme =~# '^/' 17 | throw gina#core#revelator#error( 18 | \ '/{pattern} cannot be used to define a command alias' 19 | \) 20 | endif 21 | let preference = gina#custom#preference(a:alias, 0) 22 | let preference.command.origin = a:scheme 23 | let preference.command.raw = get(a:000, 0, 0) 24 | endfunction 25 | 26 | 27 | " Private -------------------------------------------------------------------- 28 | function! s:build_remover(query) abort 29 | let terms = split(a:query, '|') 30 | let names = map(copy(terms), 'matchstr(v:val, ''^--\?\zs\S\+'')') 31 | let remover = map( 32 | \ range(len(terms)), 33 | \ '(terms[v:val] =~# ''^--'' ? ''--no-'' : ''-!'') . names[v:val]' 34 | \) 35 | return join(remover, '|') 36 | endfunction 37 | -------------------------------------------------------------------------------- /autoload/gina/custom/mapping.vim: -------------------------------------------------------------------------------- 1 | function! gina#custom#mapping#map(scheme, lhs, rhs, ...) abort 2 | let options = get(a:000, 0, {}) 3 | let preference = gina#custom#preference(a:scheme, 0) 4 | call add(preference.mapping.mappings, [a:lhs, a:rhs, options]) 5 | endfunction 6 | 7 | function! gina#custom#mapping#nmap(scheme, lhs, rhs, ...) abort 8 | let options = get(a:000, 0, {}) 9 | let options.mode = 'n' 10 | call gina#custom#mapping#map(a:scheme, a:lhs, a:rhs, options) 11 | endfunction 12 | 13 | function! gina#custom#mapping#vmap(scheme, lhs, rhs, ...) abort 14 | let options = get(a:000, 0, {}) 15 | let options.mode = 'v' 16 | call gina#custom#mapping#map(a:scheme, a:lhs, a:rhs, options) 17 | endfunction 18 | 19 | function! gina#custom#mapping#imap(scheme, lhs, rhs, ...) abort 20 | let options = get(a:000, 0, {}) 21 | let options.mode = 'i' 22 | call gina#custom#mapping#map(a:scheme, a:lhs, a:rhs, options) 23 | endfunction 24 | 25 | 26 | " Private -------------------------------------------------------------------- 27 | function! s:apply_preference(preference) abort 28 | for [lhs, rhs, options] in a:preference.mapping.mappings 29 | call gina#util#map(lhs, rhs, options) 30 | endfor 31 | endfunction 32 | 33 | function! s:FileType() abort 34 | let scheme = gina#core#buffer#param('%', 'scheme') 35 | if empty(scheme) 36 | return 37 | endif 38 | for preference in gina#custom#preferences(scheme) 39 | call s:apply_preference(preference) 40 | endfor 41 | endfunction 42 | 43 | 44 | " Autocmd -------------------------------------------------------------------- 45 | augroup gina_custom_mapping_internal 46 | autocmd! * 47 | autocmd FileType * call s:FileType() 48 | augroup END 49 | -------------------------------------------------------------------------------- /autoload/vital/__gina__/Options.vim: -------------------------------------------------------------------------------- 1 | let s:t_list = type([]) 2 | let s:t_func = type(function('tr')) 3 | 4 | function! s:_vital_loaded(V) abort 5 | let s:String = a:V.import('Data.String') 6 | endfunction 7 | 8 | function! s:_vital_depends() abort 9 | return ['Data.String'] 10 | endfunction 11 | 12 | function! s:new() abort 13 | let options = copy(s:options) 14 | let options._options = {} 15 | return options 16 | endfunction 17 | 18 | 19 | " Private -------------------------------------------------------------------- 20 | function! s:_filter(arglead, candidates) abort 21 | let pattern = '^' . s:String.escape_pattern(a:arglead) 22 | let candidates = copy(a:candidates) 23 | call filter(candidates, 'v:val =~# pattern') 24 | return candidates 25 | endfunction 26 | 27 | function! s:_complete_choice(arglead, cmdline, cursorpos, choices) abort 28 | let leading = matchstr(a:arglead, '\%(-[^-]\|--\S\+=\)') 29 | let candidates = map( 30 | \ copy(a:choices), 31 | \ 'leading . v:val' 32 | \) 33 | return s:_filter(a:arglead, candidates) 34 | endfunction 35 | 36 | function! s:_complete_callback(arglead, cmdline, cursorpos, callback) abort 37 | let m = matchlist(a:arglead, '\(-[^-]\|--\S\+=\)\(.*\)') 38 | let candidates = a:callback(m[2], a:cmdline, a:cursorpos) 39 | return map( 40 | \ copy(candidates), 41 | \ 'm[1] . v:val' 42 | \) 43 | endfunction 44 | 45 | function! s:_compare_options(lhs, rhs) abort 46 | let lhs = a:lhs.names[-1] 47 | let rhs = a:rhs.names[-1] 48 | return lhs ==# rhs ? 0 : (lhs < rhs ? -1 : 1) 49 | endfunction 50 | 51 | 52 | " Options -------------------------------------------------------------------- 53 | let s:options = {} 54 | 55 | function! s:options.define(query, description, ...) abort 56 | let self._options[a:query] = extend(copy(s:option), { 57 | \ 'names': split(a:query, '|'), 58 | \ 'value': a:0 ? a:1 : v:null, 59 | \ 'description': a:description, 60 | \}) 61 | endfunction 62 | 63 | function! s:options.help(...) abort 64 | let lwidth = max(map(keys(self._options), 'len(v:val)')) + 3 65 | let rwidth = get(a:000, 0, 80) - lwidth 66 | let text = ['options:'] 67 | call map( 68 | \ sort(values(self._options), function('s:_compare_options')), 69 | \ 'extend(text, v:val.help(lwidth, rwidth))' 70 | \) 71 | redraw | echo join(text, "\n") 72 | endfunction 73 | 74 | function! s:options.complete(arglead, cmdline, cursorpos) abort 75 | let leading = matchstr(a:arglead, '^\%(-[^-]\|--\S\+=\)') 76 | let candidates = [] 77 | for option in values(self._options) 78 | if index(option.names, leading) != -1 79 | return option.complete(a:arglead, a:cmdline, a:cursorpos) 80 | endif 81 | call extend(candidates, option.names) 82 | endfor 83 | return s:_filter(a:arglead, candidates) 84 | endfunction 85 | 86 | 87 | " Option --------------------------------------------------------------------- 88 | let s:option = {} 89 | 90 | function! s:option.help(lwidth, rwidth) abort 91 | let rhs = s:String.wrap(self.description, a:rwidth) 92 | let lhs = [s:String.pad_right(join(self.names), a:lwidth)] 93 | let lhs += repeat([repeat(' ', a:lwidth)], len(rhs) - 1) 94 | return map(range(len(lhs)), 'lhs[v:val] . rhs[v:val]') 95 | endfunction 96 | 97 | function! s:option.complete(arglead, cmdline, cursorpos) abort 98 | if type(self.value) == s:t_list 99 | return s:_complete_choice(a:arglead, a:cmdline, a:cursorpos, self.value) 100 | elseif type(self.value) == s:t_func 101 | return s:_complete_callback(a:arglead, a:cmdline, a:cursorpos, self.value) 102 | endif 103 | return [] 104 | endfunction 105 | -------------------------------------------------------------------------------- /autoload/vital/__gina__/System/Store.vim: -------------------------------------------------------------------------------- 1 | let s:t_list = type([]) 2 | let s:store_cache = {} 3 | 4 | function! s:hash(pathlist) abort 5 | return sha256(join(sort(a:pathlist))) 6 | endfunction 7 | 8 | function! s:get_slug_expr() abort 9 | return 'matchstr(expand(''''), ''\zs[^. ]\+$'')' 10 | endfunction 11 | 12 | function! s:of(pathlist) abort 13 | let pathlist = type(a:pathlist) == s:t_list 14 | \ ? a:pathlist 15 | \ : [a:pathlist] 16 | let hash = s:hash(pathlist) 17 | if has_key(s:store_cache, hash) 18 | return s:store_cache[hash] 19 | endif 20 | let store = copy(s:store) 21 | let store.caches = {} 22 | let store.pathlist = copy(pathlist) 23 | lockvar store.pathlist 24 | let s:store_cache[hash] = store 25 | return store 26 | endfunction 27 | 28 | function! s:remove(pathlist) abort 29 | let pathlist = type(a:pathlist) == s:t_list 30 | \ ? a:pathlist 31 | \ : [a:pathlist] 32 | let hash = s:hash(pathlist) 33 | silent! unlet s:store_cache[hash] 34 | endfunction 35 | 36 | 37 | " Store instance ------------------------------------------------------------- 38 | let s:store = {} 39 | 40 | function! s:store.is_expired(name) abort 41 | let cache = get(self.caches, a:name, {}) 42 | if empty(cache) 43 | return 1 44 | endif 45 | for i in range(len(self.pathlist)) 46 | let uptime1 = getftime(self.pathlist[i]) 47 | let uptime2 = cache.uptimes[i] 48 | if uptime1 != uptime2 && (uptime1 == -1 || uptime2 == -1) 49 | return 1 50 | elseif uptime1 > uptime2 51 | return 1 52 | endif 53 | endfor 54 | return 0 55 | endfunction 56 | 57 | function! s:store.has(name) abort 58 | return has_key(self.caches, a:name) 59 | endfunction 60 | 61 | function! s:store.get(name, ...) abort 62 | let default = get(a:000, 0) 63 | if self.is_expired(a:name) 64 | return default 65 | endif 66 | return self.caches[a:name].cache 67 | endfunction 68 | 69 | function! s:store.set(name, value) abort 70 | let uptimes = map(copy(self.pathlist), 'getftime(v:val)') 71 | let cache = { 72 | \ 'cache': a:value, 73 | \ 'uptimes': uptimes 74 | \} 75 | let self.caches[a:name] = cache 76 | return self 77 | endfunction 78 | 79 | function! s:store.remove(name) abort 80 | silent! unlet self.caches[a:name] 81 | return self 82 | endfunction 83 | 84 | function! s:store.clear() abort 85 | let self.caches = {} 86 | return self 87 | endfunction 88 | -------------------------------------------------------------------------------- /autoload/vital/_gina.vim: -------------------------------------------------------------------------------- 1 | let s:_plugin_name = expand(':t:r') 2 | 3 | function! vital#{s:_plugin_name}#new() abort 4 | return vital#{s:_plugin_name[1:]}#new() 5 | endfunction 6 | 7 | function! vital#{s:_plugin_name}#function(funcname) abort 8 | silent! return function(a:funcname) 9 | endfunction 10 | -------------------------------------------------------------------------------- /autoload/vital/_gina/App/Emitter.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not modify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_gina#App#Emitter#import() abort', printf("return map({'subscribe': '', 'unsubscribe': '', 'emit': '', 'add_middleware': '', 'remove_middleware': ''}, \"vital#_gina#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | let s:listeners = {} 11 | let s:middlewares = [] 12 | 13 | function! s:subscribe(name, listener, ...) abort 14 | let dict = get(a:000, 0, v:null) 15 | let s:listeners[a:name] = get(s:listeners, a:name, []) 16 | call add(s:listeners[a:name], [a:listener, dict]) 17 | endfunction 18 | 19 | function! s:unsubscribe(...) abort 20 | if a:0 == 0 21 | let s:listeners = {} 22 | elseif a:0 == 1 23 | let s:listeners[a:1] = [] 24 | else 25 | let dict = a:0 == 3 ? a:3 : v:null 26 | let s:listeners[a:1] = get(s:listeners, a:1, []) 27 | let index = index(s:listeners[a:1], [a:2, dict]) 28 | if index != -1 29 | call remove(s:listeners[a:1], index) 30 | endif 31 | endif 32 | endfunction 33 | 34 | function! s:add_middleware(middleware) abort 35 | call add(s:middlewares, a:middleware) 36 | endfunction 37 | 38 | function! s:remove_middleware(...) abort 39 | if a:0 == 0 40 | let s:middlewares = [] 41 | else 42 | let index = index(s:middlewares, a:1) 43 | if index != -1 44 | call remove(s:middlewares, index) 45 | endif 46 | endif 47 | endfunction 48 | 49 | function! s:emit(name, ...) abort 50 | let attrs = copy(a:000) 51 | let listeners = copy(get(s:listeners, a:name, [])) 52 | let middlewares = map(s:middlewares, 'extend(copy(s:middleware), v:val)') 53 | for middleware in middlewares 54 | call call(middleware.on_emit_pre, [a:name, listeners, attrs], middleware) 55 | endfor 56 | for [l:Listener, dict] in listeners 57 | if empty(dict) 58 | call call(Listener, attrs) 59 | else 60 | call call(Listener, attrs, dict) 61 | endif 62 | endfor 63 | for middleware in middlewares 64 | call call(middleware.on_emit_post, [a:name, listeners, attrs], middleware) 65 | endfor 66 | endfunction 67 | 68 | 69 | " Middleware skeleton -------------------------------------------------------- 70 | let s:middleware = {} 71 | 72 | function! s:middleware.on_emit_pre(name, listeners, attrs) abort 73 | " Users can override this method 74 | endfunction 75 | 76 | function! s:middleware.on_emit_post(name, listeners, attrs) abort 77 | " Users can override this method 78 | endfunction 79 | -------------------------------------------------------------------------------- /autoload/vital/_gina/Config.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not modify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_gina#Config#import() abort', printf("return map({'define': '', 'translate': '', 'config': ''}, \"vital#_gina#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | let s:plugin_name = expand(':p:h:t') 11 | let s:plugin_name = s:plugin_name =~# '^__.\+__$' 12 | \ ? s:plugin_name[2:-3] 13 | \ : s:plugin_name =~# '^_.\+$' 14 | \ ? s:plugin_name[1:] 15 | \ : s:plugin_name 16 | 17 | function! s:define(prefix, default) abort 18 | let prefix = a:prefix =~# '^g:' ? a:prefix : 'g:' . a:prefix 19 | for [key, Value] in items(a:default) 20 | let name = prefix . '#' . key 21 | if !exists(name) 22 | execute 'let ' . name . ' = ' . string(Value) 23 | endif 24 | unlet Value 25 | endfor 26 | endfunction 27 | 28 | function! s:config(scriptfile, default) abort 29 | let prefix = s:translate(a:scriptfile) 30 | call s:define(prefix, a:default) 31 | endfunction 32 | 33 | function! s:translate(scriptfile) abort 34 | let path = fnamemodify(a:scriptfile, ':gs?\\?/?') 35 | let name = matchstr(path, printf( 36 | \ 'autoload/\zs\%%(%s\.vim\|%s/.*\)$', 37 | \ s:plugin_name, 38 | \ s:plugin_name, 39 | \)) 40 | let name = substitute(name, '\.vim$', '', '') 41 | let name = substitute(name, '/', '#', 'g') 42 | let name = substitute(name, '\%(^#\|#$\)', '', 'g') 43 | return 'g:' . name 44 | endfunction 45 | -------------------------------------------------------------------------------- /autoload/vital/_gina/Data/String/Formatter.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not modify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_gina#Data#String#Formatter#import() abort', printf("return map({'format': ''}, \"vital#_gina#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | let s:t_string = type('') 11 | let s:t_number = type(0) 12 | let s:t_list = type([]) 13 | let s:t_dict = type({}) 14 | let s:t_funcref = type(function('tr')) 15 | 16 | " format rule: 17 | " %{|} 18 | " '' if != '' 19 | " '' if == '' 20 | " %{} 21 | " '' if != '' 22 | " '' if == '' 23 | " %{|} 24 | " '' if != '' 25 | " '' if == '' 26 | function! s:format(format, format_map, data) abort 27 | if empty(a:data) 28 | return '' 29 | endif 30 | let pattern_base = '\C%\%({\([^|}]*\)\%(|\([^}]*\)\)\?}\)\?' 31 | let str = copy(a:format) 32 | for [key, Value] in items(a:format_map) 33 | let pattern = pattern_base . key 34 | if str =~# pattern 35 | if type(Value) == s:t_funcref 36 | let result = s:_smart_string(call(Value, [a:data], a:format_map)) 37 | else 38 | let result = s:_smart_string(get(a:data, Value, '')) 39 | endif 40 | let repl = strlen(result) ? '\1' . escape(result, '\') . '\2' : '' 41 | let str = substitute(str, pattern, repl, 'g') 42 | endif 43 | unlet! Value 44 | endfor 45 | return substitute(str, '^\s\+\|\s\+$', '', 'g') 46 | endfunction 47 | 48 | function! s:_smart_string(value) abort 49 | let t_value = type(a:value) 50 | if t_value == s:t_string 51 | return a:value 52 | elseif t_value == s:t_number 53 | return a:value ? string(a:value) : '' 54 | elseif t_value == s:t_list || t_value == s:t_dict 55 | return !empty(a:value) ? string(a:value) : '' 56 | else 57 | return string(a:value) 58 | endif 59 | endfunction 60 | -------------------------------------------------------------------------------- /autoload/vital/_gina/System/Cache/Base.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not modify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_gina#System#Cache#Base#import() abort', printf("return map({'_vital_depends': '', 'new': '', '_vital_loaded': ''}, \"vital#_gina#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | let s:save_cpo = &cpo 11 | set cpo&vim 12 | 13 | function! s:_vital_loaded(V) abort 14 | let s:Prelude = a:V.import('Prelude') 15 | endfunction 16 | function! s:_vital_depends() abort 17 | return ['Prelude'] 18 | endfunction 19 | 20 | let s:cache = { 21 | \ '__name__': 'base', 22 | \} 23 | function! s:new(...) abort 24 | return deepcopy(s:cache) 25 | endfunction 26 | function! s:cache.cache_key(obj) abort 27 | let cache_key = s:Prelude.is_string(a:obj) ? a:obj : string(a:obj) 28 | return cache_key 29 | endfunction 30 | function! s:cache.has(name) abort 31 | throw 'vital: System.Cache.Base: has({name}) is not implemented' 32 | endfunction 33 | function! s:cache.get(name, ...) abort 34 | throw 'vital: System.Cache.Base: get({name}[, {default}]) is not implemented' 35 | endfunction 36 | function! s:cache.set(name, value) abort 37 | throw 'vital: System.Cache.Base: set({name}, {value}[, {default}]) is not implemented' 38 | endfunction 39 | function! s:cache.keys() abort 40 | throw 'vital: System.Cache.Base: keys() is not implemented' 41 | endfunction 42 | function! s:cache.remove(name) abort 43 | throw 'vital: System.Cache.Base: remove({name}) is not implemented' 44 | endfunction 45 | function! s:cache.clear() abort 46 | throw 'vital: System.Cache.Base: clear() is not implemented' 47 | endfunction 48 | function! s:cache.on_changed() abort 49 | " A user defined hook function 50 | endfunction 51 | 52 | let &cpo = s:save_cpo 53 | unlet s:save_cpo 54 | "vim: sts=2 sw=2 smarttab et ai textwidth=0 fdm=marker 55 | -------------------------------------------------------------------------------- /autoload/vital/_gina/System/Cache/Memory.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not modify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_gina#System#Cache#Memory#import() abort', printf("return map({'_vital_depends': '', 'new': '', '_vital_loaded': ''}, \"vital#_gina#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | let s:save_cpo = &cpo 11 | set cpo&vim 12 | 13 | function! s:_vital_loaded(V) abort 14 | let s:Base = a:V.import('System.Cache.Base') 15 | endfunction 16 | function! s:_vital_depends() abort 17 | return ['System.Cache.Base'] 18 | endfunction 19 | 20 | let s:cache = { 21 | \ '_cached': {}, 22 | \ '__name__': 'memory', 23 | \} 24 | function! s:new(...) abort 25 | return extend( 26 | \ call(s:Base.new, a:000, s:Base), 27 | \ deepcopy(s:cache) 28 | \) 29 | endfunction 30 | 31 | function! s:cache.has(name) abort 32 | let cache_key = self.cache_key(a:name) 33 | return has_key(self._cached, cache_key) 34 | endfunction 35 | function! s:cache.get(name, ...) abort 36 | let default = get(a:000, 0, '') 37 | let cache_key = self.cache_key(a:name) 38 | if has_key(self._cached, cache_key) 39 | return self._cached[cache_key] 40 | else 41 | return default 42 | endif 43 | endfunction 44 | function! s:cache.set(name, value) abort 45 | let cache_key = self.cache_key(a:name) 46 | let self._cached[cache_key] = a:value 47 | call self.on_changed() 48 | endfunction 49 | function! s:cache.remove(name) abort 50 | let cache_key = self.cache_key(a:name) 51 | if has_key(self._cached, cache_key) 52 | unlet self._cached[cache_key] 53 | call self.on_changed() 54 | endif 55 | endfunction 56 | function! s:cache.keys() abort 57 | return keys(self._cached) 58 | endfunction 59 | function! s:cache.clear() abort 60 | let self._cached = {} 61 | call self.on_changed() 62 | endfunction 63 | 64 | let &cpo = s:save_cpo 65 | unlet s:save_cpo 66 | "vim: sts=2 sw=2 smarttab et ai textwidth=0 fdm=marker 67 | -------------------------------------------------------------------------------- /autoload/vital/_gina/System/Job.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not modify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_gina#System#Job#import() abort', printf("return map({'_vital_depends': '', 'is_available': '', 'start': '', '_vital_loaded': ''}, \"vital#_gina#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | let s:t_string = type('') 11 | let s:t_list = type([]) 12 | 13 | function! s:_vital_loaded(V) abort 14 | if has('nvim') 15 | let s:Job = a:V.import('System.Job.Neovim') 16 | else 17 | let s:Job = a:V.import('System.Job.Vim') 18 | endif 19 | endfunction 20 | 21 | function! s:_vital_depends() abort 22 | return [ 23 | \ 'System.Job.Vim', 24 | \ 'System.Job.Neovim', 25 | \] 26 | endfunction 27 | 28 | " Note: 29 | " Vim does not raise E902 on Unix system even the prog is not found so use a 30 | " custom exception instead to make the method compatible. 31 | " Note: 32 | " Vim/Neovim treat String a bit differently so prohibit String as well 33 | function! s:_validate_args(args) abort 34 | if type(a:args) != s:t_list 35 | throw 'vital: System.Job: Argument requires to be a List instance.' 36 | endif 37 | if len(a:args) == 0 38 | throw 'vital: System.Job: Argument vector must have at least one item.' 39 | endif 40 | let prog = a:args[0] 41 | if !executable(prog) 42 | throw printf('vital: System.Job: "%s" is not an executable', prog) 43 | endif 44 | endfunction 45 | 46 | function! s:is_available() abort 47 | return s:Job.is_available() 48 | endfunction 49 | 50 | function! s:start(args, ...) abort 51 | call s:_validate_args(a:args) 52 | return s:Job.start(a:args, a:0 ? a:1 : {}) 53 | endfunction 54 | -------------------------------------------------------------------------------- /autoload/vital/_gina/Text/INI.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not modify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_gina#Text#INI#import() abort', printf("return map({'parse': '', 'parse_record': '', 'parse_file': ''}, \"vital#_gina#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | let s:save_cpo = &cpo 11 | set cpo&vim 12 | 13 | let s:default_section = '_' 14 | let s:comment_pattern = '\v[#;].*$' 15 | let s:section_pattern = '\v^\s*\[\s*(.{-1,})\s*\]\s*$' 16 | let s:parameter_pattern = '\v^\s*([^\=]{-1,})\s*\=\s*(.{-})\s*$' 17 | 18 | function! s:parse_record(line) abort 19 | " remove comment string 20 | let line = substitute(a:line, s:comment_pattern, '', 'g') 21 | " is empty line? 22 | if line =~# '\v^\s*$' 23 | return {'type': 'emptyline'} 24 | endif 25 | " is parameter line? 26 | let m = matchlist(line, s:parameter_pattern) 27 | if len(m) > 0 28 | return {'type': 'parameter', 'key': m[1], 'value': m[2]} 29 | endif 30 | " is section line? 31 | let m = matchlist(line, s:section_pattern) 32 | if len(m) > 0 33 | return {'type': 'section', 'name': m[1]} 34 | endif 35 | " unknown format 36 | return {'type': 'unknown', 'value': line} 37 | endfunction 38 | 39 | function! s:parse(ini, ...) abort 40 | let fail_silently = get(a:000, 0, 1) 41 | let sections = {} 42 | let sections[s:default_section] = {} 43 | let current_section = s:default_section 44 | 45 | for line in split(a:ini, '\v%(\r?\n)+') 46 | let record = s:parse_record(line) 47 | if record.type ==# 'section' 48 | let current_section = record.name 49 | let sections[current_section] = get(sections, current_section, {}) 50 | elseif record.type ==# 'parameter' 51 | let sections[current_section][record.key] = record.value 52 | elseif record.type ==# 'unknown' && !fail_silently 53 | throw 'vital: Text.INI: Parsing a record failed: ' . record.value 54 | endif 55 | endfor 56 | return sections 57 | endfunction 58 | 59 | function! s:parse_file(file, ...) abort 60 | let fail_silently = get(a:000, 0, 1) 61 | return s:parse(join(readfile(a:file), "\n"), fail_silently) 62 | endfunction 63 | 64 | 65 | let &cpo = s:save_cpo 66 | unlet s:save_cpo 67 | "vim: sts=2 sw=2 smarttab et ai textwidth=0 fdm=marker 68 | -------------------------------------------------------------------------------- /autoload/vital/_gina/Vim/Buffer/ANSI.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not modify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_gina#Vim#Buffer#ANSI#import() abort', printf("return map({'_vital_created': '', 'define_syntax': ''}, \"vital#_gina#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | function! s:_vital_created(module) abort 11 | let s:COLORS = { 12 | \ '\%(30\|0;30\|30;0\)': 'AnsiColor0', 13 | \ '\%(31\|0;31\|31;0\)': 'AnsiColor1', 14 | \ '\%(32\|0;32\|32;0\)': 'AnsiColor2', 15 | \ '\%(33\|0;33\|33;0\)': 'AnsiColor3', 16 | \ '\%(34\|0;34\|34;0\)': 'AnsiColor4', 17 | \ '\%(35\|0;35\|35;0\)': 'AnsiColor5', 18 | \ '\%(36\|0;36\|36;0\)': 'AnsiColor6', 19 | \ '\%(37\|0;37\|37;0\)': 'AnsiColor7', 20 | \ '\%(1;30\|30;1\)': 'AnsiColor8', 21 | \ '\%(1;31\|31;1\)': 'AnsiColor9', 22 | \ '\%(1;32\|32;1\)': 'AnsiColor10', 23 | \ '\%(1;33\|33;1\)': 'AnsiColor11', 24 | \ '\%(1;34\|34;1\)': 'AnsiColor12', 25 | \ '\%(1;35\|35;1\)': 'AnsiColor13', 26 | \ '\%(1;36\|36;1\)': 'AnsiColor14', 27 | \ '\%(1;37\|37;1\)': 'AnsiColor15', 28 | \} 29 | call s:_define_highlight() 30 | endfunction 31 | 32 | function! s:_define_highlight() abort 33 | " Ref: https://github.com/w0ng/vim-hybrid 34 | highlight default AnsiColor0 ctermfg=0 guifg=#282A2E 35 | highlight default AnsiColor1 ctermfg=1 guifg=#A54242 36 | highlight default AnsiColor2 ctermfg=2 guifg=#8C9440 37 | highlight default AnsiColor3 ctermfg=3 guifg=#DE935F 38 | highlight default AnsiColor4 ctermfg=4 guifg=#5F819D 39 | highlight default AnsiColor5 ctermfg=5 guifg=#85678F 40 | highlight default AnsiColor6 ctermfg=6 guifg=#5E8D87 41 | highlight default AnsiColor7 ctermfg=7 guifg=#707880 42 | highlight default AnsiColor8 ctermfg=8 guifg=#373B41 43 | highlight default AnsiColor9 ctermfg=9 guifg=#CC6666 44 | highlight default AnsiColor10 ctermfg=10 guifg=#B5BD68 45 | highlight default AnsiColor11 ctermfg=11 guifg=#F0C674 46 | highlight default AnsiColor12 ctermfg=12 guifg=#81A2BE 47 | highlight default AnsiColor13 ctermfg=13 guifg=#B294BB 48 | highlight default AnsiColor14 ctermfg=14 guifg=#8ABEB7 49 | highlight default AnsiColor15 ctermfg=15 guifg=#C5C8C6 50 | augroup vital_vim_buffer_ansi 51 | autocmd! * 52 | autocmd ColorScheme * call s:_define_highlight() 53 | augroup END 54 | endfunction 55 | 56 | function! s:define_syntax(...) abort 57 | let prefix = get(a:000, 0, '') 58 | execute printf( 59 | \ 'syntax match %sAnsiSuppress conceal /\e\[[0-9A-Z;]*m/', 60 | \ prefix 61 | \) 62 | for [code, name] in items(s:COLORS) 63 | execute printf( 64 | \ 'syn region %s%s contains=%s keepend start=/\e\[%sm/ end=/\e\[[0-9A-Z;]*m/', 65 | \ prefix, name, 'AnsiSuppress', code 66 | \) 67 | execute printf( 68 | \ 'syntax cluster %sAnsiColors add=%s%s', 69 | \ prefix, prefix, name 70 | \) 71 | endfor 72 | setlocal conceallevel=3 concealcursor=nvic 73 | endfunction 74 | -------------------------------------------------------------------------------- /autoload/vital/_gina/Vim/Buffer/Group.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not modify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_gina#Vim#Buffer#Group#import() abort', printf("return map({'new': ''}, \"vital#_gina#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | let s:groups = {} 11 | 12 | function! s:new(...) abort 13 | let options = extend({ 14 | \ 'on_close_fail': v:null, 15 | \}, get(a:000, 0, {}) 16 | \) 17 | let hash = sha256(reltimestr(reltime())) 18 | let s:groups[hash] = { 19 | \ 'add': function('s:_group_add'), 20 | \ 'close': function('s:_group_close'), 21 | \} 22 | let s:groups[hash].__hash = hash 23 | let s:groups[hash].__tabnr = v:null 24 | let s:groups[hash].__members = [] 25 | let s:groups[hash].__on_close_fail = options.on_close_fail 26 | return s:groups[hash] 27 | endfunction 28 | 29 | function! s:_group_add(...) abort dict 30 | let options = extend({ 31 | \ 'keep': 0, 32 | \ 'expr': '%', 33 | \}, get(a:000, 0, {}) 34 | \) 35 | let bufnr = bufnr(options.expr) 36 | let winid = bufwinid(options.expr) 37 | let tabnr = tabpagenr() 38 | if self.__tabnr is# v:null 39 | let self.__tabnr = tabnr 40 | elseif tabnr != self.__tabnr 41 | throw printf( 42 | \ 'vital: Vim.Buffer.Group: %s', 43 | \ 'A buffer on a different tabpage cannot be added.' 44 | \) 45 | endif 46 | call add(self.__members, { 47 | \ 'bufnr': bufnr, 48 | \ 'winid': winid, 49 | \ 'options': options, 50 | \}) 51 | execute printf('augroup vital_vim_buffer_group_%s', self.__hash) 52 | execute printf('autocmd! * ', bufnr) 53 | execute printf('autocmd WinLeave call s:_on_WinLeave(''%s'')', bufnr, self.__hash) 54 | execute 'augroup END' 55 | endfunction 56 | 57 | function! s:_group_close() abort dict 58 | for member in self.__members 59 | if member.options.keep 60 | continue 61 | endif 62 | let winnr = win_id2win(member.winid) 63 | if winnr == 0 || getbufvar(member.bufnr, '&modified') || bufwinid(member.bufnr) != member.winid 64 | continue 65 | endif 66 | try 67 | execute printf('%dclose', winnr) 68 | catch /^Vim\%((\a\+)\)\=:E444/ 69 | " E444: Cannot close last window may thrown but ignore that 70 | " Vim.Buffer.Group should NOT close the last window so ignore 71 | " this exception silently. 72 | if self.__on_close_fail isnot# v:null 73 | call call(self.__on_close_fail, [winnr, member], self) 74 | endif 75 | endtry 76 | endfor 77 | endfunction 78 | 79 | 80 | function! s:_on_WinLeave(hash) abort 81 | execute 'augroup vital_vim_buffer_group_temporal_' . a:hash 82 | execute 'autocmd! *' 83 | execute printf( 84 | \ 'autocmd WinEnter * nested call s:_on_WinEnter(''%s'', %d)', 85 | \ a:hash, winnr('$'), 86 | \) 87 | execute 'augroup END' 88 | endfunction 89 | 90 | function! s:_on_WinEnter(hash, nwin) abort 91 | execute 'augroup vital_vim_buffer_group_temporal_' . a:hash 92 | execute 'autocmd! *' 93 | execute 'augroup END' 94 | if winnr('$') < a:nwin 95 | call s:groups[a:hash].close() 96 | endif 97 | endfunction 98 | -------------------------------------------------------------------------------- /autoload/vital/_gina/Vim/Highlight.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not modify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_gina#Vim#Highlight#import() abort', printf("return map({'set': '', 'get': ''}, \"vital#_gina#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | function! s:get(...) abort 11 | let name = a:0 ? a:1 : '' 12 | let records = split(s:_highlight(name), '\r\?\n') 13 | let highlights = map(records, 's:_parse_record(v:val)') 14 | let highlights = filter(highlights, '!empty(v:val)') 15 | return a:0 ? highlights[0] : highlights 16 | endfunction 17 | 18 | function! s:set(highlight, ...) abort 19 | let options = extend({ 20 | \ 'force': 0, 21 | \ 'default': 0, 22 | \}, get(a:000, 0, {}) 23 | \) 24 | let name = a:highlight.name 25 | let force = options.force ? '!' : '' 26 | let default = options.default ? 'default' : '' 27 | if get(a:highlight.attrs, 'clear') 28 | execute 'highlight' 'clear' name 29 | elseif !empty(get(a:highlight.attrs, 'link')) 30 | execute 'highlight' . force default 'link' name a:highlight.attrs.link 31 | else 32 | let attrs = map(items(a:highlight.attrs), 'v:val[0] . ''='' . v:val[1]') 33 | execute 'highlight' default name join(attrs) 34 | endif 35 | endfunction 36 | 37 | 38 | function! s:_parse_record(record) abort 39 | let m = matchlist(a:record, '^\(\S\+\)\s\+xxx\s\(.*\)$') 40 | if empty(m) 41 | return {} 42 | endif 43 | let name = m[1] 44 | let attrs = s:_parse_attrs(m[2]) 45 | return {'name': name, 'attrs': attrs} 46 | endfunction 47 | 48 | function! s:_parse_attrs(attrs) abort 49 | if a:attrs ==# 'cleared' 50 | return { 'cleared': 1 } 51 | elseif a:attrs =~# '^links to' 52 | return { 'link': matchstr(a:attrs, 'links to \zs.*') } 53 | endif 54 | let attrs = {} 55 | for term in split(a:attrs, ' ') 56 | let m = split(term, '=') 57 | if len(m) < 2 58 | continue 59 | endif 60 | let attrs[m[0]] = join(m[1:], '=') 61 | endfor 62 | return attrs 63 | endfunction 64 | 65 | function! s:_highlight(name) abort 66 | return execute(printf('highlight %s', a:name)) 67 | endfunction 68 | -------------------------------------------------------------------------------- /autoload/vital/_gina/Vim/Type.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not modify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_gina#Vim#Type#import() abort', printf("return map({'is_comparable': '', '_vital_created': '', 'is_predicate': '', 'is_numeric': '', 'is_special': ''}, \"vital#_gina#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | let s:typelist = ['number', 'string', 'func', 'list', 'dict', 'float', 'bool', 'none', 'job', 'channel', 'blob'] 11 | 12 | let s:types = {} 13 | let s:type_names = {} 14 | 15 | for s:idx in range(len(s:typelist)) 16 | let s:types[s:typelist[s:idx]] = s:idx 17 | let s:type_names[s:idx] = s:typelist[s:idx] 18 | endfor 19 | unlet s:idx 20 | unlet s:typelist 21 | 22 | lockvar 1 s:types 23 | lockvar 1 s:type_names 24 | 25 | function! s:_vital_created(module) abort 26 | let a:module.types = s:types 27 | let a:module.type_names = s:type_names 28 | endfunction 29 | 30 | 31 | function! s:is_numeric(value) abort 32 | let t = type(a:value) 33 | return t == s:types.number || t == s:types.float 34 | endfunction 35 | 36 | function! s:is_special(value) abort 37 | let t = type(a:value) 38 | return t == s:types.bool || t == s:types.none 39 | endfunction 40 | 41 | function! s:is_predicate(value) abort 42 | let t = type(a:value) 43 | return t == s:types.number || t == s:types.string || 44 | \ t == s:types.bool || t == s:types.none 45 | endfunction 46 | 47 | function! s:is_comparable(value1, value2) abort 48 | if !exists('s:is_comparable_cache') 49 | let s:is_comparable_cache = s:_make_is_comparable_cache() 50 | endif 51 | return s:is_comparable_cache[type(a:value1)][type(a:value2)] 52 | endfunction 53 | 54 | function! s:_make_is_comparable_cache() abort 55 | let vals = [ 56 | \ 0, '', function('type'), [], {}, 0.0, 57 | \ get(v:, 'false'), 58 | \ get(v:, 'null'), 59 | \ exists('*test_null_job') ? test_null_job() : 0, 60 | \ exists('*test_null_channel') ? test_null_channel() : 0, 61 | \ ] 62 | if has('patch-8.1.0735') 63 | let vals += [0z00] 64 | endif 65 | 66 | let result = [] 67 | for l:V1 in vals 68 | let result_V1 = [] 69 | let result += [result_V1] 70 | for l:V2 in vals 71 | try 72 | let _ = V1 == V2 73 | let result_V1 += [1] 74 | catch 75 | let result_V1 += [0] 76 | endtry 77 | unlet V2 78 | endfor 79 | unlet V1 80 | endfor 81 | return result 82 | endfunction 83 | -------------------------------------------------------------------------------- /autoload/vital/_gina/Vim/Window.vim: -------------------------------------------------------------------------------- 1 | " ___vital___ 2 | " NOTE: lines between '" ___vital___' is generated by :Vitalize. 3 | " Do not modify the code nor insert new lines before '" ___vital___' 4 | function! s:_SID() abort 5 | return matchstr(expand(''), '\zs\d\+\ze__SID$') 6 | endfunction 7 | execute join(['function! vital#_gina#Vim#Window#import() abort', printf("return map({'focus_buffer': '', 'focus_window': ''}, \"vital#_gina#function('%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") 8 | delfunction s:_SID 9 | " ___vital___ 10 | let s:t_string = type('') 11 | let s:DEFAULT_OPTIONS = { 12 | \ 'range': 'tabpage', 13 | \} 14 | 15 | function! s:focus_window(expr, ...) abort 16 | let options = extend(copy(s:DEFAULT_OPTIONS), a:0 ? a:1 : {}) 17 | let winid = s:_find_nearest_window_winid(a:expr, options.range) 18 | if winid == 0 19 | return v:null 20 | endif 21 | let guard = copy(s:guard) 22 | let guard.winid = win_getid() 23 | if winid != guard.winid 24 | call win_gotoid(winid) 25 | endif 26 | return guard 27 | endfunction 28 | 29 | function! s:focus_buffer(expr, ...) abort 30 | let options = extend(copy(s:DEFAULT_OPTIONS), a:0 ? a:1 : {}) 31 | let winid = s:_find_nearest_buffer_winid(a:expr, options.range) 32 | if winid == 0 33 | return v:null 34 | endif 35 | let guard = copy(s:guard) 36 | let guard.winid = win_getid() 37 | if winid != guard.winid 38 | call win_gotoid(winid) 39 | endif 40 | return guard 41 | endfunction 42 | 43 | 44 | " Guard ---------------------------------------------------------------------- 45 | let s:guard = {} 46 | 47 | function! s:guard.restore() abort 48 | let winid = self.winid 49 | if winid != win_getid() 50 | call win_gotoid(winid) 51 | endif 52 | endfunction 53 | 54 | 55 | " Private -------------------------------------------------------------------- 56 | function! s:_find_nearest_window_winid(expr, range) abort 57 | let ntabpages = tabpagenr('$') 58 | if a:range ==# 'tabpage' || ntabpages == 1 59 | return win_getid(type(a:expr) == s:t_string ? winnr(a:expr) : a:expr) 60 | endif 61 | let Distance = function('s:_distance', [tabpagenr()]) 62 | for tabpagenr in sort(range(1, ntabpages), Distance) 63 | let winnr = type(a:expr) == s:t_string 64 | \ ? tabpagewinnr(tabpagenr, a:expr) 65 | \ : a:expr 66 | if winnr > 0 && winnr <= tabpagewinnr(tabpagenr, '$') 67 | return win_getid(winnr, tabpagenr) 68 | endif 69 | endfor 70 | return 0 71 | endfunction 72 | 73 | function! s:_find_nearest_buffer_winid(expr, range) abort 74 | let bufnr = type(a:expr) == s:t_string ? bufnr(a:expr) : a:expr 75 | let ntabpages = tabpagenr('$') 76 | if a:range ==# 'tabpage' || ntabpages == 1 77 | return win_getid(bufwinnr(bufnr)) 78 | endif 79 | let Distance = function('s:_distance', [tabpagenr()]) 80 | for tabpagenr in sort(range(1, ntabpages), Distance) 81 | let s:base = tabpagewinnr(tabpagenr) 82 | let buflist = tabpagebuflist(tabpagenr) 83 | for winnr in sort(range(1, len(buflist)), Distance) 84 | if buflist[winnr - 1] == bufnr 85 | return win_getid(winnr, tabpagenr) 86 | endif 87 | endfor 88 | endfor 89 | return 0 90 | endfunction 91 | 92 | function! s:_distance(base, a, b) abort 93 | return abs(a:a - a:base) - abs(a:b - a:base) 94 | endfunction 95 | -------------------------------------------------------------------------------- /autoload/vital/gina.vital: -------------------------------------------------------------------------------- 1 | gina 2 | 2f39c1dac08f8239f0ea776b857e3c9b9d3e5fdb 3 | 4 | System.File 5 | Data.List 6 | Vim.Buffer.Opener 7 | Vim.Console 8 | Config 9 | System.Cache.Memory 10 | Data.String.Formatter 11 | System.Job 12 | Vim.Buffer.ANSI 13 | Vim.Window 14 | DateTime 15 | App.Emitter 16 | Vim.Buffer.Writer 17 | Vim.Buffer.Group 18 | App.Revelator 19 | Vim.Highlight 20 | -------------------------------------------------------------------------------- /ftplugin/gina-blame.vim: -------------------------------------------------------------------------------- 1 | setlocal nomodeline 2 | setlocal nobuflisted 3 | setlocal nolist nospell 4 | setlocal nowrap nofoldenable 5 | setlocal nonumber norelativenumber 6 | setlocal foldcolumn=0 colorcolumn=0 7 | 8 | call gina#action#include('blame') 9 | call gina#action#include('browse') 10 | call gina#action#include('changes') 11 | call gina#action#include('compare') 12 | call gina#action#include('diff') 13 | call gina#action#include('show') 14 | call gina#action#include('yank') 15 | call gina#action#include('ls') 16 | 17 | if g:gina#command#blame#use_default_aliases 18 | call gina#action#shorten('blame') 19 | endif 20 | 21 | if g:gina#command#blame#use_default_mappings 22 | nmap (gina-blame-open) 23 | nmap (gina-blame-back) 24 | nmap (gina-blame-C-L) 25 | endif 26 | -------------------------------------------------------------------------------- /ftplugin/gina-branch.vim: -------------------------------------------------------------------------------- 1 | setlocal nomodeline 2 | setlocal nobuflisted 3 | setlocal nolist nospell 4 | setlocal nowrap nofoldenable 5 | setlocal nonumber norelativenumber 6 | setlocal foldcolumn=0 colorcolumn=0 7 | 8 | call gina#action#include('branch') 9 | call gina#action#include('browse') 10 | call gina#action#include('changes') 11 | call gina#action#include('commit') 12 | call gina#action#include('show') 13 | call gina#action#include('yank') 14 | call gina#action#include('ls') 15 | 16 | if g:gina#command#branch#use_default_aliases 17 | call gina#action#shorten('branch') 18 | call gina#action#alias('checkout', 'commit:checkout') 19 | call gina#action#alias('checkout:track', 'commit:checkout:track') 20 | endif 21 | 22 | if g:gina#command#branch#use_default_mappings 23 | nmap (gina-commit-checkout) 24 | endif 25 | -------------------------------------------------------------------------------- /ftplugin/gina-changes.vim: -------------------------------------------------------------------------------- 1 | setlocal nomodeline 2 | setlocal nobuflisted 3 | setlocal nolist nospell 4 | setlocal nowrap nofoldenable 5 | setlocal nonumber norelativenumber 6 | setlocal foldcolumn=0 colorcolumn=0 7 | 8 | call gina#action#include('browse') 9 | call gina#action#include('compare') 10 | call gina#action#include('diff') 11 | call gina#action#include('edit') 12 | call gina#action#include('show') 13 | call gina#action#include('yank') 14 | 15 | if g:gina#command#changes#use_default_aliases 16 | call gina#action#shorten('edit') 17 | endif 18 | 19 | if g:gina#command#changes#use_default_mappings 20 | nmap (gina-edit)zv 21 | 22 | nmap dd (gina-diff) 23 | nmap DD (gina-diff-vsplit) 24 | 25 | nmap cc (gina-compare) 26 | nmap CC (gina-compare-tab) 27 | endif 28 | -------------------------------------------------------------------------------- /ftplugin/gina-commit.vim: -------------------------------------------------------------------------------- 1 | setlocal nomodeline 2 | setlocal nobuflisted 3 | setlocal nolist 4 | setlocal nowrap nofoldenable 5 | setlocal nonumber norelativenumber 6 | setlocal foldcolumn=0 colorcolumn=0 7 | 8 | if g:gina#command#commit#use_default_mappings 9 | nmap ! (gina-commit-amend) 10 | endif 11 | -------------------------------------------------------------------------------- /ftplugin/gina-grep.vim: -------------------------------------------------------------------------------- 1 | setlocal nomodeline 2 | setlocal nobuflisted 3 | setlocal nolist nospell 4 | setlocal nowrap nofoldenable 5 | setlocal nonumber norelativenumber 6 | setlocal foldcolumn=0 colorcolumn=0 7 | 8 | call gina#action#include('browse') 9 | call gina#action#include('compare') 10 | call gina#action#include('diff') 11 | call gina#action#include('edit') 12 | call gina#action#include('export') 13 | call gina#action#include('show') 14 | call gina#action#include('yank') 15 | call gina#action#include('ls') 16 | 17 | " Does this buffer points files on working-tree or index/commit? 18 | let s:is_worktree = empty(gina#core#buffer#param('%', 'rev')) 19 | 20 | if g:gina#command#grep#use_default_aliases 21 | if s:is_worktree 22 | call gina#action#shorten('edit') 23 | else 24 | call gina#action#shorten('show') 25 | endif 26 | endif 27 | 28 | if g:gina#command#grep#use_default_mappings 29 | if s:is_worktree 30 | nmap (gina-edit)zv 31 | else 32 | nmap (gina-show)zv 33 | endif 34 | endif 35 | -------------------------------------------------------------------------------- /ftplugin/gina-log.vim: -------------------------------------------------------------------------------- 1 | setlocal nomodeline 2 | setlocal nobuflisted 3 | setlocal nolist nospell 4 | setlocal nowrap nofoldenable 5 | setlocal nonumber norelativenumber 6 | setlocal foldcolumn=0 colorcolumn=0 7 | 8 | call gina#action#include('browse') 9 | call gina#action#include('changes') 10 | call gina#action#include('commit') 11 | call gina#action#include('compare') 12 | call gina#action#include('diff') 13 | call gina#action#include('edit') 14 | call gina#action#include('show') 15 | call gina#action#include('yank') 16 | call gina#action#include('ls') 17 | 18 | if g:gina#command#log#use_default_aliases 19 | call gina#action#shorten('commit') 20 | call gina#action#shorten('show') 21 | endif 22 | 23 | if g:gina#command#log#use_default_mappings 24 | nmap (gina-show)zv 25 | endif 26 | -------------------------------------------------------------------------------- /ftplugin/gina-ls.vim: -------------------------------------------------------------------------------- 1 | setlocal nomodeline 2 | setlocal nobuflisted 3 | setlocal nolist nospell 4 | setlocal nowrap nofoldenable 5 | setlocal nonumber norelativenumber 6 | setlocal foldcolumn=0 colorcolumn=0 7 | 8 | call gina#action#include('browse') 9 | call gina#action#include('changes') 10 | call gina#action#include('compare') 11 | call gina#action#include('diff') 12 | call gina#action#include('edit') 13 | call gina#action#include('show') 14 | call gina#action#include('yank') 15 | 16 | " Does this buffer points files on working-tree or index/commit? 17 | let s:is_worktree = empty(gina#core#buffer#param('%', 'rev')) 18 | 19 | if g:gina#command#ls#use_default_aliases 20 | if s:is_worktree 21 | call gina#action#shorten('edit') 22 | else 23 | call gina#action#shorten('show') 24 | endif 25 | endif 26 | 27 | if g:gina#command#ls#use_default_mappings 28 | if s:is_worktree 29 | nmap (gina-edit)zv 30 | else 31 | nmap (gina-show)zv 32 | endif 33 | endif 34 | -------------------------------------------------------------------------------- /ftplugin/gina-reflog.vim: -------------------------------------------------------------------------------- 1 | setlocal nomodeline 2 | setlocal nobuflisted 3 | setlocal nolist nospell 4 | setlocal nowrap nofoldenable 5 | setlocal nonumber norelativenumber 6 | setlocal foldcolumn=0 colorcolumn=0 7 | 8 | call gina#action#include('changes') 9 | call gina#action#include('commit') 10 | call gina#action#include('show') 11 | call gina#action#include('yank') 12 | 13 | if g:gina#command#reflog#use_default_aliases 14 | call gina#action#shorten('commit') 15 | call gina#action#shorten('show') 16 | endif 17 | 18 | if g:gina#command#reflog#use_default_mappings 19 | nmap (gina-show)zv 20 | endif 21 | -------------------------------------------------------------------------------- /ftplugin/gina-stash-list.vim: -------------------------------------------------------------------------------- 1 | setlocal nomodeline 2 | setlocal nobuflisted 3 | setlocal nolist nospell 4 | setlocal nowrap nofoldenable 5 | setlocal nonumber norelativenumber 6 | setlocal foldcolumn=0 colorcolumn=0 7 | 8 | call gina#action#include('diff') 9 | call gina#action#include('stash') 10 | call gina#action#include('yank') 11 | 12 | if g:gina#command#stash#list#use_default_aliases 13 | call gina#action#shorten('stash') 14 | endif 15 | 16 | if g:gina#command#stash#list#use_default_mappings 17 | nmap (gina-stash-show)zv 18 | endif 19 | -------------------------------------------------------------------------------- /ftplugin/gina-stash-show.vim: -------------------------------------------------------------------------------- 1 | setlocal nomodeline 2 | setlocal nobuflisted 3 | setlocal nolist nospell 4 | setlocal nowrap nofoldenable 5 | setlocal nonumber norelativenumber 6 | setlocal foldcolumn=0 colorcolumn=0 7 | 8 | call gina#action#include('compare') 9 | call gina#action#include('diff') 10 | call gina#action#include('edit') 11 | call gina#action#include('show') 12 | call gina#action#include('yank') 13 | 14 | if g:gina#command#stash#show#use_default_aliases 15 | call gina#action#shorten('show') 16 | endif 17 | 18 | if g:gina#command#stash#show#use_default_mappings 19 | nmap (gina-show)zv 20 | 21 | nmap dd (gina-diff) 22 | nmap DD (gina-diff-vsplit) 23 | 24 | nmap cc (gina-compare) 25 | nmap CC (gina-compare-tab) 26 | endif 27 | -------------------------------------------------------------------------------- /ftplugin/gina-status.vim: -------------------------------------------------------------------------------- 1 | setlocal nomodeline 2 | setlocal nobuflisted 3 | setlocal nolist nospell 4 | setlocal nowrap nofoldenable 5 | setlocal nonumber norelativenumber 6 | setlocal foldcolumn=0 colorcolumn=0 7 | 8 | call gina#action#include('browse') 9 | call gina#action#include('chaperon') 10 | call gina#action#include('compare') 11 | call gina#action#include('diff') 12 | call gina#action#include('edit') 13 | call gina#action#include('export') 14 | call gina#action#include('index') 15 | call gina#action#include('patch') 16 | call gina#action#include('show') 17 | call gina#action#include('yank') 18 | 19 | if g:gina#command#status#use_default_aliases 20 | call gina#action#shorten('edit') 21 | call gina#action#shorten('index') 22 | endif 23 | 24 | if g:gina#command#status#use_default_mappings 25 | nmap (gina-edit)zv 26 | 27 | nmap dd (gina-diff) 28 | nmap DD (gina-diff-vsplit) 29 | 30 | nmap cc (gina-compare) 31 | nmap CC (gina-compare-tab) 32 | 33 | nmap pp (gina-patch) 34 | nmap PP (gina-patch-tab) 35 | 36 | nmap !! (gina-chaperon) 37 | 38 | nmap << (gina-index-stage) 39 | nmap >> (gina-index-unstage) 40 | nmap -- (gina-index-toggle) 41 | nmap == (gina-index-discard) 42 | vmap << (gina-index-stage) 43 | vmap >> (gina-index-unstage) 44 | vmap -- (gina-index-toggle) 45 | vmap == (gina-index-discard) 46 | endif 47 | -------------------------------------------------------------------------------- /ftplugin/gina-tag.vim: -------------------------------------------------------------------------------- 1 | setlocal nomodeline 2 | setlocal nobuflisted 3 | setlocal nolist nospell 4 | setlocal nowrap nofoldenable 5 | setlocal nonumber norelativenumber 6 | setlocal foldcolumn=0 colorcolumn=0 7 | 8 | call gina#action#include('browse') 9 | call gina#action#include('changes') 10 | call gina#action#include('commit') 11 | call gina#action#include('show') 12 | call gina#action#include('tag') 13 | call gina#action#include('yank') 14 | call gina#action#include('ls') 15 | 16 | if g:gina#command#tag#use_default_aliases 17 | call gina#action#shorten('show') 18 | call gina#action#shorten('tag') 19 | call gina#action#alias('checkout', 'commit:checkout') 20 | call gina#action#alias('checkout:track', 'commit:checkout:track') 21 | endif 22 | 23 | if g:gina#command#tag#use_default_mappings 24 | nmap (gina-show)zv 25 | endif 26 | -------------------------------------------------------------------------------- /ftplugin/gitrebase/gina.vim: -------------------------------------------------------------------------------- 1 | if get(g:, 'gina_gitrebase_support_mappings', 1) is# 0 2 | finish 3 | endif 4 | 5 | let s:COMMANDS = [ 6 | \ 'p', 'pick', 7 | \ 'r', 'reword', 8 | \ 'e', 'edit', 9 | \ 's', 'squash', 10 | \ 'f', 'fixup', 11 | \ 'x', 'exec', 12 | \ 'd', 'drop', 13 | \] 14 | 15 | function! s:find_commit() abort 16 | let line = getline('.') 17 | let prefix = printf('^\%%(%s\)', join(s:COMMANDS, '\|')) 18 | if line !~# prefix 19 | return 20 | endif 21 | return matchstr(line, prefix . ' \zs[0-9a-fA-F]\+') 22 | endfunction 23 | 24 | function! s:open_commit() abort 25 | let commit = s:find_commit() 26 | if empty(commit) 27 | return 28 | endif 29 | execute printf('Gina show --group=commit-preview --opener=vsplit +wincmd\ p %s', commit) 30 | endfunction 31 | 32 | function! s:open_changes() abort 33 | let commit = s:find_commit() 34 | if empty(commit) 35 | return 36 | endif 37 | execute printf('Gina changes --group=commit-preview --opener=vsplit +wincmd\ p %s', commit) 38 | endfunction 39 | 40 | function! s:round_command(value) abort 41 | let command = matchstr(getline('.'), '^\w\+') 42 | let index = index(s:COMMANDS, command) 43 | if index is# -1 44 | return 45 | endif 46 | let new_index = (index + a:value) % len(s:COMMANDS) 47 | execute printf('s/^%s/%s/', command, s:COMMANDS[new_index]) 48 | endfunction 49 | 50 | nnoremap (gina-rebase-open) :call open_commit() 51 | nnoremap (gina-rebase-changes) :call open_changes() 52 | nnoremap (gina-rebase-round-up) :call round_command(2) 53 | nnoremap (gina-rebase-round-down) :call round_command(-2) 54 | 55 | nmap (gina-rebase-open) 56 | nmap g (gina-rebase-changes) 57 | nmap (gina-rebase-round-up) 58 | nmap (gina-rebase-round-down) 59 | -------------------------------------------------------------------------------- /plugin/gina.vim: -------------------------------------------------------------------------------- 1 | if exists('g:loaded_gina') || v:version < 800 2 | finish 3 | endif 4 | let g:loaded_gina = 1 5 | 6 | command! -nargs=+ -range=% -bang 7 | \ -complete=customlist,gina#command#complete 8 | \ Gina 9 | \ call gina#command#call(, [, ], , ) 10 | -------------------------------------------------------------------------------- /scripts/askpass.mac: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env osascript 2 | #--------------------------------------------------------------------------------------- 3 | # git askpass via AppleScript (For macOS) 4 | # 5 | # Author: lambdalisue 6 | # License: MIT License 7 | # 8 | # Usage: 9 | # git config --global core.askpass={path to this script} 10 | # 11 | # Reference: 12 | # http://blog.thefrontiergroup.com.au/2008/12/prompting-for-a-password-with-applescript/ 13 | # http://stackoverflow.com/questions/15605288/print-to-stdout-with-applescript 14 | # https://github.com/git/git/blob/35f6318d44379452d8d33e880d8df0267b4a0cd0/prompt.c#L7 15 | #--------------------------------------------------------------------------------------- 16 | on run argv 17 | set prompt to (item 1 of argv) 18 | if prompt contains "Username" 19 | set input to display dialog prompt ¬ 20 | with title "Username" ¬ 21 | with icon caution ¬ 22 | default answer "" ¬ 23 | buttons {"Cancel", "OK"} default button 2 ¬ 24 | giving up after 295 25 | else 26 | set input to display dialog prompt ¬ 27 | with title "Password" ¬ 28 | with icon caution ¬ 29 | default answer "" ¬ 30 | buttons {"Cancel", "OK"} default button 2 ¬ 31 | giving up after 295 ¬ 32 | with hidden answer 33 | end if 34 | return the text returned of the input 35 | end run 36 | -------------------------------------------------------------------------------- /scripts/askpass.zenity: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | #--------------------------------------------------------------------------------------- 3 | # git askpass via zenity (For Linux) 4 | # 5 | # Author: lambdalisue 6 | # License: MIT License 7 | # 8 | # Usage: 9 | # git config --global core.askpass={path to this script} 10 | # 11 | # Reference: 12 | # https://help.gnome.org/users/zenity/stable/index.html.ja 13 | # https://github.com/git/git/blob/35f6318d44379452d8d33e880d8df0267b4a0cd0/prompt.c#L7 14 | #--------------------------------------------------------------------------------------- 15 | if ! type zenity >/dev/null 2>&1; then 16 | (>&2 echo "zenity is not found. Please install it first.") 17 | exit 1 18 | fi 19 | 20 | PROMPT=$1 21 | if [[ -z $PROMPT ]]; then 22 | (>&2 echo "a first argument is not specified") 23 | exit 1 24 | fi 25 | 26 | if [[ "$PROMPT" =~ Username ]]; then 27 | zenity --entry \ 28 | --title="Username" \ 29 | --text="$PROMPT" \ 30 | 2>/dev/null 31 | else 32 | zenity --entry \ 33 | --title="Password" \ 34 | --text="$PROMPT" \ 35 | --hide-text \ 36 | 2>/dev/null 37 | fi 38 | -------------------------------------------------------------------------------- /syntax/gina-_events.vim: -------------------------------------------------------------------------------- 1 | if exists('b:current_syntax') 2 | finish 3 | endif 4 | 5 | syntax match GinaEventsPrefix /^[^:]\+/ 6 | syntax match GinaEventsTime /\d\{2}:\d\{2}:\d\{2}\.\d\{6}/ 7 | syntax match GinaEventsComment /^| .*$/ 8 | syntax match GinaEventsComment /<.\{-}>$/ 9 | 10 | function! s:define_highlights() abort 11 | highlight default link GinaEventsComment Comment 12 | highlight default link GinaEventsPrefix Statement 13 | highlight default link GinaEventsTime Title 14 | endfunction 15 | 16 | augroup gina_syntax__events_internal 17 | autocmd! * 18 | autocmd ColorScheme * call s:define_highlights() 19 | augroup END 20 | 21 | call s:define_highlights() 22 | 23 | let b:current_syntax = 'gina-_events' 24 | -------------------------------------------------------------------------------- /syntax/gina-blame.vim: -------------------------------------------------------------------------------- 1 | scriptencoding utf-8 2 | 3 | if exists('b:current_syntax') 4 | finish 5 | endif 6 | 7 | " Ref: https://github.com/w0ng/vim-hybrid 8 | let s:GUI_COLORS = [ 9 | \ '#282A2E', '#A54242', '#8C9440', '#DE935F', 10 | \ '#5F819D', '#85678F', '#5E8D87', '#707880', 11 | \ '#373B41', '#CC6666', '#B5BD68', '#F0C674', 12 | \ '#81A2BE', '#B294BB', '#8ABEB7', '#C5C8C6', 13 | \] 14 | let s:TERM_COLORS = [ 15 | \ 0, 1, 2, 3, 4, 5, 6, 7, 16 | \ 8, 9, 10, 11, 12, 13, 14, 15, 17 | \] 18 | 19 | syntax match GinaBlameBase /.*/ display 20 | syntax match GinaBlameSummary /^.*\ze\ 64 | autocmd ColorScheme * call s:define_highlights() 65 | augroup END 66 | 67 | call s:define_highlights() 68 | let b:current_syntax = 'gina-blame' 69 | -------------------------------------------------------------------------------- /syntax/gina-branch.vim: -------------------------------------------------------------------------------- 1 | if exists('b:current_syntax') 2 | finish 3 | endif 4 | 5 | let s:ANSI = vital#gina#import('Vim.Buffer.ANSI') 6 | call s:ANSI.define_syntax() 7 | 8 | let b:current_syntax = 'gina-branch' 9 | -------------------------------------------------------------------------------- /syntax/gina-changes.vim: -------------------------------------------------------------------------------- 1 | if exists('b:current_syntax') 2 | finish 3 | endif 4 | 5 | syntax match GinaChangesAdded /^\d\+/ nextgroup=GinaChangesRemoved skipwhite 6 | syntax match GinaChangesRemoved /\d\+/ nextgroup=GinaChangesPath skipwhite contained 7 | syntax match GinaChangesPath /.\+/ contained 8 | 9 | function! s:define_highlights() abort 10 | highlight default link GinaChangesAdded Statement 11 | highlight default link GinaChangesRemoved Constant 12 | highlight default link GinaChangesPath Comment 13 | endfunction 14 | 15 | augroup gina_syntax_changes_internal 16 | autocmd! * 17 | autocmd ColorScheme * call s:define_highlights() 18 | augroup END 19 | 20 | call s:define_highlights() 21 | 22 | let b:current_syntax = 'gina-changes' 23 | -------------------------------------------------------------------------------- /syntax/gina-commit.vim: -------------------------------------------------------------------------------- 1 | if exists('b:current_syntax') 2 | finish 3 | endif 4 | 5 | " Use Vim's builtin syntax for gitcommit 6 | runtime! syntax/gitcommit.vim 7 | 8 | let b:current_syntax = 'gina-commit' 9 | -------------------------------------------------------------------------------- /syntax/gina-grep.vim: -------------------------------------------------------------------------------- 1 | if exists('b:current_syntax') 2 | finish 3 | endif 4 | 5 | let s:ANSI = vital#gina#import('Vim.Buffer.ANSI') 6 | call s:ANSI.define_syntax() 7 | 8 | let b:current_syntax = 'gina-grep' 9 | -------------------------------------------------------------------------------- /syntax/gina-log.vim: -------------------------------------------------------------------------------- 1 | if exists('b:current_syntax') 2 | finish 3 | endif 4 | 5 | let s:ANSI = vital#gina#import('Vim.Buffer.ANSI') 6 | call s:ANSI.define_syntax() 7 | 8 | let b:current_syntax = 'gina-log' 9 | -------------------------------------------------------------------------------- /syntax/gina-reflog.vim: -------------------------------------------------------------------------------- 1 | if exists('b:current_syntax') 2 | finish 3 | endif 4 | 5 | let s:ANSI = vital#gina#import('Vim.Buffer.ANSI') 6 | call s:ANSI.define_syntax() 7 | 8 | let b:current_syntax = 'gina-reflog' 9 | -------------------------------------------------------------------------------- /syntax/gina-stash-show.vim: -------------------------------------------------------------------------------- 1 | if exists('b:current_syntax') 2 | finish 3 | endif 4 | 5 | syntax match GinaStashShowAdded /^\d\+/ nextgroup=GinaStashShowRemoved skipwhite 6 | syntax match GinaStashShowRemoved /\d\+/ nextgroup=GinaStashShowPath skipwhite contained 7 | syntax match GinaStashShowPath /.\+/ contained 8 | 9 | function! s:define_highlights() abort 10 | highlight default link GinaStashShowAdded Statement 11 | highlight default link GinaStashShowRemoved Constant 12 | highlight default link GinaStashShowPath Comment 13 | endfunction 14 | 15 | augroup gina_syntax_stash_show_internal 16 | autocmd! * 17 | autocmd ColorScheme * call s:define_highlights() 18 | augroup END 19 | 20 | call s:define_highlights() 21 | 22 | let b:current_syntax = 'gina-stash-show' 23 | -------------------------------------------------------------------------------- /syntax/gina-status.vim: -------------------------------------------------------------------------------- 1 | if exists('b:current_syntax') 2 | finish 3 | endif 4 | 5 | let s:ANSI = vital#gina#import('Vim.Buffer.ANSI') 6 | call s:ANSI.define_syntax() 7 | 8 | let b:current_syntax = 'gina-status' 9 | -------------------------------------------------------------------------------- /syntax/gina-tag.vim: -------------------------------------------------------------------------------- 1 | if exists('b:current_syntax') 2 | finish 3 | endif 4 | 5 | let s:ANSI = vital#gina#import('Vim.Buffer.ANSI') 6 | call s:ANSI.define_syntax() 7 | 8 | let b:current_syntax = 'gina-tag' 9 | -------------------------------------------------------------------------------- /test/.themisrc: -------------------------------------------------------------------------------- 1 | if &encoding ==# 'latin1' 2 | set encoding=utf-8 3 | endif 4 | 5 | " Force English interface 6 | try 7 | language message C 8 | catch 9 | endtry 10 | set helplang=en 11 | 12 | let s:assert = themis#helper('assert') 13 | call themis#option('recursive', 1) 14 | call themis#option('reporter', 'dot') 15 | call themis#helper('command').with(s:assert) 16 | 17 | call themis#log(substitute(execute('version'), '^\%(\r\?\n\)*', '', '')) 18 | call themis#log('-----------------------------------------------------------') 19 | call themis#log('has("lua"): ' . has('lua')) 20 | call themis#log('has("python"): ' . has('python')) 21 | call themis#log('has("python3"): ' . has('python3')) 22 | call themis#log('$LANG: ' . $LANG) 23 | call themis#log('&encoding: ' . &encoding) 24 | call themis#log('&termencoding: ' . &termencoding) 25 | call themis#log('&fileencodings: ' . &fileencodings) 26 | call themis#log('&fileformats: ' . &fileformats) 27 | call themis#log('&shellslash: ' . (exists('&shellslash') ? &shellslash : 'DISABLED')) 28 | call themis#log('&hidden: ' . &hidden) 29 | call themis#log('&autoread: ' . &autoread) 30 | call themis#log('&autowrite: ' . &autowrite) 31 | call themis#log('&autowriteall: ' . &autowriteall) 32 | call themis#log('&runtimepath:') 33 | for s:runtimepath in split(&runtimepath, ',') 34 | call themis#log(' ' . s:runtimepath) 35 | endfor 36 | call themis#log('-----------------------------------------------------------') 37 | 38 | " Decrease timeout to speed-up tests 39 | let s:timeout = 5000 40 | let g:gina#process#timeout = s:timeout 41 | 42 | " Synchronous Gina command for test 43 | function! s:GinaSync(bang, range, args, mods) abort 44 | call call('gina#command#call', [a:bang, a:range, a:args, a:mods]) 45 | if gina#process#wait(s:timeout) == -1 46 | call themis#log(printf( 47 | \ 'WARNING: ":%s%sGina%s %s" did not finish within %d ms', 48 | \ empty(a:mods) ? '' : a:mods . ' ', 49 | \ join(a:range, ','), 50 | \ a:bang, 51 | \ a:args, 52 | \ s:timeout, 53 | \)) 54 | call themis#log('Remaining running process:') 55 | for [process, _] in gina#process#runnings() 56 | call themis#log(' ' . string(process)) 57 | endfor 58 | endif 59 | endfunction 60 | 61 | command! -nargs=* -range=% -bang 62 | \ -complete=customlist,gina#command#complete 63 | \ GinaSync 64 | \ call s:GinaSync(, [, ], , ) 65 | 66 | 67 | " Git ---------------------------------------------------------------------- 68 | let s:is_windows = has('win32') || has('win64') 69 | let s:scriptroot = expand(':p:h') 70 | let s:separator = s:is_windows ? '\' : '/' 71 | execute 'source' fnameescape(s:scriptroot . s:separator . 'slit.vim') 72 | 73 | call themis#log('git version: ' . g:git_version) 74 | call themis#log(' worktree: ' . g:git_support_worktree) 75 | call themis#log('git config:') 76 | for record in split(system('git config --list'), '\r\?\n') 77 | call themis#log(' ' . record) 78 | endfor 79 | call themis#log('***********************************************************') 80 | -------------------------------------------------------------------------------- /test/gina/_testdata/cp1250-encoding.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdalisue/vim-gina/ff6c2ddeca98f886b57fb42283c12e167d6ab575/test/gina/_testdata/cp1250-encoding.txt -------------------------------------------------------------------------------- /test/gina/_testdata/cp932-encoding.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdalisue/vim-gina/ff6c2ddeca98f886b57fb42283c12e167d6ab575/test/gina/_testdata/cp932-encoding.txt -------------------------------------------------------------------------------- /test/gina/_testdata/euc-jp-encoding.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdalisue/vim-gina/ff6c2ddeca98f886b57fb42283c12e167d6ab575/test/gina/_testdata/euc-jp-encoding.txt -------------------------------------------------------------------------------- /test/gina/_testdata/pluscmd.txt: -------------------------------------------------------------------------------- 1 | !!! WARNING: THIS FILE SHOULD NOT BE MODIFIED !!! 2 | !!! WARNING: THIS FILE SHOULD NOT BE MODIFIED !!! 3 | !!! WARNING: THIS FILE SHOULD NOT BE MODIFIED !!! 4 | 5 | This file exists for testing +cmd supports. 6 | The following is a description of what +cmd copied from Vim's doc 7 | 8 | > The [+cmd] argument can be used to position the cursor in the newly opened 9 | > file, or execute any other command: 10 | > + Start at the last line. 11 | > +{num} Start at line {num}. 12 | > +/{pat} Start at first line containing {pat}. 13 | > +{command} Execute {command} after opening the new file. 14 | > {command} is any Ex command. 15 | > To include a white space in the {pat} or {command}, precede it with a 16 | > backslash. Double the number of backslashes. > 17 | > :edit +/The\ book file 18 | > :edit +/dir\ dirname\\ file 19 | > :edit +set\ dir=c:\\\\temp file 20 | > Note that in the last example the number of backslashes is halved twice: Once 21 | > for the "+cmd" argument and once for the ":set" command. 22 | 23 | While +cmd support +/{pat}, the test rely on the content. So this file should 24 | not be modified. 25 | -------------------------------------------------------------------------------- /test/gina/_testdata/sjis-encoding.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdalisue/vim-gina/ff6c2ddeca98f886b57fb42283c12e167d6ab575/test/gina/_testdata/sjis-encoding.txt -------------------------------------------------------------------------------- /test/gina/_testdata/utf-8-encoding.txt: -------------------------------------------------------------------------------- 1 | Unix EOL 2 | árvíztűrő tükörfúrógép 3 | ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP 4 | -------------------------------------------------------------------------------- /test/gina/action/index.vimspec: -------------------------------------------------------------------------------- 1 | Describe gina#action#index 2 | Before all 3 | let Path = vital#gina#import('System.Filepath') 4 | let slit = Slit(tempname(), 1) 5 | call slit.write('A/foo.txt', ['foo']) 6 | call slit.write('B/foo.txt', ['foo']) 7 | call slit.write('C/foo.txt', ['foo']) 8 | call slit.write('D/foo.txt', ['foo']) 9 | call slit.write('E/foo.txt', ['foo']) 10 | call slit.write('F/foo.txt', ['foo']) 11 | if !has('win32') && !has('win64') 12 | call slit.write('G/_', ['foo']) 13 | endif 14 | 15 | call slit.execute('add %s', slit.path('A/foo.txt')) 16 | call slit.execute('commit --quiet -m "First"') 17 | call slit.execute('add %s', slit.path('B/foo.txt')) 18 | call slit.execute('commit --quiet -m "Second"') 19 | call slit.execute('add %s', slit.path('C/foo.txt')) 20 | call slit.execute('commit --quiet -m "Thrid"') 21 | " Edit C/foo.txt 22 | call slit.write('C/foo.txt', ['foobar']) 23 | End 24 | 25 | After all 26 | %bwipeout! 27 | End 28 | 29 | Before 30 | %bwipeout! 31 | execute 'edit' fnameescape(slit.worktree) 32 | call gina#action#attach([]) 33 | call gina#action#include('index') 34 | End 35 | 36 | Describe 'discard' action 37 | It checkouts and overwrite the modification on tracked files 38 | Assert Equals(readfile(slit.path('C/foo.txt')), ['foobar']) 39 | call gina#action#call('index:discard:force', [ 40 | \ { 'path': slit.path('C/foo.txt'), 'sign': ' M' }, 41 | \]) 42 | call gina#process#wait() 43 | Assert Equals(readfile(slit.path('C/foo.txt')), ['foo']) 44 | End 45 | 46 | It deletes untracked files 47 | Assert True(filereadable(slit.path('D/foo.txt'))) 48 | call gina#action#call('index:discard:force', [ 49 | \ { 'path': slit.path('D/foo.txt'), 'sign': '!!' }, 50 | \]) 51 | Assert False(filereadable(slit.path('D/foo.txt'))) 52 | End 53 | 54 | It cannot delete directories for safety 55 | Assert True(isdirectory(slit.path('E'))) 56 | call gina#action#call('index:discard:force', [ 57 | \ { 'path': slit.path('E'), 'sign': '!!' }, 58 | \]) 59 | Assert True(isdirectory(slit.path('E'))) 60 | End 61 | 62 | It can delete directories if g:gina#action#index#discard_directories = 1 63 | let g:gina#action#index#discard_directories = 1 64 | Assert True(isdirectory(slit.path('F'))) 65 | call gina#action#call('index:discard:force', [ 66 | \ { 'path': slit.path('F'), 'sign': '!!' }, 67 | \]) 68 | Assert False(isdirectory(slit.path('F'))) 69 | unlet g:gina#action#index#discard_directories 70 | End 71 | 72 | It can handle _ correctly (Issue #133) 73 | if has('win32') || has('win64') 74 | Skip Windows filesystem does not allow < or > in path 75 | endif 76 | Assert True(filereadable(slit.path('G/_'))) 77 | call gina#action#call('index:discard:force', [ 78 | \ { 'path': slit.path('G/_'), 'sign': '!!' }, 79 | \]) 80 | Assert False(filereadable(slit.path('G/_'))) 81 | Assert True(filereadable(slit.path('A/foo.txt'))) 82 | Assert True(filereadable(slit.path('B/foo.txt'))) 83 | Assert True(filereadable(slit.path('C/foo.txt'))) 84 | End 85 | End 86 | End 87 | 88 | -------------------------------------------------------------------------------- /test/gina/command/cd.vimspec: -------------------------------------------------------------------------------- 1 | Describe gina#command#cd 2 | Before all 3 | let Path = vital#gina#import('System.Filepath') 4 | let slit1 = Slit(tempname(), 1) 5 | call slit1.write('A/foo/bar.txt', []) 6 | let saved_cwd = getcwd() 7 | End 8 | 9 | After all 10 | %bwipeout! 11 | execute 'cd' fnameescape(saved_cwd) 12 | End 13 | 14 | Before 15 | %bwipeout! 16 | execute 'cd' fnameescape(saved_cwd) 17 | edit foo 18 | execute 'new' fnameescape(Path.join(slit1.worktree, 'A', 'foo.txt')) 19 | End 20 | 21 | Describe Use cases 22 | It might be called without arguments 23 | GinaSync cd 24 | Assert Equals(getcwd(), slit1.worktree) 25 | silent wincmd p 26 | Assert Equals(getcwd(), slit1.worktree) 27 | End 28 | 29 | It might be called with 'A/foo' 30 | GinaSync cd A/foo 31 | Assert Equals( 32 | \ getcwd(), 33 | \ Path.join(slit1.worktree, 'A', 'foo') 34 | \) 35 | silent wincmd p 36 | Assert Equals( 37 | \ getcwd(), 38 | \ Path.join(slit1.worktree, 'A', 'foo') 39 | \) 40 | End 41 | End 42 | End 43 | -------------------------------------------------------------------------------- /test/gina/command/chaperon.vimspec: -------------------------------------------------------------------------------- 1 | Describe gina#command#chaperon 2 | Before all 3 | let Path = vital#gina#import('System.Filepath') 4 | let slit1 = Slit(tempname(), 1) 5 | let filename1 = slit1.path('A/foo.txt') 6 | let filename2 = slit1.path('B/foo.txt') 7 | call slit1.write('A/foo.txt', []) 8 | call slit1.write('B/foo.txt', []) 9 | call slit1.execute('add %s', slit1.path('A/foo.txt')) 10 | call slit1.execute('add %s', slit1.path('B/foo.txt')) 11 | 12 | call slit1.write('A/foo.txt', ['1']) 13 | call slit1.execute('commit --quiet -am "First"') 14 | call slit1.execute('checkout -b develop') 15 | 16 | call slit1.execute('checkout master') 17 | call slit1.write('A/foo.txt', ['2']) 18 | call slit1.execute('commit --quiet -am "Second"') 19 | 20 | call slit1.execute('checkout develop') 21 | call slit1.write('A/foo.txt', ['3']) 22 | call slit1.execute('commit --quiet -am "Third"') 23 | 24 | call slit1.execute('checkout master') 25 | call slit1.execute('merge develop') 26 | End 27 | 28 | After all 29 | %bwipeout! 30 | End 31 | 32 | Before 33 | %bwipeout! 34 | End 35 | 36 | Describe Use cases 37 | It might be called without arguments 38 | execute 'edit' fnameescape(filename1) 39 | GinaSync chaperon 40 | Assert Equals(tabpagenr('$'), 2) 41 | Assert Equals(winnr('$'), 3) 42 | " Focused buffer 43 | Assert Equals(bufname('%'), filename1) 44 | Assert Equals(getline(1, '$'), [ 45 | \ '', 46 | \]) 47 | " topleft 48 | let bufnr = winbufnr(1) 49 | Assert Equals(bufname(bufnr), printf('gina://%s:show/:2:A/foo.txt', slit1.refname)) 50 | Assert Equals(getbufline(bufnr, 1, '$'), [ 51 | \ '2', 52 | \]) 53 | " center 54 | let bufnr = winbufnr(2) 55 | Assert Equals(bufname(bufnr), filename1) 56 | Assert Equals(getbufline(bufnr, 1, '$'), [ 57 | \ '', 58 | \]) 59 | " botright 60 | let bufnr = winbufnr(3) 61 | Assert Equals(bufname(bufnr), printf('gina://%s:show/:3:A/foo.txt', slit1.refname)) 62 | Assert Equals(getbufline(bufnr, 1, '$'), [ 63 | \ '3', 64 | \]) 65 | 66 | " dor obtain changes from right 67 | normal dor 68 | Assert Equals(getline(1, '$'), [ 69 | \ '3', 70 | \]) 71 | 72 | " dol obtain changes from left 73 | normal dol 74 | Assert Equals(getline(1, '$'), [ 75 | \ '2', 76 | \]) 77 | End 78 | End 79 | End 80 | -------------------------------------------------------------------------------- /test/gina/command/commit.vimspec: -------------------------------------------------------------------------------- 1 | Describe gina#command#commit 2 | Before all 3 | let Path = vital#gina#import('System.Filepath') 4 | let slit1 = Slit(tempname(), 1) 5 | call slit1.write('A/foo.txt', []) 6 | call slit1.write('B/foo.txt', []) 7 | call slit1.execute('add .') 8 | call slit1.execute('commit -m "First"') 9 | call slit1.write('C/foo.txt', []) 10 | call slit1.execute('add .') 11 | End 12 | 13 | After all 14 | %bwipeout! 15 | End 16 | 17 | Describe Use cases 18 | Before 19 | %bwipeout! 20 | execute 'edit' fnameescape(slit1.worktree) 21 | End 22 | 23 | It might be called without arguments 24 | GinaSync commit 25 | Assert Equals(winnr('$'), 1) 26 | Assert Equals(bufname('%'), printf('gina://%s:commit', slit1.refname)) 27 | Assert Equals(getline(1, 3), [ 28 | \ '', 29 | \ '# Please enter the commit message for your changes. Lines starting', 30 | \ '# with ''#'' will be ignored, and an empty message aborts the commit.', 31 | \]) 32 | " Commit commitmsg 33 | call setline(1, ['Test Message']) | wq 34 | Assert Equals(slit1.execute('log --pretty=format:%s'), [ 35 | \ 'Test Message', 36 | \ 'First', 37 | \]) 38 | " Reset 39 | call slit1.execute('reset --soft HEAD@{1}') 40 | End 41 | 42 | It might be called with --verbose 43 | GinaSync commit --verbose 44 | Assert Equals(winnr('$'), 1) 45 | Assert Equals(bufname('%'), printf('gina://%s:commit', slit1.refname)) 46 | Assert Equals(getline(1, 3), [ 47 | \ '', 48 | \ '# Please enter the commit message for your changes. Lines starting', 49 | \ '# with ''#'' will be ignored, and an empty message aborts the commit.', 50 | \]) 51 | Assert True(index(getline(1, '$'), '# ------------------------ >8 ------------------------') >= 0) 52 | " Commit commitmsg 53 | call setline(1, ['Test Message']) | wq 54 | Assert Equals(slit1.execute('log --pretty=format:%s'), [ 55 | \ 'Test Message', 56 | \ 'First', 57 | \]) 58 | " Reset 59 | call slit1.execute('reset --soft HEAD@{1}') 60 | End 61 | 62 | It might be called with --amend 63 | GinaSync commit --amend 64 | Assert Equals(winnr('$'), 1) 65 | Assert Equals(bufname('%'), printf('gina://%s:commit', slit1.refname)) 66 | Assert Equals(getline(1, 4), [ 67 | \ 'First', 68 | \ '', 69 | \ '# Please enter the commit message for your changes. Lines starting', 70 | \ '# with ''#'' will be ignored, and an empty message aborts the commit.', 71 | \]) 72 | " Commit commitmsg 73 | call setline(1, ['Test Message']) | wq 74 | Assert Equals(slit1.execute('log --pretty=format:%s'), [ 75 | \ 'Test Message', 76 | \]) 77 | " Reset 78 | call slit1.execute('reset --soft HEAD@{1}') 79 | End 80 | 81 | It might be called with --message={message} 82 | GinaSync commit --message="Test Message" 83 | Assert Equals(winnr('$'), 1) 84 | Assert NotEquals(bufname('%'), printf('gina://%s:commit', slit1.refname)) 85 | Assert Equals(slit1.execute('log --pretty=format:%s'), [ 86 | \ 'Test Message', 87 | \ 'First', 88 | \]) 89 | " Reset 90 | call slit1.execute('reset --soft HEAD@{1}') 91 | End 92 | End 93 | End 94 | -------------------------------------------------------------------------------- /test/gina/command/edit.vimspec: -------------------------------------------------------------------------------- 1 | Describe gina#command#edit 2 | Before all 3 | let Path = vital#gina#import('System.Filepath') 4 | let slit1 = Slit(tempname(), 1) 5 | call slit1.write('A/foo.txt', []) 6 | call slit1.execute('add %s', slit1.path('A/foo.txt')) 7 | 8 | call slit1.write('A/foo.txt', ['1']) 9 | call slit1.execute('commit --quiet -am "First"') 10 | 11 | call slit1.write('A/foo.txt', ['2']) 12 | call slit1.execute('commit --quiet -am "Second"') 13 | 14 | call slit1.write('A/foo.txt', ['3']) 15 | call slit1.execute('add %s', slit1.path('A/foo.txt')) 16 | 17 | call slit1.write('A/foo.txt', ['4']) 18 | End 19 | 20 | After all 21 | %bwipeout! 22 | End 23 | 24 | Before 25 | %bwipeout! 26 | End 27 | 28 | Describe Use cases 29 | It might be called without arguments 30 | execute 'edit' fnameescape(slit1.path('A/foo.txt')) 31 | GinaSync show : 32 | GinaSync edit 33 | Assert Equals(winnr('$'), 1) 34 | " Focused buffer 35 | Assert Equals(bufname('%'), slit1.path('A/foo.txt')) 36 | Assert Equals(getline(1, '$'), [ 37 | \ '4', 38 | \]) 39 | End 40 | End 41 | End 42 | -------------------------------------------------------------------------------- /test/gina/command/lcd.vimspec: -------------------------------------------------------------------------------- 1 | Describe gina#command#lcd 2 | Before all 3 | let Path = vital#gina#import('System.Filepath') 4 | let slit1 = Slit(tempname(), 1) 5 | call slit1.write('A/foo/bar.txt', []) 6 | let saved_cwd = getcwd() 7 | End 8 | 9 | After all 10 | %bwipeout! 11 | execute 'cd' fnameescape(saved_cwd) 12 | End 13 | 14 | Before 15 | %bwipeout! 16 | execute 'cd' fnameescape(saved_cwd) 17 | edit foo 18 | execute 'new' fnameescape(Path.join(slit1.worktree, 'A', 'foo.txt')) 19 | End 20 | 21 | Describe Use cases 22 | It might be called without arguments 23 | GinaSync lcd 24 | Assert Equals(getcwd(), slit1.worktree) 25 | silent wincmd p 26 | Assert Equals(getcwd(), saved_cwd) 27 | End 28 | 29 | It might be called with 'A/foo' 30 | GinaSync lcd A/foo 31 | Assert Equals( 32 | \ getcwd(), 33 | \ Path.join(slit1.worktree, 'A', 'foo') 34 | \) 35 | silent wincmd p 36 | Assert Equals(getcwd(), saved_cwd) 37 | End 38 | End 39 | End 40 | -------------------------------------------------------------------------------- /test/gina/command/log.vimspec: -------------------------------------------------------------------------------- 1 | Describe gina#command#log 2 | Before all 3 | let Path = vital#gina#import('System.Filepath') 4 | let slit1 = Slit(tempname(), 1) 5 | call slit1.write('A/foo.txt', []) 6 | call slit1.write('B/foo.txt', []) 7 | call slit1.write('C/foo.txt', []) 8 | 9 | call slit1.execute('add %s', slit1.path('A/foo.txt')) 10 | call slit1.execute('commit --quiet -m "First"') 11 | call slit1.execute('checkout -b develop') 12 | call slit1.execute('add %s', slit1.path('B/foo.txt')) 13 | call slit1.execute('commit --quiet -m "Second"') 14 | call slit1.execute('checkout master') 15 | call slit1.execute('add %s', slit1.path('C/foo.txt')) 16 | call slit1.execute('commit --quiet -m "Thrid"') 17 | End 18 | 19 | After all 20 | %bwipeout! 21 | End 22 | 23 | Before 24 | %bwipeout! 25 | execute 'edit' fnameescape(Path.join(slit1.worktree, 'A', 'foo.txt')) 26 | End 27 | 28 | Describe Use cases (without {path}) 29 | It might be called without arguments 30 | GinaSync log --graph 31 | Assert Equals(winnr('$'), 1) 32 | Assert Equals(bufname('%'), printf('gina://%s:log', slit1.refname)) 33 | Assert Match( 34 | \ getline(1), 35 | \ '\* \w\{7} Thrid \d\+ seconds\? ago <.*> (.*)' 36 | \) 37 | Assert Match( 38 | \ getline(2), 39 | \ '\* \w\{7} First \d\+ seconds\? ago <.*>' 40 | \) 41 | Assert Equals(line('$'), 2) 42 | End 43 | 44 | It might be called with -- % 45 | GinaSync log --graph -- % 46 | Assert Equals(winnr('$'), 1) 47 | Assert Equals(bufname('%'), printf('gina://%s:log:--', slit1.refname)) 48 | Assert Match( 49 | \ getline(1), 50 | \ '\* \w\{7} First \d\+ seconds\? ago <.*>' 51 | \) 52 | Assert Equals(line('$'), 1) 53 | End 54 | 55 | It might be called with -- :/ 56 | GinaSync log --graph -- :/ 57 | Assert Equals(winnr('$'), 1) 58 | Assert Equals(bufname('%'), printf('gina://%s:log:--', slit1.refname)) 59 | Assert Match( 60 | \ getline(1), 61 | \ '\* \w\{7} Thrid \d\+ seconds\? ago <.*> (.*)' 62 | \) 63 | Assert Match( 64 | \ getline(2), 65 | \ '\* \w\{7} First \d\+ seconds\? ago <.*>' 66 | \) 67 | Assert Equals(line('$'), 2) 68 | End 69 | End 70 | 71 | Describe Use cases (with {path}) 72 | It might be called with :% 73 | GinaSync log --graph :% 74 | Assert Equals(winnr('$'), 1) 75 | Assert Equals(bufname('%'), printf('gina://%s:log/:A/foo.txt:$', slit1.refname)) 76 | Assert Match( 77 | \ getline(1), 78 | \ '\* \w\{7} First \d\+ seconds\? ago <.*>' 79 | \) 80 | Assert Equals(line('$'), 1) 81 | End 82 | End 83 | End 84 | -------------------------------------------------------------------------------- /test/gina/command/ls.vimspec: -------------------------------------------------------------------------------- 1 | Describe gina#command#ls 2 | Before all 3 | let Path = vital#gina#import('System.Filepath') 4 | let slit1 = Slit(tempname(), 1) 5 | call slit1.write('A/foo.txt', []) 6 | call slit1.write('B/foo.txt', []) 7 | call slit1.write('C/foo.txt', []) 8 | call slit1.write('D/foo.txt', []) 9 | 10 | call slit1.execute('add %s', slit1.path('A/foo.txt')) 11 | call slit1.execute('commit --quiet -m "First"') 12 | call slit1.execute('checkout -b develop') 13 | call slit1.execute('add %s', slit1.path('B/foo.txt')) 14 | call slit1.execute('commit --quiet -m "Second"') 15 | call slit1.execute('checkout master') 16 | call slit1.execute('add %s', slit1.path('C/foo.txt')) 17 | call slit1.execute('commit --quiet -m "Thrid"') 18 | End 19 | 20 | After all 21 | %bwipeout! 22 | End 23 | 24 | Before 25 | %bwipeout! 26 | execute 'edit' fnameescape(Path.join(slit1.worktree, 'A', 'foo.txt')) 27 | End 28 | 29 | Describe Use cases 30 | It might be called without arguments 31 | GinaSync ls 32 | Assert Equals(winnr('$'), 1) 33 | Assert Equals(bufname('%'), printf('gina://%s:ls', slit1.refname)) 34 | Assert Equals(getline(1, '$'), [ 35 | \ 'A/foo.txt', 36 | \ 'C/foo.txt', 37 | \]) 38 | End 39 | 40 | It might be called with --others 41 | GinaSync ls --others 42 | Assert Equals(winnr('$'), 1) 43 | Assert Equals(bufname('%'), printf('gina://%s:ls', slit1.refname)) 44 | Assert Equals(getline(1, '$'), [ 45 | \ 'D/foo.txt', 46 | \]) 47 | End 48 | 49 | It might be called with HEAD 50 | GinaSync ls HEAD 51 | Assert Equals(winnr('$'), 1) 52 | Assert Equals(bufname('%'), printf('gina://%s:ls/HEAD', slit1.refname)) 53 | Assert Equals(getline(1, '$'), [ 54 | \ 'A/foo.txt', 55 | \ 'C/foo.txt', 56 | \]) 57 | End 58 | 59 | It might be called with HEAD~ 60 | GinaSync ls HEAD~ 61 | Assert Equals(winnr('$'), 1) 62 | Assert Equals(bufname('%'), printf('gina://%s:ls/HEAD~', slit1.refname)) 63 | Assert Equals(getline(1, '$'), [ 64 | \ 'A/foo.txt', 65 | \]) 66 | End 67 | 68 | It might be called with develop 69 | GinaSync ls develop 70 | Assert Equals(winnr('$'), 1) 71 | Assert Equals(bufname('%'), printf('gina://%s:ls/develop', slit1.refname)) 72 | Assert Equals(getline(1, '$'), [ 73 | \ 'A/foo.txt', 74 | \ 'B/foo.txt', 75 | \]) 76 | End 77 | 78 | It might be called with -- A/*.txt 79 | GinaSync ls -- A/*.txt 80 | Assert Equals(winnr('$'), 1) 81 | Assert Equals(bufname('%'), printf('gina://%s:ls:--', slit1.refname)) 82 | Assert Equals(getline(1, '$'), [ 83 | \ 'A/foo.txt', 84 | \]) 85 | End 86 | End 87 | End 88 | -------------------------------------------------------------------------------- /test/gina/command/patch.vimspec: -------------------------------------------------------------------------------- 1 | Describe gina#command#patch 2 | Before all 3 | let Path = vital#gina#import('System.Filepath') 4 | let slit1 = Slit(tempname(), 1) 5 | let filename1 = slit1.path('A/foo.txt') 6 | call slit1.write('A/foo.txt', []) 7 | call slit1.execute('add %s', slit1.path('A/foo.txt')) 8 | 9 | call slit1.write('A/foo.txt', ['1']) 10 | call slit1.execute('commit --quiet -am "First"') 11 | call slit1.write('A/foo.txt', ['2']) 12 | call slit1.execute('add %s', slit1.path('A/foo.txt')) 13 | call slit1.write('A/foo.txt', ['3']) 14 | End 15 | 16 | After all 17 | %bwipeout! 18 | End 19 | 20 | Before 21 | %bwipeout! 22 | End 23 | 24 | Describe Use cases 25 | It might be called without arguments 26 | execute 'edit' fnameescape(filename1) 27 | GinaSync patch 28 | Assert Equals(tabpagenr('$'), 2) 29 | Assert Equals(winnr('$'), 3) 30 | " Focused buffer 31 | Assert Equals(bufname('%'), printf('gina://%s:show/:A/foo.txt', slit1.refname)) 32 | Assert Equals(getline(1, '$'), [ 33 | \ '2', 34 | \]) 35 | " topleft 36 | let bufnr = winbufnr(1) 37 | Assert Equals(bufname(bufnr), printf('gina://%s:show/HEAD:A/foo.txt', slit1.refname)) 38 | Assert Equals(getbufline(bufnr, 1, '$'), [ 39 | \ '1', 40 | \]) 41 | " center 42 | let bufnr = winbufnr(2) 43 | Assert Equals(bufname('%'), printf('gina://%s:show/:A/foo.txt', slit1.refname)) 44 | Assert Equals(getbufline(bufnr, 1, '$'), [ 45 | \ '2', 46 | \]) 47 | " botright 48 | let bufnr = winbufnr(3) 49 | Assert Equals(bufname(bufnr), filename1) 50 | Assert Equals(getbufline(bufnr, 1, '$'), [ 51 | \ '3', 52 | \]) 53 | 54 | " Patch should fail 55 | Assert Equals(split(execute('write'), '\r\?\n'), [ 56 | \ '[gina] No difference between index and buffer', 57 | \]) 58 | Assert Equals(slit1.execute('status --porcelain'), [ 59 | \ 'MM A/foo.txt', 60 | \]) 61 | 62 | " dor obtain changes from right 63 | normal dor 64 | Assert Equals(getline(1, '$'), [ 65 | \ '3', 66 | \]) 67 | " Patch should be able to perform and status become clear. 68 | Assert Equals(split(execute('write'), '\r\?\n'), []) 69 | Assert Equals(slit1.execute('status --porcelain'), [ 70 | \ 'M A/foo.txt', 71 | \]) 72 | 73 | " dol obtain changes from left 74 | normal dol 75 | Assert Equals(getline(1, '$'), [ 76 | \ '1', 77 | \]) 78 | " Patch should be able to perform and status become clear. 79 | Assert Equals(split(execute('write'), '\r\?\n'), []) 80 | Assert Equals(slit1.execute('status --porcelain'), [ 81 | \ ' M A/foo.txt', 82 | \]) 83 | End 84 | End 85 | End 86 | -------------------------------------------------------------------------------- /test/gina/command/reflog.vimspec: -------------------------------------------------------------------------------- 1 | Describe gina#command#reflog 2 | Before all 3 | let Path = vital#gina#import('System.Filepath') 4 | let slit1 = Slit(tempname(), 1) 5 | call slit1.write('A/foo.txt', []) 6 | call slit1.write('B/foo.txt', []) 7 | call slit1.write('C/foo.txt', []) 8 | 9 | call slit1.execute('add %s', slit1.path('A/foo.txt')) 10 | call slit1.execute('commit --quiet -m "First"') 11 | call slit1.execute('checkout -b develop') 12 | call slit1.execute('add %s', slit1.path('B/foo.txt')) 13 | call slit1.execute('commit --quiet -m "Second"') 14 | call slit1.execute('checkout master') 15 | call slit1.execute('add %s', slit1.path('C/foo.txt')) 16 | call slit1.execute('commit --quiet -m "Thrid"') 17 | End 18 | 19 | After all 20 | %bwipeout! 21 | End 22 | 23 | Before 24 | %bwipeout! 25 | execute 'edit' fnameescape(Path.join(slit1.worktree, 'A', 'foo.txt')) 26 | End 27 | 28 | Describe Use cases 29 | It might be called without arguments 30 | GinaSync reflog 31 | Assert Equals(winnr('$'), 1) 32 | Assert Equals(bufname('%'), printf('gina://%s:reflog', slit1.refname)) 33 | Assert Match(getline(1), 'HEAD@{0}: commit: Thrid') 34 | Assert Match(getline(2), 'HEAD@{1}: checkout: moving from develop to master') 35 | Assert Match(getline(3), 'HEAD@{2}: commit: Second') 36 | Assert Match(getline(4), 'HEAD@{3}: checkout: moving from master to develop') 37 | Assert Match(getline(5), 'HEAD@{4}: commit (initial): First') 38 | End 39 | End 40 | End 41 | -------------------------------------------------------------------------------- /test/gina/command/stash/list.vimspec: -------------------------------------------------------------------------------- 1 | Describe gina#command#stash#list 2 | Before all 3 | let Path = vital#gina#import('System.Filepath') 4 | let slit1 = Slit(tempname(), 1) 5 | call slit1.write('A/foo.txt', []) 6 | call slit1.write('B/foo.txt', []) 7 | call slit1.write('C/foo.txt', []) 8 | 9 | call slit1.execute('add %s', slit1.path('A/foo.txt')) 10 | call slit1.execute('commit -m First') 11 | call slit1.execute('add %s', slit1.path('B/foo.txt')) 12 | call slit1.execute('stash') 13 | call slit1.execute('add %s', slit1.path('C/foo.txt')) 14 | call slit1.execute('stash') 15 | End 16 | 17 | After all 18 | %bwipeout! 19 | End 20 | 21 | Before 22 | %bwipeout! 23 | execute 'edit' fnameescape(Path.join(slit1.worktree, 'A', 'foo.txt')) 24 | End 25 | 26 | Describe Use cases 27 | It might be called without arguments 28 | GinaSync stash list 29 | Assert Equals(winnr('$'), 1) 30 | Assert Equals(bufname('%'), printf('gina://%s:stash', slit1.refname)) 31 | Assert Match(getline(1), 'stash@{0}: WIP on master: [0-9a-f]\{7} First') 32 | Assert Match(getline(2), 'stash@{1}: WIP on master: [0-9a-f]\{7} First') 33 | Assert Equals(line('$'), 2) 34 | End 35 | End 36 | End 37 | -------------------------------------------------------------------------------- /test/gina/command/stash/show.vimspec: -------------------------------------------------------------------------------- 1 | Describe gina#command#stash#show 2 | Before all 3 | let Path = vital#gina#import('System.Filepath') 4 | let slit1 = Slit(tempname(), 1) 5 | call slit1.write('A/foo.txt', []) 6 | call slit1.write('B/foo.txt', []) 7 | call slit1.write('C/foo.txt', []) 8 | 9 | call slit1.execute('add %s', slit1.path('A/foo.txt')) 10 | call slit1.execute('commit -m First') 11 | call slit1.execute('add %s', slit1.path('B/foo.txt')) 12 | call slit1.execute('stash') 13 | call slit1.execute('add %s', slit1.path('C/foo.txt')) 14 | call slit1.execute('stash') 15 | End 16 | 17 | After all 18 | %bwipeout! 19 | End 20 | 21 | Before 22 | %bwipeout! 23 | execute 'edit' fnameescape(Path.join(slit1.worktree, 'A', 'foo.txt')) 24 | End 25 | 26 | Describe Use cases 27 | It might be called without arguments 28 | GinaSync stash show 29 | Assert Equals(winnr('$'), 1) 30 | Assert Equals(bufname('%'), printf('gina://%s:stash:show/stash@{0}', slit1.refname)) 31 | Assert Match(getline(1), '0 0 C/foo.txt') 32 | Assert Equals(line('$'), 1) 33 | End 34 | 35 | It might be called with stash rev 36 | GinaSync stash show stash@{1} 37 | Assert Equals(winnr('$'), 1) 38 | Assert Equals(bufname('%'), printf('gina://%s:stash:show/stash@{1}', slit1.refname)) 39 | Assert Match(getline(1), '0 0 B/foo.txt') 40 | Assert Equals(line('$'), 1) 41 | End 42 | End 43 | End 44 | -------------------------------------------------------------------------------- /test/gina/command/status.vimspec: -------------------------------------------------------------------------------- 1 | Describe gina#command#status 2 | Before all 3 | let Path = vital#gina#import('System.Filepath') 4 | let slit1 = Slit(tempname(), 1) 5 | call slit1.write('A/foo.txt', []) 6 | call slit1.execute('add %s', slit1.path('A/foo.txt')) 7 | 8 | call slit1.write('A/foo.txt', ['1']) 9 | call slit1.execute('commit --quiet -am "First"') 10 | 11 | call slit1.write('A/foo.txt', ['2']) 12 | call slit1.execute('commit --quiet -am "Second"') 13 | 14 | call slit1.write('A/foo.txt', ['3']) 15 | call slit1.execute('add %s', slit1.path('A/foo.txt')) 16 | 17 | call slit1.write('A/foo.txt', ['4']) 18 | End 19 | 20 | After all 21 | %bwipeout! 22 | End 23 | 24 | Before 25 | %bwipeout! 26 | End 27 | 28 | Describe Use cases 29 | It might be called without arguments 30 | execute 'edit' fnameescape(slit1.path('A/foo.txt')) 31 | GinaSync status 32 | Assert Equals(winnr('$'), 1) 33 | Assert Equals(bufname('%'), printf('gina://%s:status', slit1.refname)) 34 | Assert Match(join(getline(1, '$'), "\n"), 'modified: A/foo.txt') 35 | Assert Match(join(getline(1, '$'), "\n"), 'modified: A/foo.txt') 36 | End 37 | 38 | It might be called with -- A/*.txt 39 | execute 'edit' fnameescape(slit1.path('A/foo.txt')) 40 | GinaSync status -- A/*.txt 41 | Assert Equals(winnr('$'), 1) 42 | Assert Equals(bufname('%'), printf('gina://%s:status:--', slit1.refname)) 43 | Assert Match(join(getline(1, '$'), "\n"), 'modified: A/foo.txt') 44 | Assert Match(join(getline(1, '$'), "\n"), 'modified: A/foo.txt') 45 | End 46 | 47 | It might be called with -- A/*.vim 48 | execute 'edit' fnameescape(slit1.path('A/foo.txt')) 49 | GinaSync status -- A/*.vim 50 | Assert Equals(winnr('$'), 1) 51 | Assert Equals(bufname('%'), printf('gina://%s:status:--', slit1.refname)) 52 | Assert Equals(getline(1, '$'), [ 53 | \ 'On branch master', 54 | \ 'nothing to commit, working tree clean', 55 | \]) 56 | End 57 | End 58 | 59 | Describe Use cases (--short) 60 | It might be called without arguments 61 | execute 'edit' fnameescape(slit1.path('A/foo.txt')) 62 | GinaSync status --short 63 | Assert Equals(winnr('$'), 1) 64 | Assert Equals(bufname('%'), printf('gina://%s:status', slit1.refname)) 65 | Assert Equals(getline(1, '$'), [ 66 | \ 'MM A/foo.txt' 67 | \]) 68 | End 69 | 70 | It might be called with -- A/*.txt 71 | execute 'edit' fnameescape(slit1.path('A/foo.txt')) 72 | GinaSync status --short -- A/*.txt 73 | Assert Equals(winnr('$'), 1) 74 | Assert Equals(bufname('%'), printf('gina://%s:status:--', slit1.refname)) 75 | Assert Equals(getline(1, '$'), [ 76 | \ 'MM A/foo.txt' 77 | \]) 78 | End 79 | 80 | It might be called with -- A/*.vim 81 | execute 'edit' fnameescape(slit1.path('A/foo.txt')) 82 | GinaSync status --short -- A/*.vim 83 | Assert Equals(winnr('$'), 1) 84 | Assert Equals(bufname('%'), printf('gina://%s:status:--', slit1.refname)) 85 | Assert Equals(getline(1, '$'), ['']) 86 | End 87 | End 88 | End 89 | -------------------------------------------------------------------------------- /test/gina/command/tag.vimspec: -------------------------------------------------------------------------------- 1 | Describe gina#command#tag 2 | Before all 3 | let Path = vital#gina#import('System.Filepath') 4 | let slit1 = Slit(tempname(), 1) 5 | call slit1.write('A/foo.txt', []) 6 | call slit1.execute('add %s', slit1.path('A/foo.txt')) 7 | 8 | call slit1.write('A/foo.txt', ['1']) 9 | call slit1.execute('commit --quiet -am "First"') 10 | call slit1.execute('tag -a v0.0.1 -m "Version 0.0.1"') 11 | 12 | call slit1.write('A/foo.txt', ['2']) 13 | call slit1.execute('commit --quiet -am "Second"') 14 | call slit1.execute('tag -a v0.0.2 -m "Version 0.0.2"') 15 | End 16 | 17 | After all 18 | %bwipeout! 19 | End 20 | 21 | Before 22 | %bwipeout! 23 | End 24 | 25 | Describe Use cases 26 | It might be called without arguments 27 | execute 'edit' fnameescape(slit1.path('A/foo.txt')) 28 | GinaSync tag 29 | Assert Equals(winnr('$'), 1) 30 | " Focused buffer 31 | Assert Equals(bufname('%'), printf('gina://%s:tag', slit1.refname)) 32 | Assert Equals(getline(1, '$'), [ 33 | \ 'v0.0.1', 34 | \ 'v0.0.2', 35 | \]) 36 | End 37 | 38 | It might be called with -a 39 | execute 'edit' fnameescape(slit1.path('A/foo.txt')) 40 | GinaSync tag -a v0.0.3 41 | Assert Equals(winnr('$'), 1) 42 | " Focused buffer 43 | Assert Equals(bufname('%'), printf('gina://%s:tag:edit', slit1.refname)) 44 | Assert Equals(getline(1, '$'), [ 45 | \ '', 46 | \ '# ', 47 | \ '# Write a message for tag:', 48 | \ '# v0.0.3', 49 | \ '# Lines starting with ''#'' will be ignored.', 50 | \]) 51 | " Apply tag 52 | call setline(1, ['Test Message']) | wq 53 | let content = slit1.execute('show v0.0.3') 54 | Assert Equals(content[0], 'tag v0.0.3') 55 | Assert Equals(content[3], '') 56 | Assert Equals(content[4], 'Test Message') 57 | Assert Equals(content[5], '') 58 | Assert Equals(content[9], '') 59 | Assert Equals(content[10], ' Second') 60 | Assert Equals(content[11], '') 61 | " Reset 62 | call slit1.execute('reset --soft HEAD@{1}') 63 | End 64 | End 65 | End 66 | -------------------------------------------------------------------------------- /test/gina/core/args.vimspec: -------------------------------------------------------------------------------- 1 | Describe gina#core#args 2 | Describe #extend_treeish({git}, {args}, {treeish}) 3 | It extends {args.params} with {rev, path, treeish} dictionary 4 | let git = gina#core#get_or_fail() 5 | let args = gina#core#args#new('show HEAD:README.md') 6 | let args.params = {} 7 | call gina#core#args#extend_treeish(git, args, args.pop(1)) 8 | Assert Equals(args.params.rev, 'HEAD') 9 | Assert Equals(args.params.path, 'README.md') 10 | Assert Equals(args.params.treeish, 'HEAD:README.md') 11 | End 12 | End 13 | End 14 | -------------------------------------------------------------------------------- /test/gina/core/emitter.vimspec: -------------------------------------------------------------------------------- 1 | Describe gina#core#emitter 2 | Before all 3 | let Path = vital#gina#import('System.Filepath') 4 | let slit1 = Slit(tempname(), 1) 5 | let slit3 = Slit(tempname()) 6 | End 7 | 8 | After all 9 | %bwipeout! 10 | End 11 | 12 | Before 13 | let tracker = { 14 | \ 'modified_called_count': 0, 15 | \ 'modified_delay_called_count': 0, 16 | \} 17 | 18 | function! tracker.modified(...) abort 19 | let self.modified_called_count += 1 20 | endfunction 21 | 22 | function! tracker.modified_delay(...) abort 23 | let self.modified_delay_called_count += 1 24 | endfunction 25 | 26 | call gina#core#emitter#subscribe( 27 | \ 'modified', 28 | \ tracker.modified, tracker, 29 | \) 30 | call gina#core#emitter#subscribe( 31 | \ 'modified:delay', 32 | \ tracker.modified_delay, tracker, 33 | \) 34 | End 35 | 36 | After 37 | call gina#core#emitter#unsubscribe( 38 | \ 'modified', 39 | \ tracker.modified, tracker, 40 | \) 41 | call gina#core#emitter#unsubscribe( 42 | \ 'modified:delay', 43 | \ tracker.modified_delay, tracker, 44 | \) 45 | End 46 | 47 | It emits 'modified' event a bit later by 'modified:delay' event 48 | Assert Equals(tracker.modified_called_count, 0) 49 | Assert Equals(tracker.modified_delay_called_count, 0) 50 | call gina#core#emitter#emit('modified:delay') 51 | Assert Equals(tracker.modified_called_count, 0) 52 | Assert Equals(tracker.modified_delay_called_count, 1) 53 | execute 'sleep' (g:gina#core#emitter#modified_delay * 10) . 'm' 54 | Assert Equals(tracker.modified_called_count, 1) 55 | Assert Equals(tracker.modified_delay_called_count, 1) 56 | End 57 | 58 | It squashes 'modified:delay' events emitted within 'modified_delay' milliseconds 59 | Assert Equals(tracker.modified_called_count, 0) 60 | Assert Equals(tracker.modified_delay_called_count, 0) 61 | call gina#core#emitter#emit('modified:delay') 62 | call gina#core#emitter#emit('modified:delay') 63 | call gina#core#emitter#emit('modified:delay') 64 | call gina#core#emitter#emit('modified:delay') 65 | call gina#core#emitter#emit('modified:delay') 66 | Assert Equals(tracker.modified_called_count, 0) 67 | Assert Equals(tracker.modified_delay_called_count, 5) 68 | execute 'sleep' (g:gina#core#emitter#modified_delay * 10) . 'm' 69 | Assert Equals(tracker.modified_called_count, 1) 70 | Assert Equals(tracker.modified_delay_called_count, 5) 71 | End 72 | 73 | It DOES NOT emits 'modified:delay' event by modifing a buffer NOT in a git repository 74 | execute 'edit' fnameescape(Path.join(slit3.worktree, 'foo.txt')) 75 | Assert Equals(tracker.modified_called_count, 0) 76 | Assert Equals(tracker.modified_delay_called_count, 0) 77 | execute "normal! ifoobar\:w\" 78 | Assert Equals(tracker.modified_called_count, 0) 79 | Assert Equals(tracker.modified_delay_called_count, 0) 80 | execute 'sleep' (g:gina#core#emitter#modified_delay * 10) . 'm' 81 | Assert Equals(tracker.modified_called_count, 0) 82 | Assert Equals(tracker.modified_delay_called_count, 0) 83 | End 84 | End 85 | -------------------------------------------------------------------------------- /test/gina/core/meta.vimspec: -------------------------------------------------------------------------------- 1 | Describe gina#core#meta 2 | Before 3 | %bwipeout! 4 | End 5 | 6 | After all 7 | %bwipeout! 8 | End 9 | 10 | Describe #get() 11 | It returns a default value when {name} does not exist 12 | Assert Equals(gina#core#meta#get('foo'), '') 13 | Assert Equals(gina#core#meta#get('foo', 0), 0) 14 | End 15 | 16 | It returns a {value} of {name} when exists 17 | call gina#core#meta#set('foo', 'bar') 18 | Assert Equals(gina#core#meta#get('foo'), 'bar') 19 | End 20 | End 21 | 22 | Describe #set() 23 | It sets a {value} with {name} 24 | call gina#core#meta#set('foo', 'bar') 25 | Assert Equals(gina#core#meta#get('foo'), 'bar') 26 | End 27 | End 28 | 29 | Describe #has() 30 | It returns 0 when {name} does not exist 31 | Assert Equals(gina#core#meta#has('foo'), 0) 32 | End 33 | 34 | It returns 1 when {name} exists 35 | call gina#core#meta#set('foo', 'bar') 36 | Assert Equals(gina#core#meta#has('foo'), 1) 37 | End 38 | End 39 | 40 | Describe #remove() 41 | It does nothing when {name} does not exist 42 | call gina#core#meta#set('foo', 'foo') 43 | call gina#core#meta#remove('bar') 44 | Assert Equals(gina#core#meta#get('foo'), 'foo') 45 | Assert Equals(gina#core#meta#get('bar'), '') 46 | End 47 | 48 | It removes {name} when exists 49 | call gina#core#meta#set('foo', 'foo') 50 | call gina#core#meta#set('bar', 'bar') 51 | call gina#core#meta#remove('bar') 52 | Assert Equals(gina#core#meta#get('foo'), 'foo') 53 | Assert Equals(gina#core#meta#get('bar'), '') 54 | End 55 | End 56 | 57 | Describe #clear() 58 | It does nothing when no entry exist 59 | call gina#core#meta#clear() 60 | Assert Equals(gina#core#meta#get('foo'), '') 61 | Assert Equals(gina#core#meta#get('bar'), '') 62 | End 63 | 64 | It removes all entries 65 | call gina#core#meta#set('foo', 'foo') 66 | call gina#core#meta#set('bar', 'bar') 67 | call gina#core#meta#clear() 68 | Assert Equals(gina#core#meta#get('foo'), '') 69 | Assert Equals(gina#core#meta#get('bar'), '') 70 | End 71 | End 72 | 73 | Describe #get_or_fail() 74 | It returns a {value} of {name} when {name} exist 75 | call gina#core#meta#set('foo', 'bar') 76 | Assert Equals(gina#core#meta#get_or_fail('foo'), 'bar') 77 | End 78 | 79 | It throws an exception when {name} does not exist 80 | Throws /A required meta value/ gina#core#meta#get_or_fail('foo') 81 | End 82 | End 83 | End 84 | -------------------------------------------------------------------------------- /test/gina/core/path.vimspec: -------------------------------------------------------------------------------- 1 | Describe gina#core#path 2 | Before all 3 | let Path = vital#gina#import('System.Filepath') 4 | let slit1 = Slit(tempname(), 1) 5 | End 6 | 7 | After all 8 | %bwipeout! 9 | End 10 | 11 | Before 12 | %bwipeout! 13 | End 14 | 15 | Describe #expand({expr}) 16 | It returns a empty string when {expr} is an empty string 17 | Assert Equals(gina#core#path#expand(''), '') 18 | End 19 | 20 | It returns a Unix-like path of {expr} when {expr} starts from ':' 21 | let expr = Path.realpath(':/foo/bar.txt') 22 | Assert Equals(gina#core#path#expand(expr), ':/foo/bar.txt') 23 | End 24 | 25 | It returns an Unix-like absolute path of {expr} when {expr} starts from 'gina://' 26 | let git = gina#core#get_or_fail({'expr': slit1.worktree}) 27 | let expr = printf( 28 | \ 'gina://%s:show/:A/foo/bar.txt', 29 | \ slit1.refname, 30 | \) 31 | Assert Equals(gina#core#path#expand(expr), Path.unixpath( 32 | \ Path.join(git.worktree, 'A', 'foo', 'bar.txt') 33 | \)) 34 | End 35 | 36 | It returns an expanded Unix-like path of {expr} when {expr} is '%' 37 | execute 'edit' Path.realpath('foo/bar.txt') 38 | let expr = '%' 39 | Assert Equals(gina#core#path#expand(expr), 'foo/bar.txt') 40 | 41 | let git = gina#core#get_or_fail({'expr': slit1.worktree}) 42 | execute 'edit' fnameescape(printf( 43 | \ 'gina://%s:show/:A/foo/bar.txt', 44 | \ slit1.refname, 45 | \)) 46 | let expr = '%' 47 | Assert Equals(gina#core#path#expand(expr), Path.unixpath( 48 | \ Path.join(git.worktree, 'A', 'foo', 'bar.txt') 49 | \)) 50 | End 51 | 52 | It does not expand pathspec such as $HOME or *.vim 53 | let expr = 'autoload/gina/*.vim' 54 | Assert Equals(gina#core#path#expand(expr), expr) 55 | 56 | let expr = '$HOME' 57 | Assert Equals(gina#core#path#expand(expr), '$HOME') 58 | 59 | let expr = 'master' 60 | Assert Equals(gina#core#path#expand(expr), 'master') 61 | End 62 | End 63 | 64 | Describe #abspath({expr} [, {root}]) 65 | It returns a Unix-like path of {expr} when {expr} starts from ':' 66 | let expr = Path.realpath(':/foo/bar.txt') 67 | Assert Equals(gina#core#path#abspath(expr), ':/foo/bar.txt') 68 | End 69 | 70 | It does not expand _ 71 | let expect = Path.unixpath(getcwd() . '/_') 72 | Assert Equals(gina#core#path#abspath('_'), expect) 73 | End 74 | End 75 | 76 | Describe #relpath({expr} [, {root}]) 77 | It returns a Unix-like path of {expr} when {expr} starts from ':' 78 | let expr = Path.realpath(':/foo/bar.txt') 79 | Assert Equals(gina#core#path#relpath(expr), ':/foo/bar.txt') 80 | End 81 | 82 | It does not expand _ 83 | let expect = Path.unixpath('_') 84 | Assert Equals(gina#core#path#relpath('_'), expect) 85 | End 86 | End 87 | End 88 | -------------------------------------------------------------------------------- /test/gina/core/repo.vimspec: -------------------------------------------------------------------------------- 1 | Describe gina#core#repo 2 | Before all 3 | let Path = vital#gina#import('System.Filepath') 4 | let slit1 = Slit(tempname(), 1) 5 | call slit1.execute('config core.commentChar $') 6 | End 7 | 8 | After all 9 | %bwipeout! 10 | End 11 | 12 | Before 13 | %bwipeout! 14 | End 15 | 16 | Describe #config({git}) 17 | It returns a config dictionary of {git} repository 18 | execute 'edit' fnameescape(slit1.worktree) 19 | 20 | let git = gina#core#get() 21 | let config = gina#core#repo#config(git) 22 | Assert KeyExists(config, 'core.commentchar') 23 | Assert Equals(config['core.commentchar'], '$') 24 | End 25 | End 26 | End 27 | -------------------------------------------------------------------------------- /test/gina/custom/action.vimspec: -------------------------------------------------------------------------------- 1 | Describe gina#custom#action 2 | Before all 3 | let scheme = 'status' 4 | let pattern = '/\%(branch\|tag\)' 5 | End 6 | 7 | After all 8 | %bwipeout! 9 | call gina#custom#clear() 10 | End 11 | 12 | Before 13 | %bwipeout! 14 | call gina#custom#clear() 15 | End 16 | 17 | Describe #alias({scheme}, {alias}, {origin}) 18 | It define an alias of {origin} to {alias} in a {scheme} buffer 19 | call gina#custom#action#alias(scheme, 'doc', 'builtin:help') 20 | GinaSync status 21 | let result = execute('call gina#action#call(''doc'')') 22 | Assert Match(result, 'builtin:help \[doc\]') 23 | Assert Match(result, 'builtin:help:all \[help:all\]') 24 | GinaSync branch 25 | let result = execute('call gina#action#call(''doc'')') 26 | Assert Match(result, 'No corresponding action') 27 | GinaSync tag 28 | let result = execute('call gina#action#call(''doc'')') 29 | Assert Match(result, 'No corresponding action') 30 | End 31 | 32 | It define an alias of {origin} to {alias} in buffers which match with /{pattern} 33 | call gina#custom#action#alias(pattern, 'doc', 'builtin:help') 34 | GinaSync status 35 | let result = execute('call gina#action#call(''doc'')') 36 | Assert Match(result, 'No corresponding action') 37 | GinaSync branch 38 | let result = execute('call gina#action#call(''doc'')') 39 | Assert Match(result, 'builtin:help \[doc\]') 40 | Assert Match(result, 'builtin:help:all \[help:all\]') 41 | GinaSync tag 42 | let result = execute('call gina#action#call(''doc'')') 43 | Assert Match(result, 'builtin:help \[doc\]') 44 | Assert Match(result, 'builtin:help:all \[help:all\]') 45 | End 46 | End 47 | End 48 | -------------------------------------------------------------------------------- /test/gina/custom/command.vimspec: -------------------------------------------------------------------------------- 1 | Describe gina#custom#command 2 | Before all 3 | let scheme = 'status' 4 | let pattern = '/\%(branch\|tag\)' 5 | End 6 | 7 | After all 8 | %bwipeout! 9 | call gina#custom#clear() 10 | End 11 | 12 | Before 13 | %bwipeout! 14 | call gina#custom#clear() 15 | End 16 | 17 | Describe #option({scheme}, {query} [, {value}]) 18 | It define {query} option for a {scheme} command 19 | call gina#custom#command#option(scheme, '--ignore-submodule') 20 | let args = gina#core#args#new('status') 21 | Assert Equals(args.raw, [ 22 | \ 'status', 23 | \ '--ignore-submodule', 24 | \]) 25 | 26 | call gina#custom#command#option(scheme, '--untracked-files', 'all') 27 | let args = gina#core#args#new('status') 28 | Assert Equals(args.raw, [ 29 | \ 'status', 30 | \ '--ignore-submodule', 31 | \ '--untracked-files=all', 32 | \]) 33 | 34 | let args = gina#core#args#new('status --no-ignore-submodule') 35 | Assert Equals(args.raw, [ 36 | \ 'status', 37 | \ '--untracked-files=all', 38 | \]) 39 | 40 | let args = gina#core#args#new('status --untracked-files=none') 41 | Assert Equals(args.raw, [ 42 | \ 'status', 43 | \ '--untracked-files=none', 44 | \ '--ignore-submodule', 45 | \]) 46 | End 47 | 48 | It define {query} option for commands which match with {pattern} 49 | call gina#custom#command#option(pattern, '--opener=vsplit') 50 | let args = gina#core#args#new('status') 51 | Assert Equals(args.raw, [ 52 | \ 'status', 53 | \]) 54 | 55 | let args = gina#core#args#new('branch') 56 | Assert Equals(args.raw, [ 57 | \ 'branch', 58 | \ '--opener=vsplit', 59 | \]) 60 | 61 | let args = gina#core#args#new('tag') 62 | Assert Equals(args.raw, [ 63 | \ 'tag', 64 | \ '--opener=vsplit', 65 | \]) 66 | End 67 | End 68 | 69 | Describe #alias({scheme}, {alias} [, {raw}]) 70 | It define an alias of {scheme} command to {alias} 71 | let args = gina#core#args#new('st') 72 | Assert Equals(args.raw, ['st']) 73 | 74 | call gina#custom#command#alias('status', 'st') 75 | let args = gina#core#args#new('st') 76 | Assert Equals(args.raw, ['status']) 77 | 78 | call gina#custom#command#alias('status', 'st', 1) 79 | let args = gina#core#args#new('st') 80 | Assert Equals(args.raw, ['_raw', 'status']) 81 | End 82 | 83 | It 's namespace for options are distinct from an original command 84 | call gina#custom#command#option('status', '--ignore-submodule') 85 | call gina#custom#command#alias('status', 'st') 86 | 87 | let args = gina#core#args#new('status') 88 | Assert Equals(args.raw, ['status', '--ignore-submodule']) 89 | let args = gina#core#args#new('st') 90 | Assert Equals(args.raw, ['status']) 91 | End 92 | End 93 | End 94 | -------------------------------------------------------------------------------- /test/gina/custom/mapping.vimspec: -------------------------------------------------------------------------------- 1 | Describe gina#custom#mapping 2 | Before all 3 | let scheme = 'status' 4 | let pattern = '/\%(branch\|tag\)' 5 | End 6 | 7 | After all 8 | %bwipeout! 9 | call gina#custom#clear() 10 | End 11 | 12 | Before 13 | %bwipeout! 14 | call gina#custom#clear() 15 | End 16 | 17 | Describe #map({scheme}, {lhs}, {rhs} [, {options}]) 18 | It define {lhs} to {rhs} mapping in a {scheme} buffer 19 | call gina#custom#mapping#map(scheme, 'H', '(gina-action-builtin-help-all)') 20 | GinaSync status 21 | Assert Equals(maparg('H'), '(gina-action-builtin-help-all)') 22 | GinaSync branch 23 | Assert Equals(maparg('H'), '') 24 | GinaSync tag 25 | Assert Equals(maparg('H'), '') 26 | End 27 | 28 | It define {lhs} to {rhs} mapping in buffers which match with {pattern} 29 | call gina#custom#mapping#map(pattern, 'H', '(gina-action-builtin-help-all)') 30 | GinaSync status 31 | Assert Equals(maparg('H'), '') 32 | GinaSync branch 33 | Assert Equals(maparg('H'), '(gina-action-builtin-help-all)') 34 | GinaSync tag 35 | Assert Equals(maparg('H'), '(gina-action-builtin-help-all)') 36 | End 37 | End 38 | End 39 | -------------------------------------------------------------------------------- /test/run_themis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | themis --version 3 | 4 | export THEMIS_VIM="vim" 5 | themis --reporter dot 6 | 7 | export THEMIS_VIM="nvim" 8 | export THEMIS_ARGS="-e -s --headless" 9 | themis --reporter dot 10 | -------------------------------------------------------------------------------- /test/slit.vim: -------------------------------------------------------------------------------- 1 | "============================================================================= 2 | " 3 | " A small lib for git handling mainly for unittest 4 | " 5 | " Author: lambdalisue 6 | " License: MIT License 7 | " 8 | "============================================================================= 9 | let s:is_windows = has('win32') || has('win64') 10 | let s:separator = s:is_windows ? '\' : '/' 11 | 12 | function! s:shellescape(x) abort 13 | return a:x !~# '\s' ? a:x : shellescape(a:x) 14 | endfunction 15 | 16 | 17 | let s:guard = {} 18 | 19 | function! s:guard.restore() abort 20 | execute self.command 21 | endfunction 22 | 23 | 24 | let s:slit = {} 25 | 26 | function! s:slit.cd() abort 27 | let guard = copy(s:guard) 28 | let guard.command = printf('cd %s', fnameescape(getcwd())) 29 | execute 'cd' fnameescape(self.worktree) 30 | return guard 31 | endfunction 32 | 33 | function! s:slit.lcd() abort 34 | let guard = copy(s:guard) 35 | let guard.command = printf('lcd %s', fnameescape(getcwd())) 36 | execute 'lcd' fnameescape(self.worktree) 37 | return guard 38 | endfunction 39 | 40 | function! s:slit.path(path) abort 41 | let path = s:is_windows 42 | \ ? fnamemodify(a:path, ':gs?/?\\?') 43 | \ : fnamemodify(a:path, ':gs?\\?/?') 44 | return simplify(self.worktree . s:separator . path) 45 | endfunction 46 | 47 | function! s:slit.read(path) abort 48 | return readfile(self.path(a:path)) 49 | endfunction 50 | 51 | function! s:slit.write(path, content) abort 52 | let path = self.path(a:path) 53 | let dirpath = fnamemodify(path, ':p:h') 54 | if !isdirectory(dirpath) 55 | call mkdir(dirpath, 'p') 56 | endif 57 | return writefile(a:content, path) 58 | endfunction 59 | 60 | function! s:slit.execute(...) abort 61 | let expr = a:000[0] 62 | let terms = map(a:000[1:], 's:shellescape(v:val)') 63 | let command = empty(terms) ? expr : call('printf', [expr] + terms) 64 | let args = [ 65 | \ 'git', 66 | \ '-c color.ui=false', 67 | \ '-c core.editor=false', 68 | \ '--no-pager', 69 | \] 70 | if !empty(get(self, 'worktree')) 71 | let args += ['-C', shellescape(self.worktree)] 72 | endif 73 | let output = system(join(args + [command])) 74 | return split(output, '\r\?\n') 75 | endfunction 76 | 77 | function! s:slit.init() abort 78 | call self.execute('init') 79 | return self 80 | endfunction 81 | 82 | function! s:slit.reset() abort 83 | call self.execute('reset --hard HEAD') 84 | call self.execute('clean -df') 85 | return self 86 | endfunction 87 | 88 | function! Slit(worktree, ...) abort 89 | let slit = copy(s:slit) 90 | let slit.worktree = resolve(fnamemodify(a:worktree, ':p')) 91 | let slit.repository = slit.worktree . s:separator . '.git' 92 | let slit.refname = fnamemodify(slit.worktree, ':t') 93 | if !isdirectory(slit.worktree) 94 | call mkdir(slit.worktree, 'p') 95 | endif 96 | lockvar slit 97 | return get(a:000, 0, 0) ? slit.init() : slit 98 | endfunction 99 | 100 | let g:git_version = matchstr(system('git --version'), '\%(\d\+\.\)\+\d') 101 | let g:git_support_worktree = g:git_version !~# '^\%([01]\..*\|2\.4\..*\)$' 102 | -------------------------------------------------------------------------------- /test/vital/_testdata/Vim/Buffer/Writer/cp1250-encoding.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdalisue/vim-gina/ff6c2ddeca98f886b57fb42283c12e167d6ab575/test/vital/_testdata/Vim/Buffer/Writer/cp1250-encoding.txt -------------------------------------------------------------------------------- /test/vital/_testdata/Vim/Buffer/Writer/cp932-encoding.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdalisue/vim-gina/ff6c2ddeca98f886b57fb42283c12e167d6ab575/test/vital/_testdata/Vim/Buffer/Writer/cp932-encoding.txt -------------------------------------------------------------------------------- /test/vital/_testdata/Vim/Buffer/Writer/euc-jp-encoding.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdalisue/vim-gina/ff6c2ddeca98f886b57fb42283c12e167d6ab575/test/vital/_testdata/Vim/Buffer/Writer/euc-jp-encoding.txt -------------------------------------------------------------------------------- /test/vital/_testdata/Vim/Buffer/Writer/sjis-encoding.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lambdalisue/vim-gina/ff6c2ddeca98f886b57fb42283c12e167d6ab575/test/vital/_testdata/Vim/Buffer/Writer/sjis-encoding.txt -------------------------------------------------------------------------------- /test/vital/_testdata/Vim/Buffer/Writer/utf-8-encoding.txt: -------------------------------------------------------------------------------- 1 | Unix EOL 2 | árvíztűrő tükörfúrógép 3 | ÁRVÍZTŰRŐ TÜKÖRFÚRÓGÉP 4 | --------------------------------------------------------------------------------