├── .gitignore ├── .travis.yml ├── autoload ├── gitappraise.vim ├── vital.vim └── vital │ ├── _gitappraise.vim │ ├── _gitappraise │ ├── Data │ │ ├── List.vim │ │ └── String.vim │ ├── Prelude.vim │ └── Web │ │ └── JSON.vim │ └── gitappraise.vital ├── plugin └── gitappraise.vim └── test ├── binary.vim ├── diff.vim ├── list.vim ├── show.vim └── utils.vim /.gitignore: -------------------------------------------------------------------------------- 1 | ### Vim-Themis ### 2 | vim-themis 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: viml 2 | go: 1.5 3 | 4 | branches: 5 | only: 6 | - master 7 | - develop 8 | - /^feat(ure)?-.*$/ 9 | - /^(hot)?fix-.*$/ 10 | 11 | addons: 12 | apt: 13 | packages: 14 | - vim 15 | 16 | before_install: 17 | - export GOPATH=/home/travis/gopath 18 | - export PATH=$GOPATH/bin:$PATH 19 | - git config --global user.email "travis+vim-git-appraise@such.codes" 20 | - git config --global user.name "Travis" 21 | 22 | install: 23 | - go get github.com/google/git-appraise/git-appraise 24 | 25 | before_script: 26 | - vim --version 27 | - git clone https://github.com/thinca/vim-themis 28 | - git clone https://github.com/syngan/vim-vimlint /tmp/vim-vimlint 29 | - git clone https://github.com/ynkdir/vim-vimlparser /tmp/vim-vimlparser 30 | 31 | script: 32 | - vim-themis/bin/themis --reporter dot test 33 | - sh /tmp/vim-vimlint/bin/vimlint.sh -l /tmp/vim-vimlint -p /tmp/vim-vimlparser -c func_abort=1 autoload/gitappraise.vim plugin 34 | -------------------------------------------------------------------------------- /autoload/gitappraise.vim: -------------------------------------------------------------------------------- 1 | " Vimscript Setup: {{{1 2 | let s:save_cpo = &cpo 3 | set cpo&vim 4 | 5 | " Vital! 6 | let s:V = vital#of('gitappraise') 7 | let s:JSON = s:V.import('Web.JSON') 8 | 9 | let s:BinaryOk = 0 10 | let s:pwd = "" 11 | 12 | " Private Functions: {{{1 13 | " Utils: {{{2 14 | function! s:CheckBinary() abort 15 | if executable(g:git_appraise_binary) 16 | return 1 17 | else 18 | if g:git_appraise_binary == "" 19 | throw "git-appraise: git_appraise_binary not set" 20 | endif 21 | throw "git-appraise: git_appraise_binary not executable" 22 | endif 23 | endfunction 24 | 25 | function! s:GetLineInDiff() abort 26 | let l:result = {'got_files': 0, 'done': 0, 'got_line': 0, 'new': 0, 'deleted': 0, 'files': {}} 27 | let l:buffer_line = line(".") 28 | let l:current_line = l:buffer_line 29 | let l:file_lines = {} 30 | while !l:result.done 31 | if l:current_line < 1 32 | let l:result.done = 0 33 | endif 34 | let l:line = getline(l:current_line) 35 | 36 | if !l:result.got_files && l:line !~ '^diff --git.*' 37 | let l:current_line = search('^diff --git.*', 'Wnbc', 0) 38 | continue 39 | endif 40 | 41 | if l:line =~ '^diff --git.*' 42 | let l:files = matchlist(l:line, '^diff --git \(.\)\/\(.*\) \(.\)\/\(.*\)$') 43 | let l:result.files[l:files[1]] = {'filename': l:files[2]} 44 | let l:result.files[l:files[3]] = {'filename': l:files[4]} 45 | 46 | let l:current_line += 1 47 | let l:line = getline(l:current_line) 48 | 49 | if l:line =~ '^new file mode' 50 | let l:current_line += 1 51 | let l:line = getline(l:current_line) 52 | let l:result.new = 1 53 | endif 54 | 55 | if l:line =~ '^deleted file mode' 56 | let l:current_line += 1 57 | let l:line = getline(l:current_line) 58 | let l:result.deleted = 1 59 | endif 60 | 61 | let l:hashes = matchlist(l:line, '^index \(.*\)\.\.\([^ ]\+\).*$') 62 | let l:result.files[l:files[1]].hash = l:hashes[1] 63 | let l:result.files[l:files[3]].hash = l:hashes[2] 64 | 65 | for l:file in keys(l:result.files) 66 | let l:current_line += 1 67 | let l:line = getline(l:current_line) 68 | for l:key in keys(l:result.files) 69 | if l:result.files[l:key].hash =~ '^0\+$' 70 | let l:line = substitute(l:line, '/dev/null', l:key . '//dev/null', 0) 71 | endif 72 | endfor 73 | let l:sign_file = matchlist(l:line, '^\(.\)\1\1 \(.\)\/.*') 74 | let l:result.files[l:sign_file[2]].sign = l:sign_file[1] 75 | endfor 76 | let l:result.got_files = 1 77 | if l:current_line < l:buffer_line 78 | let l:current_line = l:buffer_line 79 | else 80 | let l:current_line +=1 81 | endif 82 | continue 83 | endif 84 | 85 | if l:result.got_files && !l:result.got_line && l:line !~ '^@@.*' 86 | let l:current_line = search('^@@.*', 'bnWc', l:current_line) 87 | continue 88 | endif 89 | 90 | if l:result.got_files && !l:result.got_line && l:line =~ '^@@.*' 91 | for l:key in keys(l:result.files) 92 | let l:r = l:result.files[l:key] 93 | let l:starting_line_no = matchlist(l:line, ' ' . l:r.sign . '\([0-9]\+\)') 94 | let l:file_lines[l:r.sign] = eval(l:starting_line_no[1]) 95 | let l:result.files[l:key].lineno = eval(l:starting_line_no[1]) 96 | endfor 97 | let l:result.got_line = 1 98 | let l:result.start_line = l:current_line 99 | endif 100 | 101 | if l:result.got_files && l:result.got_line 102 | let l:current_line += 1 103 | let l:line = getline(l:current_line) 104 | let l:sign = l:line[0] 105 | if l:sign == ' ' 106 | for l:key in keys(l:file_lines) 107 | let l:file_lines[l:key] +=1 108 | endfor 109 | else 110 | let l:file_lines[l:sign] +=1 111 | endif 112 | endif 113 | 114 | if l:result.got_files && l:result.got_line && l:current_line >= l:buffer_line 115 | for l:key in keys(l:result.files) 116 | let l:result.files[l:key].lineno = l:file_lines[l:result.files[l:key].sign] 117 | endfor 118 | let l:result.done = 1 119 | endif 120 | 121 | endwhile 122 | return l:result 123 | endfunction 124 | 125 | function! s:FindDiffLineForFile(filename, lineno, whatfile) abort 126 | let l:current_cursor = getpos('.') 127 | norm! gg^ 128 | call search('diff --git .*/' . a:filename . ' .*', 'cW', 'cW') 129 | let l:result = s:GetLineInDiff() 130 | let l:current_line = l:result.start_line 131 | let l:done = 0 132 | let l:line_in_diff = 0 133 | let l:file_lines = {} 134 | for l:key in keys(l:result.files) 135 | let l:f = l:result.files[l:key] 136 | let l:file_lines[l:f.sign] = l:f.lineno 137 | endfor 138 | let l:file_sign = l:result.files[a:whatfile].sign 139 | while !l:done 140 | let l:line = getline(l:current_line) 141 | 142 | if l:line =~ '^@@.*' 143 | for l:key in keys(l:result.files) 144 | let l:r = l:result.files[l:key] 145 | let l:starting_line_no = matchlist(l:line, ' ' . l:r.sign . '\([0-9]\+\)') 146 | let l:file_lines[l:r.sign] = eval(l:starting_line_no[1]) 147 | let l:result.files[l:key].lineno = eval(l:starting_line_no[1]) 148 | endfor 149 | let l:result.got_line = 1 150 | let l:result.start_line = l:current_line 151 | let l:current_line += 1 152 | continue 153 | endif 154 | 155 | if l:line !~ '^@@.*' 156 | let l:current_line += 1 157 | let l:line = getline(l:current_line) 158 | let l:sign = l:line[0] 159 | if l:sign == ' ' 160 | for l:key in keys(l:file_lines) 161 | let l:file_lines[l:key] +=1 162 | endfor 163 | else 164 | let l:file_lines[l:sign] +=1 165 | endif 166 | endif 167 | 168 | if l:file_lines[l:file_sign] == a:lineno 169 | let l:line_in_diff = l:current_line 170 | let l:done = 1 171 | endif 172 | endwhile 173 | call setpos('.', l:current_cursor) 174 | return l:line_in_diff 175 | endfunc 176 | 177 | function! s:SwapCwd() abort 178 | let s:pwd = getcwd() 179 | if bufname('%') !~ 'git-appraise://' 180 | lcd %:p:h 181 | endif 182 | endfunction 183 | 184 | function! s:UnSwapCwd() abort 185 | execute "lcd" s:pwd 186 | let s:pwd = "" 187 | endfunction 188 | 189 | " List: {{{2 190 | function! s:ListSyntax() abort 191 | syntax clear 192 | syn keyword GitAppraisePending pending contained 193 | syn keyword GitAppraiseAccepted accepted contained 194 | syn keyword GitAppraiseRejected rejected contained 195 | 196 | syn match GitAppraiseHash /\s\+[a-z0-9]\+ / contained 197 | 198 | syn match GitAppraiseSummaryStatus /^\[[a-z]\+\]/ contained contains=GitAppraisePending,GitAppraiseAccepted,GitAppraiseRejected 199 | syn match GitAppraiseSummaryDetails /^\[[a-z]\+\]\s\+[a-z0-9]\+ / contained contains=GitAppraiseSummaryStatus,GitAppraiseHash 200 | syn match GitAppraiseSummaryLine '^\[[a-z]\+\]\s\+[a-z0-9]\+ .*$' contains=GitAppraiseSummaryDetails 201 | 202 | hi GitAppraiseSummaryStatus term=bold cterm=bold gui=bold 203 | hi GitAppraisePending term=bold cterm=bold gui=bold ctermfg=11 guifg=#f0c674 204 | hi GitAppraiseAccepted term=bold cterm=bold gui=bold ctermfg=193 guifg=#d7ffaf 205 | hi GitAppraiseRejected term=bold cterm=bold gui=bold ctermfg=9 guifg=#cc6666 206 | hi GitAppraiseHash ctermfg=13 guifg=#b294bb 207 | endfunction 208 | 209 | function! s:ListRequestHash() abort 210 | let l:line = getline('.') 211 | let l:hash = matchlist(l:line, '^\s*\[.*\]\s\+\([a-z0-9]*\)')[1] 212 | return l:hash 213 | endfunction 214 | 215 | function! s:ListBinds() abort 216 | nnoremap :call gitappraise#show(ListRequestHash()) 217 | nnoremap :call gitappraise#show(ListRequestHash()) 218 | nnoremap q :call bw! 219 | nnoremap :call bw! 220 | endfunction 221 | 222 | function! s:ListBuffer(list) abort 223 | if !bufexists('git-appraise://list') 224 | badd git-appraise://list 225 | endif 226 | let l:summary_buffer = bufnr('git-appraise://list') 227 | call setbufvar(l:summary_buffer, '&bufhidden', 'hide') 228 | call setbufvar(l:summary_buffer, '&buflisted', 0) 229 | call setbufvar(l:summary_buffer, '&buftype', 'nofile') 230 | call setbufvar(l:summary_buffer, '&ft', 'gitappraise') 231 | execute 'silent keepa keepjump buffer' l:summary_buffer 232 | call setbufvar(l:summary_buffer, '&readonly', 0) 233 | silent norm! gg"_dG 234 | for l:item in a:list 235 | call append('$', '[' . l:item[0] . "]\t" . l:item[1] . ' ' . l:item[2]) 236 | endfor 237 | silent norm! gg"_dd 238 | call setbufvar(l:summary_buffer, '&readonly', 1) 239 | endfunction 240 | 241 | 242 | function! s:SortList(i1, i2) abort 243 | return a:i1[0] == a:i2[0] ? 0 : a:i1[0] > a:i2[0] ? 1 : -1 244 | endfunction 245 | 246 | function! s:GetList() abort 247 | let l:cmd = g:git_appraise_binary . " list" 248 | let l:output = system(l:cmd) 249 | let l:list_raw = split(l:output, '\s*\n\s*') 250 | let l:list = [] 251 | let l:current = [] 252 | for l:item in l:list_raw[1:] 253 | if len(l:current) == 0 254 | let l:details = matchlist(l:item, '\[\([a-z]\+\)\] \([a-z0-9]\+\)') 255 | call add(l:current, l:details[1]) 256 | call add(l:current, l:details[2]) 257 | else 258 | call add(l:current, l:item) 259 | call add(l:list, l:current) 260 | let l:current = [] 261 | endif 262 | endfor 263 | 264 | return sort(l:list, "s:SortList") 265 | endfunction 266 | " Show: {{{2 267 | 268 | function! s:Show(hash) abort 269 | let l:cmd = g:git_appraise_binary . " show -json " . a:hash 270 | let l:output = system(l:cmd) 271 | if l:output =~ 'There is no matching review\..*' 272 | return {} 273 | endif 274 | let l:show = s:JSON.decode(l:output) 275 | return l:show 276 | endfunction 277 | 278 | function! s:ShowDiff(hash) abort 279 | let l:cmd = g:git_appraise_binary . " show -diff " . a:hash 280 | let l:output = system(l:cmd) 281 | return l:output 282 | endfunction 283 | 284 | function! s:ShowBinds() abort 285 | nnoremap :call ShowFileFromDiff() 286 | nnoremap :call ShowFileFromDiff() 287 | nnoremap q :call bw! 288 | nnoremap :call bw! 289 | endfunction 290 | 291 | function! s:ShowFileFromDiff() abort 292 | let l:current_line = s:GetLineInDiff() 293 | echo l:current_line 294 | endfunction 295 | 296 | 297 | function! s:ShowBuffer(diff) abort 298 | if !bufexists('git-appraise://show') 299 | badd git-appraise://show 300 | endif 301 | let l:summary_buffer = bufnr('git-appraise://show') 302 | call setbufvar(l:summary_buffer, '&bufhidden', 'hide') 303 | call setbufvar(l:summary_buffer, '&buflisted', 0) 304 | call setbufvar(l:summary_buffer, '&buftype', 'nofile') 305 | call setbufvar(l:summary_buffer, '&ft', 'diff') 306 | execute 'silent keepa keepjump buffer' l:summary_buffer 307 | call setbufvar(l:summary_buffer, '&readonly', 0) 308 | silent norm! gg"_dG 309 | let l:reg_contents = @b 310 | let @d = a:diff 311 | put d 312 | let @d = l:reg_contents 313 | silent norm! gg"_dd 314 | call setbufvar(l:summary_buffer, '&readonly', 1) 315 | endfunction 316 | 317 | " Comment: {{{2 318 | " Accept: {{{2 319 | " Reject: {{{2 320 | " Push: {{{2 321 | " Pull: {{{2 322 | 323 | " Library Interface: {{{1 324 | function! gitappraise#show(hash) abort 325 | call s:SwapCwd() 326 | let l:request = s:Show(a:hash) 327 | let l:diff = s:ShowDiff(a:hash) 328 | call s:ShowBuffer(l:diff) 329 | call s:ShowBinds() 330 | call s:UnSwapCwd() 331 | endfunction 332 | 333 | function! gitappraise#list() abort 334 | call s:SwapCwd() 335 | let l:list = s:GetList() 336 | call s:ListBuffer(l:list) 337 | call s:ListBinds() 338 | call s:ListSyntax() 339 | call s:UnSwapCwd() 340 | endfunction 341 | 342 | " Teardown:{{{1 343 | let &cpo = s:save_cpo 344 | 345 | " Misc: {{{1 346 | " vim: set ft=vim ts=2 sw=2 tw=78 et fdm=marker: 347 | -------------------------------------------------------------------------------- /autoload/vital.vim: -------------------------------------------------------------------------------- 1 | function! vital#of(name) abort 2 | let files = globpath(&runtimepath, 'autoload/vital/' . a:name . '.vital') 3 | let file = split(files, "\n") 4 | if empty(file) 5 | throw 'vital: version file not found: ' . a:name 6 | endif 7 | let ver = readfile(file[0], 'b') 8 | if empty(ver) 9 | throw 'vital: invalid version file: ' . a:name 10 | endif 11 | return vital#_{substitute(ver[0], '\W', '', 'g')}#new() 12 | endfunction 13 | -------------------------------------------------------------------------------- /autoload/vital/_gitappraise.vim: -------------------------------------------------------------------------------- 1 | let s:self_version = expand(':t:r') 2 | let s:self_file = expand('') 3 | 4 | " Note: The extra argument to globpath() was added in Patch 7.2.051. 5 | let s:globpath_third_arg = v:version > 702 || v:version == 702 && has('patch51') 6 | 7 | let s:loaded = {} 8 | let s:cache_module_path = {} 9 | let s:cache_sid = {} 10 | 11 | let s:_vital_files_cache_runtimepath = '' 12 | let s:_vital_files_cache = [] 13 | let s:_unify_path_cache = {} 14 | 15 | function! s:import(name, ...) abort 16 | let target = {} 17 | let functions = [] 18 | for a in a:000 19 | if type(a) == type({}) 20 | let target = a 21 | elseif type(a) == type([]) 22 | let functions = a 23 | endif 24 | unlet a 25 | endfor 26 | let module = s:_import(a:name) 27 | if empty(functions) 28 | call extend(target, module, 'keep') 29 | else 30 | for f in functions 31 | if has_key(module, f) && !has_key(target, f) 32 | let target[f] = module[f] 33 | endif 34 | endfor 35 | endif 36 | return target 37 | endfunction 38 | 39 | function! s:load(...) dict abort 40 | for arg in a:000 41 | let [name; as] = type(arg) == type([]) ? arg[: 1] : [arg, arg] 42 | let target = split(join(as, ''), '\W\+') 43 | let dict = self 44 | let dict_type = type({}) 45 | while !empty(target) 46 | let ns = remove(target, 0) 47 | if !has_key(dict, ns) 48 | let dict[ns] = {} 49 | endif 50 | if type(dict[ns]) == dict_type 51 | let dict = dict[ns] 52 | else 53 | unlet dict 54 | break 55 | endif 56 | endwhile 57 | 58 | if exists('dict') 59 | call extend(dict, s:_import(name)) 60 | endif 61 | unlet arg 62 | endfor 63 | return self 64 | endfunction 65 | 66 | function! s:unload() abort 67 | let s:loaded = {} 68 | let s:cache_sid = {} 69 | let s:cache_module_path = {} 70 | endfunction 71 | 72 | function! s:exists(name) abort 73 | return s:_get_module_path(a:name) !=# '' 74 | endfunction 75 | 76 | function! s:search(pattern) abort 77 | let paths = s:_vital_files(a:pattern) 78 | let modules = sort(map(paths, 's:_file2module(v:val)')) 79 | return s:_uniq(modules) 80 | endfunction 81 | 82 | function! s:expand_modules(entry, all) abort 83 | if type(a:entry) == type([]) 84 | let candidates = s:_concat(map(copy(a:entry), 's:search(v:val)')) 85 | if empty(candidates) 86 | throw printf('vital: Any of module %s is not found', string(a:entry)) 87 | endif 88 | if eval(join(map(copy(candidates), 'has_key(a:all, v:val)'), '+')) 89 | let modules = [] 90 | else 91 | let modules = [candidates[0]] 92 | endif 93 | else 94 | let modules = s:search(a:entry) 95 | if empty(modules) 96 | throw printf('vital: Module %s is not found', a:entry) 97 | endif 98 | endif 99 | call filter(modules, '!has_key(a:all, v:val)') 100 | for module in modules 101 | let a:all[module] = 1 102 | endfor 103 | return modules 104 | endfunction 105 | 106 | function! s:_import(name) abort 107 | if type(a:name) == type(0) 108 | return s:_build_module(a:name) 109 | endif 110 | let path = s:_get_module_path(a:name) 111 | if path ==# '' 112 | throw 'vital: module not found: ' . a:name 113 | endif 114 | let sid = s:_get_sid_by_script(path) 115 | if !sid 116 | try 117 | execute 'source' fnameescape(path) 118 | catch /^Vim\%((\a\+)\)\?:E484/ 119 | throw 'vital: module not found: ' . a:name 120 | catch /^Vim\%((\a\+)\)\?:E127/ 121 | " Ignore. 122 | endtry 123 | 124 | let sid = s:_get_sid_by_script(path) 125 | endif 126 | return s:_build_module(sid) 127 | endfunction 128 | 129 | function! s:_get_module_path(name) abort 130 | let key = a:name . '_' 131 | if has_key(s:cache_module_path, key) 132 | return s:cache_module_path[key] 133 | endif 134 | if s:_is_absolute_path(a:name) && filereadable(a:name) 135 | return a:name 136 | endif 137 | if a:name ==# '' 138 | let paths = [s:self_file] 139 | elseif a:name =~# '\v^\u\w*%(\.\u\w*)*$' 140 | let paths = s:_vital_files(a:name) 141 | else 142 | throw 'vital: Invalid module name: ' . a:name 143 | endif 144 | 145 | call filter(paths, 'filereadable(expand(v:val, 1))') 146 | let path = get(paths, 0, '') 147 | let s:cache_module_path[key] = path 148 | return path 149 | endfunction 150 | 151 | function! s:_get_sid_by_script(path) abort 152 | if has_key(s:cache_sid, a:path) 153 | return s:cache_sid[a:path] 154 | endif 155 | 156 | let path = s:_unify_path(a:path) 157 | for line in filter(split(s:_redir('scriptnames'), "\n"), 158 | \ 'stridx(v:val, s:self_version) > 0') 159 | let list = matchlist(line, '^\s*\(\d\+\):\s\+\(.\+\)\s*$') 160 | if !empty(list) && s:_unify_path(list[2]) ==# path 161 | let s:cache_sid[a:path] = list[1] - 0 162 | return s:cache_sid[a:path] 163 | endif 164 | endfor 165 | return 0 166 | endfunction 167 | 168 | function! s:_file2module(file) abort 169 | let filename = fnamemodify(a:file, ':p:gs?[\\/]?/?') 170 | let tail = matchstr(filename, 'autoload/vital/_\w\+/\zs.*\ze\.vim$') 171 | return join(split(tail, '[\\/]\+'), '.') 172 | endfunction 173 | 174 | if filereadable(expand(':r') . '.VIM') 175 | " resolve() is slow, so we cache results. 176 | " Note: On windows, vim can't expand path names from 8.3 formats. 177 | " So if getting full path via and $HOME was set as 8.3 format, 178 | " vital load duplicated scripts. Below's :~ avoid this issue. 179 | function! s:_unify_path(path) abort 180 | if has_key(s:_unify_path_cache, a:path) 181 | return s:_unify_path_cache[a:path] 182 | endif 183 | let value = tolower(fnamemodify(resolve(fnamemodify( 184 | \ a:path, ':p')), ':~:gs?[\\/]?/?')) 185 | let s:_unify_path_cache[a:path] = value 186 | return value 187 | endfunction 188 | else 189 | function! s:_unify_path(path) abort 190 | return resolve(fnamemodify(a:path, ':p:gs?[\\/]?/?')) 191 | endfunction 192 | endif 193 | 194 | if s:globpath_third_arg 195 | function! s:_runtime_files(path) abort 196 | return split(globpath(&runtimepath, a:path, 1), "\n") 197 | endfunction 198 | else 199 | function! s:_runtime_files(path) abort 200 | return split(globpath(&runtimepath, a:path), "\n") 201 | endfunction 202 | endif 203 | 204 | function! s:_vital_files(pattern) abort 205 | if s:_vital_files_cache_runtimepath !=# &runtimepath 206 | let path = printf('autoload/vital/%s/**/*.vim', s:self_version) 207 | let s:_vital_files_cache = s:_runtime_files(path) 208 | let mod = ':p:gs?[\\/]\+?/?' 209 | call map(s:_vital_files_cache, 'fnamemodify(v:val, mod)') 210 | let s:_vital_files_cache_runtimepath = &runtimepath 211 | endif 212 | let target = substitute(a:pattern, '\.', '/', 'g') 213 | let target = substitute(target, '\*', '[^/]*', 'g') 214 | let regexp = printf('autoload/vital/%s/%s.vim', s:self_version, target) 215 | return filter(copy(s:_vital_files_cache), 'v:val =~# regexp') 216 | endfunction 217 | 218 | " Copy from System.Filepath 219 | if has('win16') || has('win32') || has('win64') 220 | function! s:_is_absolute_path(path) abort 221 | return a:path =~? '^[a-z]:[/\\]' 222 | endfunction 223 | else 224 | function! s:_is_absolute_path(path) abort 225 | return a:path[0] ==# '/' 226 | endfunction 227 | endif 228 | 229 | function! s:_build_module(sid) abort 230 | if has_key(s:loaded, a:sid) 231 | return copy(s:loaded[a:sid]) 232 | endif 233 | let functions = s:_get_functions(a:sid) 234 | 235 | let prefix = '' . a:sid . '_' 236 | let module = {} 237 | for func in functions 238 | let module[func] = function(prefix . func) 239 | endfor 240 | if has_key(module, '_vital_created') 241 | call module._vital_created(module) 242 | endif 243 | let export_module = filter(copy(module), 'v:key =~# "^\\a"') 244 | let s:loaded[a:sid] = get(g:, 'vital_debug', 0) ? module : export_module 245 | if has_key(module, '_vital_loaded') 246 | let V = vital#{s:self_version}#new() 247 | call module._vital_loaded(V) 248 | endif 249 | return copy(s:loaded[a:sid]) 250 | endfunction 251 | 252 | if exists('+regexpengine') 253 | function! s:_get_functions(sid) abort 254 | let funcs = s:_redir(printf("function /\\%%#=2^\%d_", a:sid)) 255 | let map_pat = '' . a:sid . '_\zs\w\+' 256 | return map(split(funcs, "\n"), 'matchstr(v:val, map_pat)') 257 | endfunction 258 | else 259 | function! s:_get_functions(sid) abort 260 | let prefix = '' . a:sid . '_' 261 | let funcs = s:_redir('function') 262 | let filter_pat = '^\s*function ' . prefix 263 | let map_pat = prefix . '\zs\w\+' 264 | return map(filter(split(funcs, "\n"), 265 | \ 'stridx(v:val, prefix) > 0 && v:val =~# filter_pat'), 266 | \ 'matchstr(v:val, map_pat)') 267 | endfunction 268 | endif 269 | 270 | if exists('*uniq') 271 | function! s:_uniq(list) abort 272 | return uniq(a:list) 273 | endfunction 274 | else 275 | function! s:_uniq(list) abort 276 | let i = len(a:list) - 1 277 | while 0 < i 278 | if a:list[i] ==# a:list[i - 1] 279 | call remove(a:list, i) 280 | let i -= 2 281 | else 282 | let i -= 1 283 | endif 284 | endwhile 285 | return a:list 286 | endfunction 287 | endif 288 | 289 | function! s:_concat(lists) abort 290 | let result_list = [] 291 | for list in a:lists 292 | let result_list += list 293 | endfor 294 | return result_list 295 | endfunction 296 | 297 | function! s:_redir(cmd) abort 298 | let [save_verbose, save_verbosefile] = [&verbose, &verbosefile] 299 | set verbose=0 verbosefile= 300 | redir => res 301 | silent! execute a:cmd 302 | redir END 303 | let [&verbose, &verbosefile] = [save_verbose, save_verbosefile] 304 | return res 305 | endfunction 306 | 307 | function! vital#{s:self_version}#new() abort 308 | return s:_import('') 309 | endfunction 310 | -------------------------------------------------------------------------------- /autoload/vital/_gitappraise/Data/List.vim: -------------------------------------------------------------------------------- 1 | " Utilities for list. 2 | 3 | let s:save_cpo = &cpo 4 | set cpo&vim 5 | 6 | function! s:pop(list) abort 7 | return remove(a:list, -1) 8 | endfunction 9 | 10 | function! s:push(list, val) abort 11 | call add(a:list, a:val) 12 | return a:list 13 | endfunction 14 | 15 | function! s:shift(list) abort 16 | return remove(a:list, 0) 17 | endfunction 18 | 19 | function! s:unshift(list, val) abort 20 | return insert(a:list, a:val) 21 | endfunction 22 | 23 | function! s:cons(x, xs) abort 24 | return [a:x] + a:xs 25 | endfunction 26 | 27 | function! s:conj(xs, x) abort 28 | return a:xs + [a:x] 29 | endfunction 30 | 31 | " Removes duplicates from a list. 32 | function! s:uniq(list) abort 33 | return s:uniq_by(a:list, 'v:val') 34 | endfunction 35 | 36 | " Removes duplicates from a list. 37 | function! s:uniq_by(list, f) abort 38 | let list = map(copy(a:list), printf('[v:val, %s]', a:f)) 39 | let i = 0 40 | let seen = {} 41 | while i < len(list) 42 | let key = string(list[i][1]) 43 | if has_key(seen, key) 44 | call remove(list, i) 45 | else 46 | let seen[key] = 1 47 | let i += 1 48 | endif 49 | endwhile 50 | return map(list, 'v:val[0]') 51 | endfunction 52 | 53 | function! s:clear(list) abort 54 | if !empty(a:list) 55 | unlet! a:list[0 : len(a:list) - 1] 56 | endif 57 | return a:list 58 | endfunction 59 | 60 | " Concatenates a list of lists. 61 | " XXX: Should we verify the input? 62 | function! s:concat(list) abort 63 | let memo = [] 64 | for Value in a:list 65 | let memo += Value 66 | endfor 67 | return memo 68 | endfunction 69 | 70 | " Take each elements from lists to a new list. 71 | function! s:flatten(list, ...) abort 72 | let limit = a:0 > 0 ? a:1 : -1 73 | let memo = [] 74 | if limit == 0 75 | return a:list 76 | endif 77 | let limit -= 1 78 | for Value in a:list 79 | let memo += 80 | \ type(Value) == type([]) ? 81 | \ s:flatten(Value, limit) : 82 | \ [Value] 83 | unlet! Value 84 | endfor 85 | return memo 86 | endfunction 87 | 88 | " Sorts a list with expression to compare each two values. 89 | " a:a and a:b can be used in {expr}. 90 | function! s:sort(list, expr) abort 91 | if type(a:expr) == type(function('function')) 92 | return sort(a:list, a:expr) 93 | endif 94 | let s:expr = a:expr 95 | return sort(a:list, 's:_compare') 96 | endfunction 97 | 98 | function! s:_compare(a, b) abort 99 | return eval(s:expr) 100 | endfunction 101 | 102 | " Sorts a list using a set of keys generated by mapping the values in the list 103 | " through the given expr. 104 | " v:val is used in {expr} 105 | function! s:sort_by(list, expr) abort 106 | let pairs = map(a:list, printf('[v:val, %s]', a:expr)) 107 | return map(s:sort(pairs, 108 | \ 'a:a[1] ==# a:b[1] ? 0 : a:a[1] ># a:b[1] ? 1 : -1'), 'v:val[0]') 109 | endfunction 110 | 111 | " Returns a maximum value in {list} through given {expr}. 112 | " Returns 0 if {list} is empty. 113 | " v:val is used in {expr} 114 | function! s:max_by(list, expr) abort 115 | if empty(a:list) 116 | return 0 117 | endif 118 | let list = map(copy(a:list), a:expr) 119 | return a:list[index(list, max(list))] 120 | endfunction 121 | 122 | " Returns a minimum value in {list} through given {expr}. 123 | " Returns 0 if {list} is empty. 124 | " v:val is used in {expr} 125 | " FIXME: -0x80000000 == 0x80000000 126 | function! s:min_by(list, expr) abort 127 | return s:max_by(a:list, '-(' . a:expr . ')') 128 | endfunction 129 | 130 | " Returns List of character sequence between [a:from, a:to] 131 | " e.g.: s:char_range('a', 'c') returns ['a', 'b', 'c'] 132 | function! s:char_range(from, to) abort 133 | return map( 134 | \ range(char2nr(a:from), char2nr(a:to)), 135 | \ 'nr2char(v:val)' 136 | \) 137 | endfunction 138 | 139 | " Returns true if a:list has a:value. 140 | " Returns false otherwise. 141 | function! s:has(list, value) abort 142 | return index(a:list, a:value) isnot -1 143 | endfunction 144 | 145 | " Returns true if a:list[a:index] exists. 146 | " Returns false otherwise. 147 | " NOTE: Returns false when a:index is negative number. 148 | function! s:has_index(list, index) abort 149 | " Return true when negative index? 150 | " let index = a:index >= 0 ? a:index : len(a:list) + a:index 151 | return 0 <= a:index && a:index < len(a:list) 152 | endfunction 153 | 154 | " similar to Haskell's Data.List.span 155 | function! s:span(f, xs) abort 156 | let border = len(a:xs) 157 | for i in range(len(a:xs)) 158 | if !eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g')) 159 | let border = i 160 | break 161 | endif 162 | endfor 163 | return border == 0 ? [[], copy(a:xs)] : [a:xs[: border - 1], a:xs[border :]] 164 | endfunction 165 | 166 | " similar to Haskell's Data.List.break 167 | function! s:break(f, xs) abort 168 | return s:span(printf('!(%s)', a:f), a:xs) 169 | endfunction 170 | 171 | " similar to Haskell's Data.List.takeWhile 172 | function! s:take_while(f, xs) abort 173 | return s:span(a:f, a:xs)[0] 174 | endfunction 175 | 176 | " similar to Haskell's Data.List.partition 177 | function! s:partition(f, xs) abort 178 | return [filter(copy(a:xs), a:f), filter(copy(a:xs), '!(' . a:f . ')')] 179 | endfunction 180 | 181 | " similar to Haskell's Prelude.all 182 | function! s:all(f, xs) abort 183 | return !s:any(printf('!(%s)', a:f), a:xs) 184 | endfunction 185 | 186 | " similar to Haskell's Prelude.any 187 | function! s:any(f, xs) abort 188 | return !empty(filter(map(copy(a:xs), a:f), 'v:val')) 189 | endfunction 190 | 191 | " similar to Haskell's Prelude.and 192 | function! s:and(xs) abort 193 | return s:all('v:val', a:xs) 194 | endfunction 195 | 196 | " similar to Haskell's Prelude.or 197 | function! s:or(xs) abort 198 | return s:any('v:val', a:xs) 199 | endfunction 200 | 201 | function! s:map_accum(expr, xs, init) abort 202 | let memo = [] 203 | let init = a:init 204 | for x in a:xs 205 | let expr = substitute(a:expr, 'v:memo', init, 'g') 206 | let expr = substitute(expr, 'v:val', x, 'g') 207 | let [tmp, init] = eval(expr) 208 | call add(memo, tmp) 209 | endfor 210 | return memo 211 | endfunction 212 | 213 | " similar to Haskell's Prelude.foldl 214 | function! s:foldl(f, init, xs) abort 215 | let memo = a:init 216 | for x in a:xs 217 | let expr = substitute(a:f, 'v:val', string(x), 'g') 218 | let expr = substitute(expr, 'v:memo', string(memo), 'g') 219 | unlet memo 220 | let memo = eval(expr) 221 | endfor 222 | return memo 223 | endfunction 224 | 225 | " similar to Haskell's Prelude.foldl1 226 | function! s:foldl1(f, xs) abort 227 | if len(a:xs) == 0 228 | throw 'vital: Data.List: foldl1' 229 | endif 230 | return s:foldl(a:f, a:xs[0], a:xs[1:]) 231 | endfunction 232 | 233 | " similar to Haskell's Prelude.foldr 234 | function! s:foldr(f, init, xs) abort 235 | return s:foldl(a:f, a:init, reverse(copy(a:xs))) 236 | endfunction 237 | 238 | " similar to Haskell's Prelude.fold11 239 | function! s:foldr1(f, xs) abort 240 | if len(a:xs) == 0 241 | throw 'vital: Data.List: foldr1' 242 | endif 243 | return s:foldr(a:f, a:xs[-1], a:xs[0:-2]) 244 | endfunction 245 | 246 | " similar to python's zip() 247 | function! s:zip(...) abort 248 | return map(range(min(map(copy(a:000), 'len(v:val)'))), "map(copy(a:000), 'v:val['.v:val.']')") 249 | endfunction 250 | 251 | " similar to zip(), but goes until the longer one. 252 | function! s:zip_fill(xs, ys, filler) abort 253 | if empty(a:xs) && empty(a:ys) 254 | return [] 255 | elseif empty(a:ys) 256 | return s:cons([a:xs[0], a:filler], s:zip_fill(a:xs[1 :], [], a:filler)) 257 | elseif empty(a:xs) 258 | return s:cons([a:filler, a:ys[0]], s:zip_fill([], a:ys[1 :], a:filler)) 259 | else 260 | return s:cons([a:xs[0], a:ys[0]], s:zip_fill(a:xs[1 :], a:ys[1: ], a:filler)) 261 | endif 262 | endfunction 263 | 264 | " Inspired by Ruby's with_index method. 265 | function! s:with_index(list, ...) abort 266 | let base = a:0 > 0 ? a:1 : 0 267 | return map(copy(a:list), '[v:val, v:key + base]') 268 | endfunction 269 | 270 | " similar to Ruby's detect or Haskell's find. 271 | function! s:find(list, default, f) abort 272 | for x in a:list 273 | if eval(substitute(a:f, 'v:val', string(x), 'g')) 274 | return x 275 | endif 276 | endfor 277 | return a:default 278 | endfunction 279 | 280 | " Returns the index of the first element which satisfies the given expr. 281 | function! s:find_index(xs, f, ...) abort 282 | let len = len(a:xs) 283 | let start = a:0 > 0 ? (a:1 < 0 ? len + a:1 : a:1) : 0 284 | let default = a:0 > 1 ? a:2 : -1 285 | if start >=# len || start < 0 286 | return default 287 | endif 288 | for i in range(start, len - 1) 289 | if eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g')) 290 | return i 291 | endif 292 | endfor 293 | return default 294 | endfunction 295 | 296 | " Returns the index of the last element which satisfies the given expr. 297 | function! s:find_last_index(xs, f, ...) abort 298 | let len = len(a:xs) 299 | let start = a:0 > 0 ? (a:1 < 0 ? len + a:1 : a:1) : len - 1 300 | let default = a:0 > 1 ? a:2 : -1 301 | if start >=# len || start < 0 302 | return default 303 | endif 304 | for i in range(start, 0, -1) 305 | if eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g')) 306 | return i 307 | endif 308 | endfor 309 | return default 310 | endfunction 311 | 312 | " Similar to find_index but returns the list of indices satisfying the given expr. 313 | function! s:find_indices(xs, f, ...) abort 314 | let len = len(a:xs) 315 | let start = a:0 > 0 ? (a:1 < 0 ? len + a:1 : a:1) : 0 316 | let result = [] 317 | if start >=# len || start < 0 318 | return result 319 | endif 320 | for i in range(start, len - 1) 321 | if eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g')) 322 | call add(result, i) 323 | endif 324 | endfor 325 | return result 326 | endfunction 327 | 328 | " Return non-zero if a:list1 and a:list2 have any common item(s). 329 | " Return zero otherwise. 330 | function! s:has_common_items(list1, list2) abort 331 | return !empty(filter(copy(a:list1), 'index(a:list2, v:val) isnot -1')) 332 | endfunction 333 | 334 | function! s:intersect(list1, list2) abort 335 | let items = [] 336 | " for funcref 337 | for X in a:list1 338 | if index(a:list2, X) != -1 && index(items, X) == -1 339 | let items += [X] 340 | endif 341 | endfor 342 | return items 343 | endfunction 344 | 345 | " similar to Ruby's group_by. 346 | function! s:group_by(xs, f) abort 347 | let result = {} 348 | let list = map(copy(a:xs), printf('[v:val, %s]', a:f)) 349 | for x in list 350 | let Val = x[0] 351 | let key = type(x[1]) !=# type('') ? string(x[1]) : x[1] 352 | if has_key(result, key) 353 | call add(result[key], Val) 354 | else 355 | let result[key] = [Val] 356 | endif 357 | unlet Val 358 | endfor 359 | return result 360 | endfunction 361 | 362 | function! s:_default_compare(a, b) abort 363 | return a:a <# a:b ? -1 : a:a ># a:b ? 1 : 0 364 | endfunction 365 | 366 | function! s:binary_search(list, value, ...) abort 367 | let Predicate = a:0 >= 1 ? a:1 : 's:_default_compare' 368 | let dic = a:0 >= 2 ? a:2 : {} 369 | let start = 0 370 | let end = len(a:list) - 1 371 | 372 | while 1 373 | if start > end 374 | return -1 375 | endif 376 | 377 | let middle = (start + end) / 2 378 | 379 | let compared = call(Predicate, [a:value, a:list[middle]], dic) 380 | 381 | if compared < 0 382 | let end = middle - 1 383 | elseif compared > 0 384 | let start = middle + 1 385 | else 386 | return middle 387 | endif 388 | endwhile 389 | endfunction 390 | 391 | function! s:product(lists) abort 392 | let result = [[]] 393 | for pool in a:lists 394 | let tmp = [] 395 | for x in result 396 | let tmp += map(copy(pool), 'x + [v:val]') 397 | endfor 398 | let result = tmp 399 | endfor 400 | return result 401 | endfunction 402 | 403 | function! s:permutations(list, ...) abort 404 | if a:0 > 1 405 | throw 'vital: Data.List: too many arguments' 406 | endif 407 | let r = a:0 == 1 ? a:1 : len(a:list) 408 | if r > len(a:list) 409 | return [] 410 | elseif r < 0 411 | throw 'vital: Data.List: {r} must be non-negative integer' 412 | endif 413 | let n = len(a:list) 414 | let result = [] 415 | for indices in s:product(map(range(r), 'range(n)')) 416 | if len(s:uniq(indices)) == r 417 | call add(result, map(indices, 'a:list[v:val]')) 418 | endif 419 | endfor 420 | return result 421 | endfunction 422 | 423 | function! s:combinations(list, r) abort 424 | if a:r > len(a:list) 425 | return [] 426 | elseif a:r < 0 427 | throw 'vital: Data:List: {r} must be non-negative integer' 428 | endif 429 | let n = len(a:list) 430 | let result = [] 431 | for indices in s:permutations(range(n), a:r) 432 | if s:sort(copy(indices), 'a:a - a:b') == indices 433 | call add(result, map(indices, 'a:list[v:val]')) 434 | endif 435 | endfor 436 | return result 437 | endfunction 438 | 439 | let &cpo = s:save_cpo 440 | unlet s:save_cpo 441 | 442 | " vim:set et ts=2 sts=2 sw=2 tw=0: 443 | -------------------------------------------------------------------------------- /autoload/vital/_gitappraise/Data/String.vim: -------------------------------------------------------------------------------- 1 | " Utilities for string. 2 | 3 | let s:save_cpo = &cpo 4 | set cpo&vim 5 | 6 | function! s:_vital_loaded(V) abort 7 | let s:V = a:V 8 | let s:P = s:V.import('Prelude') 9 | let s:L = s:V.import('Data.List') 10 | endfunction 11 | 12 | function! s:_vital_depends() abort 13 | return ['Prelude', 'Data.List'] 14 | endfunction 15 | 16 | " Substitute a:from => a:to by string. 17 | " To substitute by pattern, use substitute() instead. 18 | function! s:replace(str, from, to) abort 19 | return s:_replace(a:str, a:from, a:to, 'g') 20 | endfunction 21 | 22 | " Substitute a:from => a:to only once. 23 | " cf. s:replace() 24 | function! s:replace_first(str, from, to) abort 25 | return s:_replace(a:str, a:from, a:to, '') 26 | endfunction 27 | 28 | " implement of replace() and replace_first() 29 | function! s:_replace(str, from, to, flags) abort 30 | return substitute(a:str, '\V'.escape(a:from, '\'), escape(a:to, '\'), a:flags) 31 | endfunction 32 | 33 | function! s:scan(str, pattern) abort 34 | let list = [] 35 | call substitute(a:str, a:pattern, '\=add(list, submatch(0)) == [] ? "" : ""', 'g') 36 | return list 37 | endfunction 38 | 39 | function! s:reverse(str) abort 40 | return join(reverse(split(a:str, '.\zs')), '') 41 | endfunction 42 | 43 | function! s:starts_with(str, prefix) abort 44 | return stridx(a:str, a:prefix) == 0 45 | endfunction 46 | 47 | function! s:ends_with(str, suffix) abort 48 | let idx = strridx(a:str, a:suffix) 49 | return 0 <= idx && idx + len(a:suffix) == len(a:str) 50 | endfunction 51 | 52 | function! s:common_head(strs) abort 53 | if empty(a:strs) 54 | return '' 55 | endif 56 | let len = len(a:strs) 57 | if len == 1 58 | return a:strs[0] 59 | endif 60 | let strs = len == 2 ? a:strs : sort(copy(a:strs)) 61 | let pat = substitute(strs[0], '.', '\="[" . escape(submatch(0), "^\\") . "]"', 'g') 62 | return pat == '' ? '' : matchstr(strs[-1], '\C^\%[' . pat . ']') 63 | endfunction 64 | 65 | " Split to two elements of List. ([left, right]) 66 | " e.g.: s:split3('neocomplcache', 'compl') returns ['neo', 'compl', 'cache'] 67 | function! s:split_leftright(expr, pattern) abort 68 | let [left, _, right] = s:split3(a:expr, a:pattern) 69 | return [left, right] 70 | endfunction 71 | 72 | function! s:split3(expr, pattern) abort 73 | let ERROR = ['', '', ''] 74 | if a:expr ==# '' || a:pattern ==# '' 75 | return ERROR 76 | endif 77 | let begin = match(a:expr, a:pattern) 78 | if begin is -1 79 | return ERROR 80 | endif 81 | let end = matchend(a:expr, a:pattern) 82 | let left = begin <=# 0 ? '' : a:expr[: begin - 1] 83 | let right = a:expr[end :] 84 | return [left, a:expr[begin : end-1], right] 85 | endfunction 86 | 87 | " Slices into strings determines the number of substrings. 88 | " e.g.: s:nsplit("neo compl cache", 2, '\s') returns ['neo', 'compl cache'] 89 | function! s:nsplit(expr, n, ...) abort 90 | let pattern = get(a:000, 0, '\s') 91 | let keepempty = get(a:000, 1, 1) 92 | let ret = [] 93 | let expr = a:expr 94 | if a:n <= 1 95 | return [expr] 96 | endif 97 | while 1 98 | let pos = match(expr, pattern) 99 | if pos == -1 100 | if expr !~ pattern || keepempty 101 | call add(ret, expr) 102 | endif 103 | break 104 | elseif pos >= 0 105 | let left = pos > 0 ? expr[:pos-1] : '' 106 | if pos > 0 || keepempty 107 | call add(ret, left) 108 | endif 109 | let ml = len(matchstr(expr, pattern)) 110 | if pos == 0 && ml == 0 111 | let pos = 1 112 | endif 113 | let expr = expr[pos+ml :] 114 | endif 115 | if len(expr) == 0 116 | break 117 | endif 118 | if len(ret) == a:n - 1 119 | call add(ret, expr) 120 | break 121 | endif 122 | endwhile 123 | return ret 124 | endfunction 125 | 126 | " Returns the number of character in a:str. 127 | " NOTE: This returns proper value 128 | " even if a:str contains multibyte character(s). 129 | " s:strchars(str) {{{ 130 | if exists('*strchars') 131 | function! s:strchars(str) abort 132 | return strchars(a:str) 133 | endfunction 134 | else 135 | function! s:strchars(str) abort 136 | return strlen(substitute(copy(a:str), '.', 'x', 'g')) 137 | endfunction 138 | endif "}}} 139 | 140 | " Returns the bool of contains any multibyte character in s:str 141 | function! s:contains_multibyte(str) abort "{{{ 142 | return strlen(a:str) != s:strchars(a:str) 143 | endfunction "}}} 144 | 145 | " Remove last character from a:str. 146 | " NOTE: This returns proper value 147 | " even if a:str contains multibyte character(s). 148 | function! s:chop(str) abort "{{{ 149 | return substitute(a:str, '.$', '', '') 150 | endfunction "}}} 151 | 152 | " Remove last \r,\n,\r\n from a:str. 153 | function! s:chomp(str) abort "{{{ 154 | return substitute(a:str, '\%(\r\n\|[\r\n]\)$', '', '') 155 | endfunction "}}} 156 | 157 | " wrap() and its internal functions 158 | " * _split_by_wcswidth_once() 159 | " * _split_by_wcswidth() 160 | " * _concat() 161 | " * wrap() 162 | " 163 | " NOTE _concat() is just a copy of Data.List.concat(). 164 | " FIXME don't repeat yourself 165 | function! s:_split_by_wcswidth_once(body, x) abort 166 | let fst = s:strwidthpart(a:body, a:x) 167 | let snd = s:strwidthpart_reverse(a:body, s:wcswidth(a:body) - s:wcswidth(fst)) 168 | return [fst, snd] 169 | endfunction 170 | 171 | function! s:_split_by_wcswidth(body, x) abort 172 | let memo = [] 173 | let body = a:body 174 | while s:wcswidth(body) > a:x 175 | let [tmp, body] = s:_split_by_wcswidth_once(body, a:x) 176 | call add(memo, tmp) 177 | endwhile 178 | call add(memo, body) 179 | return memo 180 | endfunction 181 | 182 | function! s:trim(str) abort 183 | return matchstr(a:str,'^\s*\zs.\{-}\ze\s*$') 184 | endfunction 185 | 186 | function! s:trim_start(str) abort 187 | return matchstr(a:str,'^\s*\zs.\{-}$') 188 | endfunction 189 | 190 | function! s:trim_end(str) abort 191 | return matchstr(a:str,'^.\{-}\ze\s*$') 192 | endfunction 193 | 194 | function! s:wrap(str,...) abort 195 | let _columns = a:0 > 0 ? a:1 : &columns 196 | return s:L.concat( 197 | \ map(split(a:str, '\r\n\|[\r\n]'), 's:_split_by_wcswidth(v:val, _columns - 1)')) 198 | endfunction 199 | 200 | function! s:nr2byte(nr) abort 201 | if a:nr < 0x80 202 | return nr2char(a:nr) 203 | elseif a:nr < 0x800 204 | return nr2char(a:nr/64+192).nr2char(a:nr%64+128) 205 | else 206 | return nr2char(a:nr/4096%16+224).nr2char(a:nr/64%64+128).nr2char(a:nr%64+128) 207 | endif 208 | endfunction 209 | 210 | function! s:nr2enc_char(charcode) abort 211 | if &encoding == 'utf-8' 212 | return nr2char(a:charcode) 213 | endif 214 | let char = s:nr2byte(a:charcode) 215 | if strlen(char) > 1 216 | let char = strtrans(iconv(char, 'utf-8', &encoding)) 217 | endif 218 | return char 219 | endfunction 220 | 221 | function! s:nr2hex(nr) abort 222 | let n = a:nr 223 | let r = "" 224 | while n 225 | let r = '0123456789ABCDEF'[n % 16] . r 226 | let n = n / 16 227 | endwhile 228 | return r 229 | endfunction 230 | 231 | " If a ==# b, returns -1. 232 | " If a !=# b, returns first index of different character. 233 | function! s:diffidx(a, b) abort 234 | return a:a ==# a:b ? -1 : strlen(s:common_head([a:a, a:b])) 235 | endfunction 236 | 237 | function! s:substitute_last(expr, pat, sub) abort 238 | return substitute(a:expr, printf('.*\zs%s', a:pat), a:sub, '') 239 | endfunction 240 | 241 | function! s:dstring(expr) abort 242 | let x = substitute(string(a:expr), "^'\\|'$", '', 'g') 243 | let x = substitute(x, "''", "'", 'g') 244 | return printf('"%s"', escape(x, '"')) 245 | endfunction 246 | 247 | function! s:lines(str) abort 248 | return split(a:str, '\r\?\n') 249 | endfunction 250 | 251 | function! s:_pad_with_char(str, left, right, char) abort 252 | return repeat(a:char, a:left). a:str. repeat(a:char, a:right) 253 | endfunction 254 | 255 | function! s:pad_left(str, width, ...) abort 256 | let char = get(a:, 1, ' ') 257 | if strdisplaywidth(char) != 1 258 | throw "vital: Data.String: Can't use non-half-width characters for padding." 259 | endif 260 | let left = max([0, a:width - strdisplaywidth(a:str)]) 261 | return s:_pad_with_char(a:str, left, 0, char) 262 | endfunction 263 | 264 | function! s:pad_right(str, width, ...) abort 265 | let char = get(a:, 1, ' ') 266 | if strdisplaywidth(char) != 1 267 | throw "vital: Data.String: Can't use non-half-width characters for padding." 268 | endif 269 | let right = max([0, a:width - strdisplaywidth(a:str)]) 270 | return s:_pad_with_char(a:str, 0, right, char) 271 | endfunction 272 | 273 | function! s:pad_both_sides(str, width, ...) abort 274 | let char = get(a:, 1, ' ') 275 | if strdisplaywidth(char) != 1 276 | throw "vital: Data.String: Can't use non-half-width characters for padding." 277 | endif 278 | let space = max([0, a:width - strdisplaywidth(a:str)]) 279 | let left = space / 2 280 | let right = space - left 281 | return s:_pad_with_char(a:str, left, right, char) 282 | endfunction 283 | 284 | function! s:pad_between_letters(str, width, ...) abort 285 | let char = get(a:, 1, ' ') 286 | if strdisplaywidth(char) != 1 287 | throw "vital: Data.String: Can't use non-half-width characters for padding." 288 | endif 289 | let letters = split(a:str, '\zs') 290 | let each_width = a:width / len(letters) 291 | let str = join(map(letters, 's:pad_both_sides(v:val, each_width, char)'), '') 292 | if a:width - strdisplaywidth(str) > 0 293 | return char. s:pad_both_sides(str, a:width - 1, char) 294 | endif 295 | return str 296 | endfunction 297 | 298 | function! s:justify_equal_spacing(str, width, ...) abort 299 | let char = get(a:, 1, ' ') 300 | if strdisplaywidth(char) != 1 301 | throw "vital: Data.String: Can't use non-half-width characters for padding." 302 | endif 303 | let letters = split(a:str, '\zs') 304 | let first_letter = letters[0] 305 | " {width w/o the first letter} / {length w/o the first letter} 306 | let each_width = (a:width - strdisplaywidth(first_letter)) / (len(letters) - 1) 307 | let remainder = (a:width - strdisplaywidth(first_letter)) % (len(letters) - 1) 308 | return first_letter. join(s:L.concat([ 309 | \ map(letters[1:remainder], 's:pad_left(v:val, each_width + 1, char)'), 310 | \ map(letters[remainder + 1:], 's:pad_left(v:val, each_width, char)') 311 | \ ]), '') 312 | endfunction 313 | 314 | function! s:levenshtein_distance(str1, str2) abort 315 | let letters1 = split(a:str1, '\zs') 316 | let letters2 = split(a:str2, '\zs') 317 | let length1 = len(letters1) 318 | let length2 = len(letters2) 319 | let distances = map(range(1, length1 + 1), 'map(range(1, length2 + 1), "0")') 320 | 321 | for i1 in range(0, length1) 322 | let distances[i1][0] = i1 323 | endfor 324 | for i2 in range(0, length2) 325 | let distances[0][i2] = i2 326 | endfor 327 | 328 | for i1 in range(1, length1) 329 | for i2 in range(1, length2) 330 | let cost = (letters1[i1 - 1] ==# letters2[i2 - 1]) ? 0 : 1 331 | 332 | let distances[i1][i2] = min([ 333 | \ distances[i1 - 1][i2 ] + 1, 334 | \ distances[i1 ][i2 - 1] + 1, 335 | \ distances[i1 - 1][i2 - 1] + cost, 336 | \]) 337 | endfor 338 | endfor 339 | 340 | return distances[length1][length2] 341 | endfunction 342 | 343 | function! s:padding_by_displaywidth(expr, width, float) abort 344 | let padding_char = ' ' 345 | let n = a:width - strdisplaywidth(a:expr) 346 | if n <= 0 347 | let n = 0 348 | endif 349 | if a:float < 0 350 | return a:expr . repeat(padding_char, n) 351 | elseif 0 < a:float 352 | return repeat(padding_char, n) . a:expr 353 | else 354 | if n % 2 is 0 355 | return repeat(padding_char, n / 2) . a:expr . repeat(padding_char, n / 2) 356 | else 357 | return repeat(padding_char, (n - 1) / 2) . a:expr . repeat(padding_char, (n - 1) / 2) . padding_char 358 | endif 359 | endif 360 | endfunction 361 | 362 | function! s:split_by_displaywidth(expr, width, float, is_wrap) abort 363 | if a:width is 0 364 | return [''] 365 | endif 366 | 367 | let lines = [] 368 | 369 | let cs = split(a:expr, '\zs') 370 | let cs_index = 0 371 | 372 | let text = '' 373 | while cs_index < len(cs) 374 | if cs[cs_index] is "\n" 375 | let text = s:padding_by_displaywidth(text, a:width, a:float) 376 | let lines += [text] 377 | let text = '' 378 | else 379 | let w = strdisplaywidth(text . cs[cs_index]) 380 | 381 | if w < a:width 382 | let text .= cs[cs_index] 383 | elseif a:width < w 384 | let text = s:padding_by_displaywidth(text, a:width, a:float) 385 | else 386 | let text .= cs[cs_index] 387 | endif 388 | 389 | if a:width <= w 390 | let lines += [text] 391 | let text = '' 392 | if a:is_wrap 393 | if a:width < w 394 | if a:width < strdisplaywidth(cs[cs_index]) 395 | while get(cs, cs_index, "\n") isnot "\n" 396 | let cs_index += 1 397 | endwhile 398 | continue 399 | else 400 | let text = cs[cs_index] 401 | endif 402 | endif 403 | else 404 | while get(cs, cs_index, "\n") isnot "\n" 405 | let cs_index += 1 406 | endwhile 407 | continue 408 | endif 409 | endif 410 | 411 | endif 412 | let cs_index += 1 413 | endwhile 414 | 415 | if !empty(text) 416 | let lines += [ s:padding_by_displaywidth(text, a:width, a:float) ] 417 | endif 418 | 419 | return lines 420 | endfunction 421 | 422 | function! s:hash(str) abort 423 | if exists('*sha256') 424 | return sha256(a:str) 425 | else 426 | " This gives up sha256ing but just adds up char with index. 427 | let sum = 0 428 | for i in range(len(a:str)) 429 | let sum += char2nr(a:str[i]) * (i + 1) 430 | endfor 431 | 432 | return printf('%x', sum) 433 | endif 434 | endfunction 435 | 436 | function! s:truncate(str, width) abort 437 | " Original function is from mattn. 438 | " http://github.com/mattn/googlereader-vim/tree/master 439 | 440 | if a:str =~# '^[\x00-\x7f]*$' 441 | return len(a:str) < a:width ? 442 | \ printf('%-'.a:width.'s', a:str) : strpart(a:str, 0, a:width) 443 | endif 444 | 445 | let ret = a:str 446 | let width = s:wcswidth(a:str) 447 | if width > a:width 448 | let ret = s:strwidthpart(ret, a:width) 449 | let width = s:wcswidth(ret) 450 | endif 451 | 452 | if width < a:width 453 | let ret .= repeat(' ', a:width - width) 454 | endif 455 | 456 | return ret 457 | endfunction 458 | 459 | function! s:truncate_skipping(str, max, footer_width, separator) abort 460 | let width = s:wcswidth(a:str) 461 | if width <= a:max 462 | let ret = a:str 463 | else 464 | let header_width = a:max - s:wcswidth(a:separator) - a:footer_width 465 | let ret = s:strwidthpart(a:str, header_width) . a:separator 466 | \ . s:strwidthpart_reverse(a:str, a:footer_width) 467 | endif 468 | return s:truncate(ret, a:max) 469 | endfunction 470 | 471 | function! s:strwidthpart(str, width) abort 472 | if a:width <= 0 473 | return '' 474 | endif 475 | let strarr = split(a:str, '\zs') 476 | let width = s:wcswidth(a:str) 477 | let index = len(strarr) 478 | let diff = (index + 1) / 2 479 | let rightindex = index - 1 480 | while width > a:width 481 | let index = max([rightindex - diff + 1, 0]) 482 | let partwidth = s:wcswidth(join(strarr[(index):(rightindex)], '')) 483 | if width - partwidth >= a:width || diff <= 1 484 | let width -= partwidth 485 | let rightindex = index - 1 486 | endif 487 | if diff > 1 488 | let diff = diff / 2 489 | endif 490 | endwhile 491 | return index ? join(strarr[:index - 1], '') : '' 492 | endfunction 493 | 494 | function! s:strwidthpart_reverse(str, width) abort 495 | if a:width <= 0 496 | return '' 497 | endif 498 | let strarr = split(a:str, '\zs') 499 | let width = s:wcswidth(a:str) 500 | let strlen = len(strarr) 501 | let diff = (strlen + 1) / 2 502 | let leftindex = 0 503 | let index = -1 504 | while width > a:width 505 | let index = min([leftindex + diff, strlen]) - 1 506 | let partwidth = s:wcswidth(join(strarr[(leftindex):(index)], '')) 507 | if width - partwidth >= a:width || diff <= 1 508 | let width -= partwidth 509 | let leftindex = index + 1 510 | endif 511 | if diff > 1 512 | let diff = diff / 2 513 | endif 514 | endwhile 515 | return index < strlen ? join(strarr[(index + 1):], '') : '' 516 | endfunction 517 | 518 | if v:version >= 703 519 | " Use builtin function. 520 | function! s:wcswidth(str) abort 521 | return strwidth(a:str) 522 | endfunction 523 | else 524 | function! s:wcswidth(str) abort 525 | if a:str =~# '^[\x00-\x7f]*$' 526 | return strlen(a:str) 527 | endif 528 | let mx_first = '^\(.\)' 529 | let str = a:str 530 | let width = 0 531 | while 1 532 | let ucs = char2nr(substitute(str, mx_first, '\1', '')) 533 | if ucs == 0 534 | break 535 | endif 536 | let width += s:_wcwidth(ucs) 537 | let str = substitute(str, mx_first, '', '') 538 | endwhile 539 | return width 540 | endfunction 541 | 542 | " UTF-8 only. 543 | function! s:_wcwidth(ucs) abort 544 | let ucs = a:ucs 545 | if (ucs >= 0x1100 546 | \ && (ucs <= 0x115f 547 | \ || ucs == 0x2329 548 | \ || ucs == 0x232a 549 | \ || (ucs >= 0x2e80 && ucs <= 0xa4cf 550 | \ && ucs != 0x303f) 551 | \ || (ucs >= 0xac00 && ucs <= 0xd7a3) 552 | \ || (ucs >= 0xf900 && ucs <= 0xfaff) 553 | \ || (ucs >= 0xfe30 && ucs <= 0xfe6f) 554 | \ || (ucs >= 0xff00 && ucs <= 0xff60) 555 | \ || (ucs >= 0xffe0 && ucs <= 0xffe6) 556 | \ || (ucs >= 0x20000 && ucs <= 0x2fffd) 557 | \ || (ucs >= 0x30000 && ucs <= 0x3fffd) 558 | \ )) 559 | return 2 560 | endif 561 | return 1 562 | endfunction 563 | endif 564 | 565 | let &cpo = s:save_cpo 566 | unlet s:save_cpo 567 | 568 | " vim:set et ts=2 sts=2 sw=2 tw=0: 569 | -------------------------------------------------------------------------------- /autoload/vital/_gitappraise/Prelude.vim: -------------------------------------------------------------------------------- 1 | let s:save_cpo = &cpo 2 | set cpo&vim 3 | 4 | if v:version ># 703 || 5 | \ (v:version is 703 && has('patch465')) 6 | function! s:glob(expr) abort 7 | return glob(a:expr, 1, 1) 8 | endfunction 9 | else 10 | function! s:glob(expr) abort 11 | let R = glob(a:expr, 1) 12 | return split(R, '\n') 13 | endfunction 14 | endif 15 | 16 | function! s:globpath(path, expr) abort 17 | let R = globpath(a:path, a:expr, 1) 18 | return split(R, '\n') 19 | endfunction 20 | 21 | " Wrapper functions for type(). 22 | let [ 23 | \ s:__TYPE_NUMBER, 24 | \ s:__TYPE_STRING, 25 | \ s:__TYPE_FUNCREF, 26 | \ s:__TYPE_LIST, 27 | \ s:__TYPE_DICT, 28 | \ s:__TYPE_FLOAT] = [ 29 | \ type(3), 30 | \ type(""), 31 | \ type(function('tr')), 32 | \ type([]), 33 | \ type({}), 34 | \ has('float') ? type(str2float('0')) : -1] 35 | " __TYPE_FLOAT = -1 when -float 36 | " This doesn't match to anything. 37 | 38 | " Number or Float 39 | function! s:is_numeric(Value) abort 40 | let _ = type(a:Value) 41 | return _ ==# s:__TYPE_NUMBER 42 | \ || _ ==# s:__TYPE_FLOAT 43 | endfunction 44 | 45 | " Number 46 | function! s:is_number(Value) abort 47 | return type(a:Value) ==# s:__TYPE_NUMBER 48 | endfunction 49 | 50 | " Float 51 | function! s:is_float(Value) abort 52 | return type(a:Value) ==# s:__TYPE_FLOAT 53 | endfunction 54 | " String 55 | function! s:is_string(Value) abort 56 | return type(a:Value) ==# s:__TYPE_STRING 57 | endfunction 58 | " Funcref 59 | function! s:is_funcref(Value) abort 60 | return type(a:Value) ==# s:__TYPE_FUNCREF 61 | endfunction 62 | " List 63 | function! s:is_list(Value) abort 64 | return type(a:Value) ==# s:__TYPE_LIST 65 | endfunction 66 | " Dictionary 67 | function! s:is_dict(Value) abort 68 | return type(a:Value) ==# s:__TYPE_DICT 69 | endfunction 70 | 71 | function! s:truncate_skipping(str, max, footer_width, separator) abort 72 | call s:_warn_deprecated("truncate_skipping", "Data.String.truncate_skipping") 73 | 74 | let width = s:wcswidth(a:str) 75 | if width <= a:max 76 | let ret = a:str 77 | else 78 | let header_width = a:max - s:wcswidth(a:separator) - a:footer_width 79 | let ret = s:strwidthpart(a:str, header_width) . a:separator 80 | \ . s:strwidthpart_reverse(a:str, a:footer_width) 81 | endif 82 | 83 | return s:truncate(ret, a:max) 84 | endfunction 85 | 86 | function! s:truncate(str, width) abort 87 | " Original function is from mattn. 88 | " http://github.com/mattn/googlereader-vim/tree/master 89 | 90 | call s:_warn_deprecated("truncate", "Data.String.truncate") 91 | 92 | if a:str =~# '^[\x00-\x7f]*$' 93 | return len(a:str) < a:width ? 94 | \ printf('%-'.a:width.'s', a:str) : strpart(a:str, 0, a:width) 95 | endif 96 | 97 | let ret = a:str 98 | let width = s:wcswidth(a:str) 99 | if width > a:width 100 | let ret = s:strwidthpart(ret, a:width) 101 | let width = s:wcswidth(ret) 102 | endif 103 | 104 | if width < a:width 105 | let ret .= repeat(' ', a:width - width) 106 | endif 107 | 108 | return ret 109 | endfunction 110 | 111 | function! s:strwidthpart(str, width) abort 112 | call s:_warn_deprecated("strwidthpart", "Data.String.strwidthpart") 113 | 114 | if a:width <= 0 115 | return '' 116 | endif 117 | let ret = a:str 118 | let width = s:wcswidth(a:str) 119 | while width > a:width 120 | let char = matchstr(ret, '.$') 121 | let ret = ret[: -1 - len(char)] 122 | let width -= s:wcswidth(char) 123 | endwhile 124 | 125 | return ret 126 | endfunction 127 | function! s:strwidthpart_reverse(str, width) abort 128 | call s:_warn_deprecated("strwidthpart_reverse", "Data.String.strwidthpart_reverse") 129 | 130 | if a:width <= 0 131 | return '' 132 | endif 133 | let ret = a:str 134 | let width = s:wcswidth(a:str) 135 | while width > a:width 136 | let char = matchstr(ret, '^.') 137 | let ret = ret[len(char) :] 138 | let width -= s:wcswidth(char) 139 | endwhile 140 | 141 | return ret 142 | endfunction 143 | 144 | if v:version >= 703 145 | " Use builtin function. 146 | function! s:wcswidth(str) abort 147 | call s:_warn_deprecated("wcswidth", "Data.String.wcswidth") 148 | return strwidth(a:str) 149 | endfunction 150 | else 151 | function! s:wcswidth(str) abort 152 | call s:_warn_deprecated("wcswidth", "Data.String.wcswidth") 153 | 154 | if a:str =~# '^[\x00-\x7f]*$' 155 | return strlen(a:str) 156 | end 157 | 158 | let mx_first = '^\(.\)' 159 | let str = a:str 160 | let width = 0 161 | while 1 162 | let ucs = char2nr(substitute(str, mx_first, '\1', '')) 163 | if ucs == 0 164 | break 165 | endif 166 | let width += s:_wcwidth(ucs) 167 | let str = substitute(str, mx_first, '', '') 168 | endwhile 169 | return width 170 | endfunction 171 | 172 | " UTF-8 only. 173 | function! s:_wcwidth(ucs) abort 174 | let ucs = a:ucs 175 | if (ucs >= 0x1100 176 | \ && (ucs <= 0x115f 177 | \ || ucs == 0x2329 178 | \ || ucs == 0x232a 179 | \ || (ucs >= 0x2e80 && ucs <= 0xa4cf 180 | \ && ucs != 0x303f) 181 | \ || (ucs >= 0xac00 && ucs <= 0xd7a3) 182 | \ || (ucs >= 0xf900 && ucs <= 0xfaff) 183 | \ || (ucs >= 0xfe30 && ucs <= 0xfe6f) 184 | \ || (ucs >= 0xff00 && ucs <= 0xff60) 185 | \ || (ucs >= 0xffe0 && ucs <= 0xffe6) 186 | \ || (ucs >= 0x20000 && ucs <= 0x2fffd) 187 | \ || (ucs >= 0x30000 && ucs <= 0x3fffd) 188 | \ )) 189 | return 2 190 | endif 191 | return 1 192 | endfunction 193 | endif 194 | 195 | let s:is_windows = has('win16') || has('win32') || has('win64') || has('win95') 196 | let s:is_cygwin = has('win32unix') 197 | let s:is_mac = !s:is_windows && !s:is_cygwin 198 | \ && (has('mac') || has('macunix') || has('gui_macvim') || 199 | \ (!isdirectory('/proc') && executable('sw_vers'))) 200 | let s:is_unix = has('unix') 201 | 202 | function! s:is_windows() abort 203 | return s:is_windows 204 | endfunction 205 | 206 | function! s:is_cygwin() abort 207 | return s:is_cygwin 208 | endfunction 209 | 210 | function! s:is_mac() abort 211 | return s:is_mac 212 | endfunction 213 | 214 | function! s:is_unix() abort 215 | return s:is_unix 216 | endfunction 217 | 218 | function! s:_warn_deprecated(name, alternative) abort 219 | try 220 | echohl Error 221 | echomsg "Prelude." . a:name . " is deprecated! Please use " . a:alternative . " instead." 222 | finally 223 | echohl None 224 | endtry 225 | endfunction 226 | 227 | function! s:smart_execute_command(action, word) abort 228 | execute a:action . ' ' . (a:word == '' ? '' : '`=a:word`') 229 | endfunction 230 | 231 | function! s:escape_file_searching(buffer_name) abort 232 | return escape(a:buffer_name, '*[]?{}, ') 233 | endfunction 234 | 235 | function! s:escape_pattern(str) abort 236 | return escape(a:str, '~"\.^$[]*') 237 | endfunction 238 | 239 | function! s:getchar(...) abort 240 | let c = call('getchar', a:000) 241 | return type(c) == type(0) ? nr2char(c) : c 242 | endfunction 243 | 244 | function! s:getchar_safe(...) abort 245 | let c = s:input_helper('getchar', a:000) 246 | return type(c) == type("") ? c : nr2char(c) 247 | endfunction 248 | 249 | function! s:input_safe(...) abort 250 | return s:input_helper('input', a:000) 251 | endfunction 252 | 253 | function! s:input_helper(funcname, args) abort 254 | let success = 0 255 | if inputsave() !=# success 256 | throw 'vital: Prelude: inputsave() failed' 257 | endif 258 | try 259 | return call(a:funcname, a:args) 260 | finally 261 | if inputrestore() !=# success 262 | throw 'vital: Prelude: inputrestore() failed' 263 | endif 264 | endtry 265 | endfunction 266 | 267 | function! s:set_default(var, val) abort 268 | if !exists(a:var) || type({a:var}) != type(a:val) 269 | let {a:var} = a:val 270 | endif 271 | endfunction 272 | 273 | function! s:substitute_path_separator(path) abort 274 | return s:is_windows ? substitute(a:path, '\\', '/', 'g') : a:path 275 | endfunction 276 | 277 | function! s:path2directory(path) abort 278 | return s:substitute_path_separator(isdirectory(a:path) ? a:path : fnamemodify(a:path, ':p:h')) 279 | endfunction 280 | 281 | function! s:_path2project_directory_git(path) abort 282 | let parent = a:path 283 | 284 | while 1 285 | let path = parent . '/.git' 286 | if isdirectory(path) || filereadable(path) 287 | return parent 288 | endif 289 | let next = fnamemodify(parent, ':h') 290 | if next == parent 291 | return '' 292 | endif 293 | let parent = next 294 | endwhile 295 | endfunction 296 | 297 | function! s:_path2project_directory_svn(path) abort 298 | let search_directory = a:path 299 | let directory = '' 300 | 301 | let find_directory = s:escape_file_searching(search_directory) 302 | let d = finddir('.svn', find_directory . ';') 303 | if d == '' 304 | return '' 305 | endif 306 | 307 | let directory = fnamemodify(d, ':p:h:h') 308 | 309 | " Search parent directories. 310 | let parent_directory = s:path2directory( 311 | \ fnamemodify(directory, ':h')) 312 | 313 | if parent_directory != '' 314 | let d = finddir('.svn', parent_directory . ';') 315 | if d != '' 316 | let directory = s:_path2project_directory_svn(parent_directory) 317 | endif 318 | endif 319 | return directory 320 | endfunction 321 | 322 | function! s:_path2project_directory_others(vcs, path) abort 323 | let vcs = a:vcs 324 | let search_directory = a:path 325 | 326 | let find_directory = s:escape_file_searching(search_directory) 327 | let d = finddir(vcs, find_directory . ';') 328 | if d == '' 329 | return '' 330 | endif 331 | return fnamemodify(d, ':p:h:h') 332 | endfunction 333 | 334 | function! s:path2project_directory(path, ...) abort 335 | let is_allow_empty = get(a:000, 0, 0) 336 | let search_directory = s:path2directory(a:path) 337 | let directory = '' 338 | 339 | " Search VCS directory. 340 | for vcs in ['.git', '.bzr', '.hg', '.svn'] 341 | if vcs ==# '.git' 342 | let directory = s:_path2project_directory_git(search_directory) 343 | elseif vcs ==# '.svn' 344 | let directory = s:_path2project_directory_svn(search_directory) 345 | else 346 | let directory = s:_path2project_directory_others(vcs, search_directory) 347 | endif 348 | if directory != '' 349 | break 350 | endif 351 | endfor 352 | 353 | " Search project file. 354 | if directory == '' 355 | for d in ['build.xml', 'prj.el', '.project', 'pom.xml', 'package.json', 356 | \ 'Makefile', 'configure', 'Rakefile', 'NAnt.build', 357 | \ 'P4CONFIG', 'tags', 'gtags'] 358 | let d = findfile(d, s:escape_file_searching(search_directory) . ';') 359 | if d != '' 360 | let directory = fnamemodify(d, ':p:h') 361 | break 362 | endif 363 | endfor 364 | endif 365 | 366 | if directory == '' 367 | " Search /src/ directory. 368 | let base = s:substitute_path_separator(search_directory) 369 | if base =~# '/src/' 370 | let directory = base[: strridx(base, '/src/') + 3] 371 | endif 372 | endif 373 | 374 | if directory == '' && !is_allow_empty 375 | " Use original path. 376 | let directory = search_directory 377 | endif 378 | 379 | return s:substitute_path_separator(directory) 380 | endfunction 381 | 382 | let &cpo = s:save_cpo 383 | unlet s:save_cpo 384 | 385 | " vim:set et ts=2 sts=2 sw=2 tw=0: 386 | -------------------------------------------------------------------------------- /autoload/vital/_gitappraise/Web/JSON.vim: -------------------------------------------------------------------------------- 1 | let s:save_cpo = &cpo 2 | set cpo&vim 3 | 4 | function! s:_true() abort 5 | return 1 6 | endfunction 7 | 8 | function! s:_false() abort 9 | return 0 10 | endfunction 11 | 12 | function! s:_null() abort 13 | return 0 14 | endfunction 15 | 16 | function! s:_resolve(val, prefix) abort 17 | let t = type(a:val) 18 | if t == type('') 19 | let m = matchlist(a:val, '^' . a:prefix . '\(null\|true\|false\)$') 20 | if !empty(m) 21 | return s:const[m[1]] 22 | endif 23 | elseif t == type([]) || t == type({}) 24 | return map(a:val, 's:_resolve(v:val, a:prefix)') 25 | endif 26 | return a:val 27 | endfunction 28 | 29 | 30 | function! s:_vital_created(module) abort 31 | " define constant variables 32 | if !exists('s:const') 33 | let s:const = {} 34 | let s:const.true = function('s:_true') 35 | let s:const.false = function('s:_false') 36 | let s:const.null = function('s:_null') 37 | lockvar s:const 38 | endif 39 | call extend(a:module, s:const) 40 | endfunction 41 | 42 | function! s:_vital_loaded(V) abort 43 | let s:V = a:V 44 | let s:string = s:V.import('Data.String') 45 | endfunction 46 | 47 | function! s:_vital_depends() abort 48 | return ['Data.String'] 49 | endfunction 50 | 51 | " @vimlint(EVL102, 1, l:null) 52 | " @vimlint(EVL102, 1, l:true) 53 | " @vimlint(EVL102, 1, l:false) 54 | function! s:decode(json, ...) abort 55 | let settings = extend({ 56 | \ 'use_token': 0, 57 | \}, get(a:000, 0, {})) 58 | let json = iconv(a:json, 'utf-8', &encoding) 59 | let json = join(split(json, "\n"), '') 60 | let json = substitute(json, '\\u34;', '\\"', 'g') 61 | let json = substitute(json, '\\u\(\x\x\x\x\)', '\=s:string.nr2enc_char("0x".submatch(1))', 'g') 62 | if settings.use_token 63 | let prefix = '__Web.JSON__' 64 | while stridx(json, prefix) != -1 65 | let prefix .= '_' 66 | endwhile 67 | let [null,true,false] = map(['null','true','false'], 'prefix . v:val') 68 | sandbox return s:_resolve(eval(json), prefix) 69 | else 70 | let [null,true,false] = [s:const.null(),s:const.true(),s:const.false()] 71 | sandbox return eval(json) 72 | endif 73 | endfunction 74 | " @vimlint(EVL102, 0, l:null) 75 | " @vimlint(EVL102, 0, l:true) 76 | " @vimlint(EVL102, 0, l:false) 77 | 78 | function! s:encode(val, ...) abort 79 | let settings = extend({ 80 | \ 'indent': 0, 81 | \}, get(a:000, 0, {}) 82 | \) 83 | if type(a:val) == 0 84 | return a:val 85 | elseif type(a:val) == 1 86 | let json = '"' . escape(a:val, '\"') . '"' 87 | let json = substitute(json, "\r", '\\r', 'g') 88 | let json = substitute(json, "\n", '\\n', 'g') 89 | let json = substitute(json, "\t", '\\t', 'g') 90 | return iconv(json, &encoding, 'utf-8') 91 | elseif type(a:val) == 2 92 | if s:const.true == a:val 93 | return 'true' 94 | elseif s:const.false == a:val 95 | return 'false' 96 | elseif s:const.null == a:val 97 | return 'null' 98 | else 99 | " backward compatibility 100 | return string(a:val) 101 | endif 102 | elseif type(a:val) == 3 103 | return s:_encode_list(a:val, settings) 104 | elseif type(a:val) == 4 105 | return s:_encode_dict(a:val, settings) 106 | else 107 | return string(a:val) 108 | endif 109 | endfunction 110 | 111 | " @vimlint(EVL102, 1, l:ns) 112 | function! s:_encode_list(val, settings) abort 113 | if empty(a:val) 114 | return '[]' 115 | elseif !a:settings.indent 116 | let encoded_candidates = map(copy(a:val), 's:encode(v:val, a:settings)') 117 | return printf('[%s]', join(encoded_candidates, ',')) 118 | else 119 | let previous_indent = get(a:settings, '_previous_indent') 120 | let indent = previous_indent + a:settings.indent 121 | let ns = extend(copy(a:settings), { 122 | \ '_previous_indent': indent, 123 | \}) 124 | let encoded_candidates = map( 125 | \ copy(a:val), 126 | \ printf('''%s'' . s:encode(v:val, ns)', repeat(' ', indent)), 127 | \) 128 | return printf( 129 | \ "[\n%s\n%s]", 130 | \ join(encoded_candidates, ",\n"), 131 | \ repeat(' ', previous_indent) 132 | \) 133 | endif 134 | endfunction 135 | " @vimlint(EVL102, 0, l:ns) 136 | 137 | " @vimlint(EVL102, 1, l:ns) 138 | function! s:_encode_dict(val, settings) abort 139 | if empty(a:val) 140 | return '{}' 141 | elseif !a:settings.indent 142 | let encoded_candidates = map(keys(a:val), 143 | \ 's:encode(v:val, a:settings) . '':'' . s:encode(a:val[v:val], a:settings)' 144 | \) 145 | return printf('{%s}', join(encoded_candidates, ',')) 146 | else 147 | let previous_indent = get(a:settings, '_previous_indent') 148 | let indent = previous_indent + a:settings.indent 149 | let ns = extend(copy(a:settings), { 150 | \ '_previous_indent': indent, 151 | \}) 152 | let encoded_candidates = map(keys(a:val), 153 | \ printf( 154 | \ '''%s'' . s:encode(v:val, ns) . '': '' . s:encode(a:val[v:val], ns)', 155 | \ repeat(' ', indent), 156 | \ ), 157 | \) 158 | return printf("{\n%s\n%s}", 159 | \ join(encoded_candidates, ",\n"), 160 | \ repeat(' ', previous_indent), 161 | \) 162 | endif 163 | endfunction 164 | " @vimlint(EVL102, 0, l:ns) 165 | 166 | let &cpo = s:save_cpo 167 | unlet s:save_cpo 168 | 169 | " vim:set et ts=2 sts=2 sw=2 tw=0: 170 | -------------------------------------------------------------------------------- /autoload/vital/gitappraise.vital: -------------------------------------------------------------------------------- 1 | gitappraise 2 | 536046c6f928ad6162141089d3d6e94f58101653 3 | 4 | Web.JSON 5 | -------------------------------------------------------------------------------- /plugin/gitappraise.vim: -------------------------------------------------------------------------------- 1 | " Vimscript Setup: {{{1 2 | let s:save_cpo = &cpo 3 | set cpo&vim 4 | 5 | " load guard 6 | " uncomment after plugin development. 7 | "if exists("g:loaded_git_appraise") 8 | " let &cpo = s:save_cpo 9 | " finish 10 | "endif 11 | "let g:loaded_git_appraise = 1 12 | 13 | " Options: {{{1 14 | if !exists('g:git_appraise_binary') 15 | if executable('git-appraise') 16 | let g:git_appraise_binary = 'git-appraise' 17 | else 18 | let g:git_appraise_binary = "" 19 | endif 20 | 21 | endif 22 | 23 | " Commands: {{{1 24 | command! -nargs=0 GARequest call gitappraise#request() 25 | command! -nargs=? GAPush call gitappraise#push() 26 | command! -nargs=? GAPull call gitappraise#pull() 27 | command! -nargs=0 GAList call gitappraise#list() 28 | command! -nargs=1 GAShow call gitappraise#show() 29 | command! -nargs=1 GAComment call gitappraise#comment() 30 | command! -nargs=* GAAccept call gitappraise#accept() 31 | command! -nargs=0 -bang GASubmit call gitappraise#submit( == "!") 32 | 33 | 34 | " Teardown: {{{1 35 | let &cpo = s:save_cpo 36 | 37 | " Misc: {{{1 38 | " vim: set ft=vim ts=2 sw=2 tw=78 et fdm=marker: 39 | -------------------------------------------------------------------------------- /test/binary.vim: -------------------------------------------------------------------------------- 1 | let s:suite = themis#suite('binary') 2 | let s:assert = themis#helper('assert') 3 | let s:scope = themis#helper('scope') 4 | let s:gitappraise = s:scope.funcs('autoload/gitappraise.vim') 5 | call themis#helper('command').with(s:) 6 | 7 | function! s:suite.before_each() 8 | let g:git_appraise_binary = 0 9 | endfunction 10 | 11 | function! s:suite.check_binary_exists() 12 | let g:git_appraise_binary = "vim" 13 | call s:assert.equals(s:gitappraise.CheckBinary(), 1) 14 | endfunction 15 | 16 | function! s:suite.check_binary_doesnt_exist() 17 | let g:git_appraise_binary = "" 18 | Throws /git-appraise: git_appraise_binary not set/ :call gitappraise.CheckBinary() 19 | endfunction 20 | 21 | function! s:suite.check_binary_isnt_executable() 22 | let g:git_appraise_binary = ".gitignore" 23 | Throws /git-appraise: git_appraise_binary not executable/ :call gitappraise.CheckBinary() 24 | endfunction 25 | -------------------------------------------------------------------------------- /test/diff.vim: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicwest/vim-git-appraise/ad2ef21390802a1fdde4bb84c177cb7e6eecaa30/test/diff.vim -------------------------------------------------------------------------------- /test/list.vim: -------------------------------------------------------------------------------- 1 | let s:suite = themis#suite('list') 2 | let s:assert = themis#helper('assert') 3 | let s:scope = themis#helper('scope') 4 | let s:gitappraise = s:scope.funcs('autoload/gitappraise.vim') 5 | call themis#helper('command').with(s:) 6 | 7 | let s:original_cwd = getcwd() 8 | let s:test_git_path = "" 9 | let s:original_buffers = filter(range(1, bufnr('$')), 'bufexists(v:val)') 10 | 11 | function! s:suite.before() 12 | let g:git_appraise_binary = 'git-appraise' 13 | let l:rand = system("echo -n $RANDOM") 14 | let l:path = "/tmp/vim-git-appraise-test." . l:rand 15 | call system("mkdir " . l:path) 16 | execute "cd" l:path 17 | call system("git init") 18 | call system("echo 'test text' > test.txt") 19 | call system("git add --all") 20 | call system("git commit -m 'first commit!'") 21 | call system("git checkout -b new-feature") 22 | call system("echo 'ALL I WANT IS CAKE!' > test.txt") 23 | call system("git add --all") 24 | call system("git commit -m 'NEW commit!'") 25 | call system(g:git_appraise_binary . " request") 26 | call system("git checkout master") 27 | call system("git checkout -b bug-fix") 28 | call system("echo 'I LIKE PIE!' > test.txt") 29 | call system("git add --all") 30 | call system("git commit -m 'fixing all dem bugs'") 31 | call system(g:git_appraise_binary . " request") 32 | call system(g:git_appraise_binary . " accept") 33 | execute "cd" s:original_cwd 34 | let s:test_git_path = l:path 35 | endfunction 36 | 37 | function! s:suite.after() 38 | call system("rm -rf /tmp/vim-git-appraise-test.*") 39 | execute "cd" s:original_cwd 40 | endfunction 41 | 42 | function! s:suite.before_each() 43 | let l:current_buffers = filter(range(1, bufnr('$')), 'bufexists(v:val)') 44 | let l:buffers_to_wipe = filter(l:current_buffers, 'index(s:original_buffers, v:val) == -1') 45 | for l:buffer in l:buffers_to_wipe 46 | execute 'bw!' . l:buffer 47 | endfor 48 | endfunction 49 | 50 | function! s:suite.list_buffer_creates_new_unlisted_buffer() 51 | let l:list = [] 52 | call s:gitappraise.ListBuffer(l:list) 53 | call s:assert.equals(bufexists('git-appraise://list'), 1) 54 | call s:assert.equals(getbufvar('git-appraise://list', '&buflisted'), 0) 55 | endfunction 56 | 57 | function! s:suite.list_buffer_creates_new_hidden_buffer() 58 | let l:list = [] 59 | call s:gitappraise.ListBuffer(l:list) 60 | call s:assert.equals(bufexists('git-appraise://list'), 1) 61 | call s:assert.equals(getbufvar('git-appraise://list', '&bufhidden'), 'hide') 62 | endfunction 63 | 64 | function! s:suite.list_buffer_creates_new_nofile_buffer() 65 | let l:list = [] 66 | call s:gitappraise.ListBuffer(l:list) 67 | call s:assert.equals(bufexists('git-appraise://list'), 1) 68 | call s:assert.equals(getbufvar('git-appraise://list', '&buftype'), 'nofile') 69 | endfunction 70 | 71 | function! s:suite.list_buffer_creates_new_readonly_buffer() 72 | let l:list = [] 73 | call s:gitappraise.ListBuffer(l:list) 74 | call s:assert.equals(bufexists('git-appraise://list'), 1) 75 | call s:assert.equals(getbufvar('git-appraise://list', '&readonly'), 1) 76 | endfunction 77 | 78 | function! s:suite.list_buffer_focuses_summary_buffer() 79 | let l:list = [] 80 | call s:gitappraise.ListBuffer(l:list) 81 | call s:assert.equals(expand('%'), 'git-appraise://list') 82 | endfunction 83 | 84 | function! s:suite.list_buffer_is_populated_with_list() 85 | let l:list = [['pending', 'abc123', 'Some new feature'], ['approved', 'def456', 'Fixing some bug']] 86 | call s:gitappraise.ListBuffer(l:list) 87 | let l:lines = getbufline('git-appraise://list', 0, '$') 88 | call s:assert.equals(l:lines[0], '[pending] abc123 Some new feature') 89 | call s:assert.equals(l:lines[1], '[approved] def456 Fixing some bug') 90 | endfunction 91 | 92 | function! s:suite.get_list() 93 | execute "cd" s:test_git_path 94 | let l:list = s:gitappraise.GetList() 95 | let l:item = l:list[0] 96 | call s:assert.equals(l:item[0],'accepted') 97 | call s:assert.match(l:item[1],'[a-z0-9]\+') 98 | call s:assert.equals(l:item[2],'fixing all dem bugs') 99 | let l:item = l:list[1] 100 | call s:assert.equals(l:item[0],'pending') 101 | call s:assert.match(l:item[1], '[a-z0-9]\+') 102 | call s:assert.equals(l:item[2],'NEW commit!') 103 | endfunction 104 | 105 | function! s:suite.list_request_hash() 106 | new test_buffer 107 | call append(0, ['[pending] abc123 some new things', '[approved] def456 fixing some shit']) 108 | call cursor(1, 0) 109 | let l:hash = s:gitappraise.ListRequestHash() 110 | call s:assert.equals(l:hash, 'abc123') 111 | call cursor(2, 0) 112 | let l:hash = s:gitappraise.ListRequestHash() 113 | call s:assert.equals(l:hash, 'def456') 114 | endfunction 115 | -------------------------------------------------------------------------------- /test/show.vim: -------------------------------------------------------------------------------- 1 | let s:suite = themis#suite('show') 2 | let s:assert = themis#helper('assert') 3 | let s:scope = themis#helper('scope') 4 | let s:gitappraise = s:scope.funcs('autoload/gitappraise.vim') 5 | call themis#helper('command').with(s:) 6 | 7 | let s:original_cwd = getcwd() 8 | let s:test_git_path = "" 9 | let s:original_buffers = filter(range(1, bufnr('$')), 'bufexists(v:val)') 10 | let s:sample_diff = "diff --git a/foo.txt b/foo.txt\n 11 | \index e69de29..4f59978 100644\n 12 | \--- a/test.txt\n 13 | \+++ b/test.txt\n 14 | \@@ -0,0 +1 @@\n 15 | \+THIS IS A TEST\n 16 | \diff --git a/autoload/foobar.txt b/autoload/foobar.txt\n 17 | \index 600cc6a..a4a5ad6 100644\n 18 | \--- a/autoload/foobar.txt\n 19 | \+++ b/autoload/foobar.txt\n 20 | \@@ -3,6 +3,6 @@\n 21 | \ Foo Bar\n 22 | \ FOOBAR\n 23 | \-f00b4r\n 24 | \+hahahapewpew\n 25 | \+foobar\n 26 | \ foooooooobar\n 27 | \ fb\n 28 | \ barfoo\n 29 | \diff --git a/test/buffers.vim b/test/buffers.vim\n 30 | \deleted file mode 100644\n 31 | \index 3a55482..0000000\n 32 | \--- a/test/buffers.vim\n 33 | \+++ /dev/null\n 34 | \@@ -1,57 +0,0 @@\n 35 | \-let s:suite = themis#suite('buffers')\n 36 | \-let s:assert = themis#helper('assert')\n 37 | \-let s:scope = themis#helper('scope')\n 38 | \-let s:gitappraise = s:scope.funcs('autoload/gitappraise.vim')\n" 39 | 40 | function! s:suite.before() 41 | let g:git_appraise_binary = 'git-appraise' 42 | let l:rand = system("echo -n $RANDOM") 43 | let l:path = "/tmp/vim-git-appraise-test." . l:rand 44 | call system("mkdir " . l:path) 45 | execute "cd" l:path 46 | call system("git init") 47 | call system("echo 'test text' > test.txt") 48 | call system("git add --all") 49 | call system("git commit -m 'first commit!'") 50 | call system("git checkout -b new-feature") 51 | call system("echo 'ALL I WANT IS CAKE!' > test.txt") 52 | call system("git add --all") 53 | call system("git commit -m 'NEW commit!'") 54 | call system(g:git_appraise_binary . " request") 55 | call system("git checkout master") 56 | call system("git checkout -b bug-fix") 57 | call system("echo 'I LIKE PIE!' > test.txt") 58 | call system("git add --all") 59 | call system("git commit -m 'fixing all dem bugs'") 60 | call system(g:git_appraise_binary . " request") 61 | call system(g:git_appraise_binary . " accept") 62 | execute "cd" s:original_cwd 63 | let s:test_git_path = l:path 64 | endfunction 65 | 66 | function! s:suite.after() 67 | call system("rm -rf /tmp/vim-git-appraise-test.*") 68 | execute "cd" s:original_cwd 69 | endfunction 70 | 71 | function! s:suite.before_each() 72 | let l:current_buffers = filter(range(1, bufnr('$')), 'bufexists(v:val)') 73 | let l:buffers_to_wipe = filter(l:current_buffers, 'index(s:original_buffers, v:val) == -1') 74 | for l:buffer in l:buffers_to_wipe 75 | execute 'bw!' . l:buffer 76 | endfor 77 | endfunction 78 | 79 | function! s:suite.show_returns_object_with_the_correct_elements() 80 | execute 'cd' s:test_git_path 81 | let l:list = system('git appraise list') 82 | let l:hash = matchlist(l:list, '\s*.*\] \([a-z0-9]\+\)\n\s*fixing all dem bugs')[1] 83 | let l:show = s:gitappraise.Show(l:hash) 84 | call s:assert.key_exists(l:show, 'comments') 85 | call s:assert.key_exists(l:show, 'resolved') 86 | call s:assert.key_exists(l:show, 'submitted') 87 | call s:assert.key_exists(l:show, 'request') 88 | call s:assert.key_exists(l:show, 'revision') 89 | call s:assert.equals(l:show.revision, l:hash) 90 | call s:assert.equals(l:show.request.reviewRef, 'refs/heads/bug-fix') 91 | endfunction 92 | 93 | function! s:suite.show_diff_gets_request_diff() 94 | execute 'cd' s:test_git_path 95 | let l:list = system('git appraise list') 96 | let l:hash = matchlist(l:list, '\s*.*\] \([a-z0-9]\+\)\n\s*fixing all dem bugs')[1] 97 | let l:diff = s:gitappraise.ShowDiff(l:hash) 98 | endfunction 99 | 100 | function! s:suite.show_buffer_creates_new_unlisted_buffer() 101 | call s:gitappraise.ShowBuffer('') 102 | call s:assert.equals(bufexists('git-appraise://show'), 1) 103 | call s:assert.equals(getbufvar('git-appraise://show', '&buflisted'), 0) 104 | endfunction 105 | 106 | function! s:suite.show_buffer_creates_new_hidden_buffer() 107 | call s:gitappraise.ShowBuffer('') 108 | call s:assert.equals(bufexists('git-appraise://show'), 1) 109 | call s:assert.equals(getbufvar('git-appraise://show', '&bufhidden'), 'hide') 110 | endfunction 111 | 112 | function! s:suite.show_buffer_creates_new_nofile_buffer() 113 | call s:gitappraise.ShowBuffer('') 114 | call s:assert.equals(bufexists('git-appraise://show'), 1) 115 | call s:assert.equals(getbufvar('git-appraise://show', '&buftype'), 'nofile') 116 | endfunction 117 | 118 | function! s:suite.show_buffer_creates_new_readonly_buffer() 119 | call s:gitappraise.ShowBuffer('') 120 | call s:assert.equals(bufexists('git-appraise://show'), 1) 121 | call s:assert.equals(getbufvar('git-appraise://show', '&readonly'), 1) 122 | endfunction 123 | 124 | function! s:suite.show_buffer_creates_new_buffer_with_diff_file_type() 125 | call s:gitappraise.ShowBuffer('') 126 | call s:assert.equals(bufexists('git-appraise://show'), 1) 127 | call s:assert.equals(getbufvar('git-appraise://show', '&ft'), 'diff') 128 | endfunction 129 | 130 | function! s:suite.show_buffer_focuses_summary_buffer() 131 | call s:gitappraise.ShowBuffer('') 132 | call s:assert.equals(expand('%'), 'git-appraise://show') 133 | endfunction 134 | 135 | function! s:suite.show_buffer_is_populated_with_diff() 136 | let l:diff = "--- a/test.txt\n+++ b/test.txt" 137 | call s:gitappraise.ShowBuffer(l:diff) 138 | let l:lines = getbufline('git-appraise://show', 0, '$') 139 | call s:assert.equals(l:lines[0], '--- a/test.txt') 140 | call s:assert.equals(l:lines[1], '+++ b/test.txt') 141 | endfunction 142 | 143 | function! s:suite.get_line_in_diff_returns_correct_filename() 144 | new sample_diff 145 | call append(0, split(s:sample_diff, "\n")) 146 | norm! 17G 147 | let l:line_in_file = s:gitappraise.GetLineInDiff() 148 | call s:assert.equals(l:line_in_file.files.a.filename, 'autoload/foobar.txt') 149 | call s:assert.equals(l:line_in_file.files.b.filename, 'autoload/foobar.txt') 150 | endfunction 151 | 152 | function! s:suite.get_line_in_diff_returns_correct_linenumber() 153 | new sample_diff 154 | call append(0, split(s:sample_diff, "\n")) 155 | norm! 17G 156 | let l:line_in_file = s:gitappraise.GetLineInDiff() 157 | call s:assert.equals(l:line_in_file.files.a.lineno, 7) 158 | call s:assert.equals(l:line_in_file.files.b.lineno, 8) 159 | endfunction 160 | 161 | function! s:suite.get_line_in_diff_returns_correct_hash() 162 | new sample_diff 163 | call append(0, split(s:sample_diff, "\n")) 164 | norm! 17G 165 | let l:line_in_file = s:gitappraise.GetLineInDiff() 166 | call s:assert.equals(l:line_in_file.files.a.hash, '600cc6a') 167 | call s:assert.equals(l:line_in_file.files.b.hash, 'a4a5ad6') 168 | endfunction 169 | 170 | function! s:suite.get_line_in_diff_returns_correctly_on_diff_line() 171 | new sample_diff 172 | call append(0, split(s:sample_diff, "\n")) 173 | norm! 7G 174 | let l:line_in_file = s:gitappraise.GetLineInDiff() 175 | call s:assert.equals(l:line_in_file.got_files, 1) 176 | call s:assert.equals(l:line_in_file.got_line, 1) 177 | call s:assert.equals(l:line_in_file.done, 1) 178 | call s:assert.equals(l:line_in_file.files.a.hash, '600cc6a') 179 | call s:assert.equals(l:line_in_file.files.b.hash, 'a4a5ad6') 180 | call s:assert.equals(l:line_in_file.files.a.lineno, 4) 181 | call s:assert.equals(l:line_in_file.files.b.lineno, 4) 182 | call s:assert.equals(l:line_in_file.files.a.filename, 'autoload/foobar.txt') 183 | call s:assert.equals(l:line_in_file.files.b.filename, 'autoload/foobar.txt') 184 | endfunction 185 | 186 | function! s:suite.get_line_in_diff_returns_correctly_on_index_line() 187 | new sample_diff 188 | call append(0, split(s:sample_diff, "\n")) 189 | norm! 8G 190 | let l:line_in_file = s:gitappraise.GetLineInDiff() 191 | call s:assert.equals(l:line_in_file.got_files, 1) 192 | call s:assert.equals(l:line_in_file.got_line, 1) 193 | call s:assert.equals(l:line_in_file.done, 1) 194 | call s:assert.equals(l:line_in_file.files.a.hash, '600cc6a') 195 | call s:assert.equals(l:line_in_file.files.b.hash, 'a4a5ad6') 196 | call s:assert.equals(l:line_in_file.files.a.lineno, 4) 197 | call s:assert.equals(l:line_in_file.files.b.lineno, 4) 198 | call s:assert.equals(l:line_in_file.files.a.filename, 'autoload/foobar.txt') 199 | call s:assert.equals(l:line_in_file.files.b.filename, 'autoload/foobar.txt') 200 | endfunction 201 | 202 | function! s:suite.get_line_in_diff_returns_correctly_on_sign_line() 203 | new sample_diff 204 | call append(0, split(s:sample_diff, "\n")) 205 | norm! 9G 206 | let l:line_in_file = s:gitappraise.GetLineInDiff() 207 | call s:assert.equals(l:line_in_file.got_files, 1) 208 | call s:assert.equals(l:line_in_file.got_line, 1) 209 | call s:assert.equals(l:line_in_file.done, 1) 210 | call s:assert.equals(l:line_in_file.files.a.hash, '600cc6a') 211 | call s:assert.equals(l:line_in_file.files.b.hash, 'a4a5ad6') 212 | call s:assert.equals(l:line_in_file.files.a.lineno, 4) 213 | call s:assert.equals(l:line_in_file.files.b.lineno, 4) 214 | call s:assert.equals(l:line_in_file.files.a.filename, 'autoload/foobar.txt') 215 | call s:assert.equals(l:line_in_file.files.b.filename, 'autoload/foobar.txt') 216 | norm! 10G 217 | let l:line_in_file = s:gitappraise.GetLineInDiff() 218 | call s:assert.equals(l:line_in_file.got_files, 1) 219 | call s:assert.equals(l:line_in_file.got_line, 1) 220 | call s:assert.equals(l:line_in_file.done, 1) 221 | call s:assert.equals(l:line_in_file.files.a.hash, '600cc6a') 222 | call s:assert.equals(l:line_in_file.files.b.hash, 'a4a5ad6') 223 | call s:assert.equals(l:line_in_file.files.a.lineno, 4) 224 | call s:assert.equals(l:line_in_file.files.b.lineno, 4) 225 | call s:assert.equals(l:line_in_file.files.a.filename, 'autoload/foobar.txt') 226 | call s:assert.equals(l:line_in_file.files.b.filename, 'autoload/foobar.txt') 227 | endfunction 228 | 229 | function! s:suite.get_line_in_diff_returns_correctly_on_first_line_of_snippet() 230 | new sample_diff 231 | call append(0, split(s:sample_diff, "\n")) 232 | norm! 11G 233 | let l:line_in_file = s:gitappraise.GetLineInDiff() 234 | call s:assert.equals(l:line_in_file.got_files, 1) 235 | call s:assert.equals(l:line_in_file.got_line, 1) 236 | call s:assert.equals(l:line_in_file.done, 1) 237 | call s:assert.equals(l:line_in_file.files.a.hash, '600cc6a') 238 | call s:assert.equals(l:line_in_file.files.b.hash, 'a4a5ad6') 239 | call s:assert.equals(l:line_in_file.files.a.lineno, 4) 240 | call s:assert.equals(l:line_in_file.files.b.lineno, 4) 241 | call s:assert.equals(l:line_in_file.files.a.filename, 'autoload/foobar.txt') 242 | call s:assert.equals(l:line_in_file.files.b.filename, 'autoload/foobar.txt') 243 | endfunction 244 | 245 | function! s:suite.get_line_in_diff_returns_correctly_for_deleted_file() 246 | new sample_diff 247 | call append(0, split(s:sample_diff, "\n")) 248 | norm! 28G 249 | let l:line_in_file = s:gitappraise.GetLineInDiff() 250 | call s:assert.equals(l:line_in_file.got_files, 1) 251 | call s:assert.equals(l:line_in_file.got_line, 1) 252 | call s:assert.equals(l:line_in_file.done, 1) 253 | call s:assert.equals(l:line_in_file.files.a.hash, '3a55482') 254 | call s:assert.equals(l:line_in_file.files.b.hash, '0000000') 255 | call s:assert.equals(l:line_in_file.files.a.lineno, 4) 256 | call s:assert.equals(l:line_in_file.files.b.lineno, 0) 257 | call s:assert.equals(l:line_in_file.files.a.filename, 'test/buffers.vim') 258 | call s:assert.equals(l:line_in_file.files.b.filename, 'test/buffers.vim') 259 | endfunction 260 | 261 | function! s:suite.find_line_in_diff_locates_correct_line() 262 | new sample_diff 263 | call append(0, split(s:sample_diff, "\n")) 264 | norm! G"_dd 265 | let l:line_in_diff = s:gitappraise.FindDiffLineForFile('autoload/foobar.txt', 6, 'b') 266 | call s:assert.equals(l:line_in_diff, 16) 267 | endfunction 268 | -------------------------------------------------------------------------------- /test/utils.vim: -------------------------------------------------------------------------------- 1 | let s:suite = themis#suite('utils') 2 | let s:assert = themis#helper('assert') 3 | let s:scope = themis#helper('scope') 4 | let s:gitappraise = s:scope.funcs('autoload/gitappraise.vim') 5 | call themis#helper('command').with(s:) 6 | 7 | let s:original_cwd = getcwd() 8 | let s:test_git_path = "" 9 | let s:original_buffers = filter(range(1, bufnr('$')), 'bufexists(v:val)') 10 | 11 | function! s:suite.before() 12 | let g:git_appraise_binary = 'git-appraise' 13 | let l:rand = system("echo -n $RANDOM") 14 | let l:path = "/tmp/vim-git-appraise-test." . l:rand 15 | call system("mkdir " . l:path) 16 | execute "cd" l:path 17 | call system("git init") 18 | call system("echo 'test text' > test.txt") 19 | call system("git add --all") 20 | call system("git commit -m 'first commit!'") 21 | call system("git checkout -b new-feature") 22 | call system("echo 'ALL I WANT IS CAKE!' > test.txt") 23 | call system("git add --all") 24 | call system("git commit -m 'NEW commit!'") 25 | call system(g:git_appraise_binary . " request") 26 | call system("git checkout master") 27 | call system("git checkout -b bug-fix") 28 | call system("echo 'I LIKE PIE!' > test.txt") 29 | call system("git add --all") 30 | call system("git commit -m 'fixing all dem bugs'") 31 | call system(g:git_appraise_binary . " request") 32 | call system(g:git_appraise_binary . " accept") 33 | call system("git checkout master") 34 | execute "cd" s:original_cwd 35 | let s:test_git_path = l:path 36 | endfunction 37 | 38 | function! s:suite.after() 39 | call system("rm -rf /tmp/vim-git-appraise-test.*") 40 | execute "cd" s:original_cwd 41 | endfunction 42 | 43 | function! s:suite.before_each() 44 | let l:current_buffers = filter(range(1, bufnr('$')), 'bufexists(v:val)') 45 | let l:buffers_to_wipe = filter(l:current_buffers, 'index(s:original_buffers, v:val) > -1') 46 | for l:buffer in l:buffers_to_wipe 47 | execute 'bw!' . l:buffer 48 | endfor 49 | endfunction 50 | 51 | function! s:suite.swap_cwd() 52 | execute printf('edit! %s/test.txt', s:test_git_path) 53 | let l:pwd = getcwd() 54 | call s:gitappraise.SwapCwd() 55 | call s:assert.equals(getcwd(), expand("%:p:h")) 56 | call s:gitappraise.UnSwapCwd() 57 | call s:assert.equals(getcwd(), l:pwd) 58 | endfunction 59 | --------------------------------------------------------------------------------