1
0
Fork 0
mirror of synced 2024-11-21 16:25:34 -05:00

Updated plugins

This commit is contained in:
Amir Salihefendic 2018-03-31 11:56:26 -03:00
parent 7c643a2d9c
commit 02572caa95
84 changed files with 4588 additions and 1749 deletions

View file

@ -89,7 +89,7 @@ I recommend reading the docs of these plugins to understand them better. Each pl
* [open_file_under_cursor.vim](https://github.com/amix/open_file_under_cursor.vim): Open file under cursor when pressing `gf`
* [pathogen.vim](https://github.com/tpope/vim-pathogen): Manage your vim runtimepath
* [snipmate.vim](https://github.com/garbas/vim-snipmate): snipmate.vim aims to be a concise vim script that implements some of TextMate's snippets features in Vim
* [syntastic](https://github.com/scrooloose/syntastic): Syntax checking hacks for vim
* [ale](https://github.com/w0rp/ale): Syntax and lint checking for vim (async)
* [vim-commentary](https://github.com/tpope/vim-commentary): Comment stuff out. Use `gcc` to comment out a line (takes a count), `gc` to comment out the target of a motion. `gcu` uncomments a set of adjacent commented lines.
* [vim-expand-region](https://github.com/terryma/vim-expand-region): Allows you to visually select increasingly larger regions of text using the same key combination
* [vim-fugitive](https://github.com/tpope/vim-fugitive): A Git wrapper so awesome, it should be illegal

View file

@ -130,8 +130,8 @@ function! s:ApplyMappings() "{{{
endif
if exists("g:ackpreview") " if auto preview in on, remap j and k keys
nnoremap <buffer> <silent> j j<CR><C-W><C-W>
nnoremap <buffer> <silent> k k<CR><C-W><C-W>
nnoremap <buffer> <silent> j j<CR><C-W><C-P>
nnoremap <buffer> <silent> k k<CR><C-W><C-P>
nmap <buffer> <silent> <Down> j
nmap <buffer> <silent> <Up> k
endif

View file

@ -540,7 +540,7 @@ fu! s:bufparts(bufnr)
endf
fu! ctrlp#buffers(...)
let ids = sort(filter(range(1, bufnr('$')), '(empty(getbufvar(v:val, "&bt"))'
\ .' || s:isneovimterminal(v:val)) && getbufvar(v:val, "&bl")'), 's:compmreb')
\ .' || s:isterminal(v:val)) && getbufvar(v:val, "&bl")'), 's:compmreb')
if a:0 && a:1 == 'id'
retu ids
el
@ -2031,7 +2031,7 @@ fu! ctrlp#normcmd(cmd, ...)
let buftypes = [ 'quickfix', 'help' ]
if a:0 < 2 && s:nosplit() | retu a:cmd | en
let norwins = filter(range(1, winnr('$')),
\ 'index(buftypes, getbufvar(winbufnr(v:val), "&bt")) == -1 || s:isneovimterminal(winbufnr(v:val))')
\ 'index(buftypes, getbufvar(winbufnr(v:val), "&bt")) == -1 || s:isterminal(winbufnr(v:val))')
for each in norwins
let bufnr = winbufnr(each)
if empty(bufname(bufnr)) && empty(getbufvar(bufnr, '&ft'))
@ -2305,8 +2305,8 @@ fu! s:delbuf()
cal s:PrtClearCache()
endf
fu! s:isneovimterminal(buf)
retu has('nvim') && getbufvar(a:buf, "&bt") == "terminal"
fu! s:isterminal(buf)
retu getbufvar(a:buf, "&bt") == "terminal"
endf
" Entering & Exiting {{{2
fu! s:getenv()

View file

@ -1,4 +1,4 @@
*ctrlp.txt* 支持模糊匹配的 文件, 缓冲区, 最近最多使用, 标签, ... 检索. v1.79
*ctrlp.txt* 支持模糊匹配的 文件, 缓冲区, 最近最多使用, 标签, ... 检索. v1.80
*CtrlP* *ControlP* *'ctrlp'* *'ctrl-p'*
===============================================================================
# #

View file

@ -1,4 +1,4 @@
*ctrlp.txt* Fuzzy file, buffer, mru, tag, ... finder. v1.79
*ctrlp.txt* Fuzzy file, buffer, mru, tag, ... finder. v1.80
*CtrlP* *ControlP* *'ctrlp'* *'ctrl-p'*
===============================================================================
# #

View file

@ -688,8 +688,8 @@ hi! link EasyMotionShade Comment
" }}}
" Sneak: {{{
autocmd ColorScheme gruvbox hi! link Sneak Search
autocmd ColorScheme gruvbox hi! link SneakLabel Search
hi! link Sneak Search
hi! link SneakLabel Search
" }}}
" Indent Guides: {{{

View file

@ -1,37 +1,28 @@
_To assist in resolving your issue, provide as much information as possible, in place of the ellipses (`…`) below._
<!--- To assist in resolving your issue, provide as much information as possible. -->
---
**Environment:** _Describe your Vim/NERDTree setup._
### Environment
<!--- Describe your Vim/NERDTree setup. -->
>* Operating System: …
>* Vim version `:version`: …
>* NERDTree version `git rev-parse --short HEAD`: …
>* NERDTree settings applied in your vimrc, if any:
>
> ```
> …
> ```
* Operating System:
* Vim version `:version`:
* NERDTree version `git rev-parse --short HEAD`:
* NERDTree settings applied in your vimrc, if any:
```vim
```
**Process:** _List the steps that will recreate the issue._
### Process
<!--- List the steps that will recreate the issue. -->
>1.
1.
**Current Result:** _Describe what you you currently experience from this process._
### Current Result
<!--- Describe what you you currently experience from this process. -->
>…
### Expected Result
<!--- Describe what you would have expected from this process. -->
**Expected Result:** _Describe what you would expect to have resulted from this process._
### Screenshot(s)
>…
---
**Optional**
**Screenshot(s):**
>…
**Possible Fix:** _(Have you poked around in the code?)_
>…
### Possible Fix
<!--- If you have explored the code, share what you've found. -->

View file

@ -694,6 +694,16 @@ NERD tree. These options should be set in your vimrc.
|'NERDTreeCreatePrefix'| Specify a prefix to be used when creating the
NERDTree window.
|'NERDTreeRemoveFileCmd'| Specify a custom shell command to be used when
deleting files. Note that it should include
one space character at the end of the command
and it applies only to files.
|'NERDTreeRemoveDirCmd'| Specify a custom shell command to be used when
deleting directories. Note that it should
include one space character at the end of the
command and it applies only to directories.
------------------------------------------------------------------------------
3.2. Customisation details *NERDTreeOptionDetails*

View file

@ -185,7 +185,7 @@ function! s:Creator._createTreeWin()
let splitLocation = g:NERDTreeWinPos ==# "left" ? "topleft " : "botright "
let splitSize = g:NERDTreeWinSize
if !exists('t:NERDTreeBufName')
if !g:NERDTree.ExistsForTab()
let t:NERDTreeBufName = self._nextBufferName()
silent! exec splitLocation . 'vertical ' . splitSize . ' new'
silent! exec "edit " . t:NERDTreeBufName

View file

@ -245,8 +245,14 @@ function! s:Path.delete()
if v:shell_error != 0
throw "NERDTree.PathDeletionError: Could not delete directory: '" . self.str() . "'"
endif
else
if exists('g:NERDTreeRemoveFileCmd')
let cmd = g:NERDTreeRemoveFileCmd . self.str({'escape': 1})
let success = system(cmd)
else
let success = delete(self.str())
endif
if success != 0
throw "NERDTree.PathDeletionError: Could not delete file: '" . self.str() . "'"
endif

View file

@ -0,0 +1,42 @@
" TODO refactor: create glob function
" noremap \og :call<space>glob_linux#FileByGlobCurrentDir('**/*'.input('glob open '),"\\.git\\<bar>\\.hg\\<bar>node_modules\\<bar>\\.pyc" )<cr>
" noremap \og :call<space>glob_linux#FileByGlobCurrentDir('**/*'.input('glob open '),"default" )<cr>
function! glob_linux#FileByGlobCurrentDir(glob, exclude_pattern)
if a:exclude_pattern == "default"
let exclude_pattern = '\.git\|\.hg\|node_modules\|\.pyc'
else
let exclude_pattern = a:exclude_pattern
endif
" let files = split(glob(a:glob),"\n")
let g = a:glob
let replace = {'**': '.*','*': '[^/\]*','.': '\.'}
let g = substitute(g, '\(\*\*\|\*\|\.\)', '\='.string(replace).'[submatch(1)]','g')
let exclude = exclude_pattern == '' ? '' : ' | grep -v -e '.shellescape(exclude_pattern)
let cmd = 'find | grep -e '.shellescape(g).exclude
let files = split(system(cmd),"\n")
" for nom in a:excludes
" call filter(files,nom)
" endfor
if len(files) > 1000
echoe "more than ".2000." files - would be too slow. Open the file in another way"
else
if empty(files)
echoe "no file found"
elseif len(files) == 1
exec 'e '.fnameescape(files[0])
else
let g:abc=7
call tovl#ui#filter_list#ListView({
\ 'number' : 1,
\ 'selectByIdOrFilter' : 1,
\ 'Continuation' : funcref#Function('exec "e ".fnameescape(ARGS[0])'),
\ 'items' : files,
\ 'cmds' : ['wincmd J']
\ })
endif
endif
endfunction

View file

@ -32,7 +32,7 @@ hi def link coffeeConditional Conditional
syn match coffeeException /\<\%(try\|catch\|finally\)\>/ display
hi def link coffeeException Exception
syn match coffeeKeyword /\<\%(new\|in\|of\|by\|and\|or\|not\|is\|isnt\|class\|extends\|super\|do\|yield\|debugger\|import\|export\|default\|await\)\>/
syn match coffeeKeyword /\<\%(new\|in\|of\|from\|by\|and\|or\|not\|is\|isnt\|class\|extends\|super\|do\|yield\|debugger\|import\|export\|default\|await\)\>/
\ display
" The `own` keyword is only a keyword after `for`.
syn match coffeeKeyword /\<for\s\+own\>/ contained containedin=coffeeRepeat

View file

@ -8,8 +8,14 @@ platform issues, and interactions with other plugins. I end up bisecting a
lot more than other projects, and thus I'm especially meticulous here about
maintaining a clean, readable, history. Squash and force push any requested
changes to a pull request. And if your [commit message
sucks](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html),
I'm not going to accept it. Period.
sucks](https://commit.style), I'm not going to accept it. Period.
If your contribution involves adding a configuration option, you are going to
need a very compelling justification for it. Options add a maintenance
burden, support burden, and documentation bloat, and oftentimes can be
achieved much more simply with a custom map or autocommand. If your option
controls an underlying Git command, ask yourself why Git itself does not offer
such configuration.
Beyond that, don't be shy about asking before patching. What takes you hours
might take me minutes simply because I have both domain knowledge and a

View file

@ -0,0 +1,8 @@
> What is the latest commit SHA in your installed vim-gitgutter?
> What vim/nvim version are you on?
> If no signs are showing at all, what does `:echo b:gitgutter.path` give?
> If no signs are showing at all, and the `path` value is a path and not `-2`, does it work with `let g:gitgutter_grep=''`?

View file

@ -1,12 +1,14 @@
## vim-gitgutter
A Vim plugin which shows a git diff in the 'gutter' (sign column). It shows whether each line has been added, modified, and where lines have been removed. You can also stage and undo individual hunks.
A Vim plugin which shows a git diff in the 'gutter' (sign column). It shows which lines have been added, modified, or removed. You can also preview, stage, and undo individual hunks. The plugin also provides a hunk text object.
The signs are always up to date and the plugin never saves your buffer.
Features:
* Shows signs for added, modified, and removed lines.
* Runs the diffs asynchronously in terminal Vim/MacVim (7.4.1826+), gVim (7.4.1850+), MacVim GUI (7.4.1832+), and NeoVim.
* Ensures signs are always as up to date as possible (but without running more than necessary).
* Runs the diffs asynchronously where possible.
* Ensures signs are always up to date.
* Quick jumping between blocks of changed lines ("hunks").
* Stage/undo/preview individual hunks.
* Provides a hunk text object.
@ -21,9 +23,8 @@ Features:
Constraints:
* Supports git only.
If you work with other version control systems, I recommend [vim-signify](https://github.com/mhinz/vim-signify).
* Supports git only. If you work with other version control systems, I recommend [vim-signify](https://github.com/mhinz/vim-signify).
* Relies on the `FocusGained` event. If your terminal doesn't report focus events, either use something like [Terminus][] or set `let g:gitgutter_terminal_reports_focus=0`.
### Screenshot
@ -39,7 +40,7 @@ In the screenshot above you can see:
### Installation
Before installation, please check your Vim supports signs by running `:echo has('signs')`. `1` means you're all set; `0` means you need to install a Vim with signs support. If you're compiling Vim yourself you need the 'big' or 'huge' feature set. [MacVim][] supports signs.
Before installation, please check your Vim supports signs by running `:echo has('signs')`. `1` means you're all set; `0` means you need to install a Vim with signs support. If you're compiling Vim yourself you need the 'big' or 'huge' feature set. MacVim supports signs.
You install vim-gitgutter like any other vim plugin.
@ -100,16 +101,13 @@ cp -r vim-gitgutter/* ~/.vim/
See `:help add-global-plugin`.
If you are on Windows you may find the command prompt pops up briefly every time vim-gitgutter runs. You can avoid this by installing both [vim-misc](https://github.com/xolox/vim-misc) and [vim-shell](https://github.com/xolox/vim-shell). If you have those two plugins but don't want vim-gitgutter to use them, you can opt out with `let g:gitgutter_avoid_cmd_prompt_on_windows = 0` in your `~/.vimrc`.
### Getting started
When you make a change to a file tracked by git, the diff markers should appear automatically. The delay is governed by vim's `updatetime` option; the default value is 4 seconds but I suggest reducing it to around 250ms (add `set updatetime=250` to your vimrc).
When you make a change to a file tracked by git, the diff markers should appear automatically. The delay is governed by vim's `updatetime` option; the default value is `4000`, i.e. 4 seconds, but I suggest reducing it to around 100ms (add `set updatetime=100` to your vimrc).
You can jump between hunks with `[c` and `]c`. You can preview, stage, and undo hunks with `<leader>hp`, `<leader>hs`, and `<leader>hu` respectively.
You cannot currently unstage a staged hunk.
You cannot unstage a staged hunk.
#### Activation
@ -136,7 +134,7 @@ Note that if you have line highlighting on and signs off, you will have an empty
If you switch off both line highlighting and signs, you won't see the sign column. That is unless you configure the sign column always to be there (see Sign Column section).
To keep your Vim snappy, vim-gitgutter will suppress itself when a file has more than 500 changes. As soon as the number of changes falls below the limit vim-gitgutter will show the signs again. You can configure the threshold with:
To keep your Vim snappy, vim-gitgutter will suppress the signs when a file has more than 500 changes. As soon as the number of changes falls below the limit vim-gitgutter will show the signs again. You can configure the threshold with:
```viml
let g:gitgutter_max_signs = 500 " default value
@ -205,33 +203,6 @@ Finally, you can force vim-gitgutter to update its signs across all visible buff
See the customisation section below for how to change the defaults.
### When are the signs updated?
By default the signs are updated as follows:
| Event | Reason for update | Configuration |
|---------------------------|--------------------------------------|------------------------|
| Stop typing | So the signs are real time | `g:gitgutter_realtime` |
| Switch buffer | To notice change to git index | `g:gitgutter_eager` |
| Switch tab | To notice change to git index | `g:gitgutter_eager` |
| Focus the GUI | To notice change to git index | `g:gitgutter_eager` (not gVim on Windows) |
| After shell command | To notice change to git index | `g:gitgutter_eager` |
| Read a file into a buffer | To display initial signs | [always] |
| Save a buffer | So non-realtime signs are up to date | [always] |
| Change a file outside Vim | To notice `git stash` | [always] |
The length of time Vim waits after you stop typing before it triggers the plugin is governed by the setting `updatetime`. This defaults to `4000` milliseconds which is rather too long. I recommend around `250` milliseconds but it depends on your system and your preferences. Note that in terminal Vim pre-7.4.427 an `updatetime` of less than approximately `1000` milliseconds can lead to random highlighting glitches; the lower the `updatetime`, the more glitches.
If you experience a lag, you can trade speed for accuracy:
```viml
let g:gitgutter_realtime = 0
let g:gitgutter_eager = 0
```
Note the realtime updating requires Vim 7.3.105 or higher.
### Customisation
You can customise:
@ -351,7 +322,7 @@ If you use an alternative to grep, you can tell vim-gitgutter to use it here.
```viml
" Default:
let g:gitgutter_grep_command = 'grep'
let g:gitgutter_grep = 'grep'
```
#### To turn off vim-gitgutter by default
@ -490,57 +461,53 @@ nmap <silent> [c :call PrevHunkAllBuffers()<CR>
### FAQ
> How can I turn off realtime updates?
Add this to your vim configuration (in an `/after/plugin` directory):
```viml
" .vim/after/plugin/gitgutter.vim
autocmd! gitgutter CursorHold,CursorHoldI
```
> Why can't I unstage staged changes?
Unstaging staged hunks is feasible but not quite as easy as it sounds. There are three relevant versions of a file at any one time:
1. The version at HEAD in the repo.
2. The version staged in the index.
3. The version in the working tree, in your vim buffer.
`git-diff` without arguments shows you how 3 and 2 differ; this is what vim-gitgutter shows too.
`git-diff --staged` shows you how 2 and 1 differ.
Let's say you are looking at a file in vim which has some unstaged changes. Now you stage a hunk, either via vim-gitgutter or another means. The hunk is no longer marked in vim-gitgutter because it is the same in 3 and 2.
Now you want to unstage that hunk. To see it, you need the difference between 2 and 1. For vim-gitgutter to show those differences, it would need to show you 2 instead of 3 in your vim buffer. But 2 is virtual so vim-gitgutter would need to handle it without touching 3.
I intend to implement this but I can't commit to any deadline.
This plugin is for showing changes between the working tree and the index (and staging/undoing those changes). Unstaging a staged hunk would require showing changes between the index and HEAD, which is out of scope.
> Why are the colours in the sign column weird?
Your colorscheme is configuring the `SignColumn` highlight group weirdly. Please see the section above on customising the sign column.
> There's a noticeable lag when vim-gitter runs; how can I avoid it?
By default vim-gitgutter runs often so the signs are as accurate as possible. The delay is governed by `updatetime`; see [above](#when-are-the-signs-updated) for more information.
If you don't want realtime updates and would like to trade a little accuracy for speed, add this to your `~/.vimrc`:
```viml
let g:gitgutter_realtime = 0
let g:gitgutter_eager = 0
```
> What happens if I also use another plugin which uses signs (e.g. Syntastic)?
Vim only allows one sign per line. Before adding a sign to a line, vim-gitgutter checks whether a sign has already been added by somebody else. If so it doesn't do anything. In other words vim-gitgutter won't overwrite another plugin's signs. It also won't remove another plugin's signs.
> Why aren't any signs showing at all?
### Troubleshooting
#### When no signs are showing at all
Here are some things you can check:
* `:echo system("git --version")` succeeds.
* Your git config is compatible with the version of git returned by the command above.
* Your Vim supports signs (`:echo has('signs')` should give `1`).
* Your file is being tracked by git and has unstaged changes.
* If you have aliased or configured `grep` to use any flags, add `let g:gitgutter_grep_command = 'grep'` to your `~/.vimrc`.
* Try adding `let g:gitgutter_grep=''` to your vimrc. If it works, the problem is grep producing non-plain output; e.g. ANSI escape codes or colours.
* Verify `:echo system("git --version")` succeeds.
* Verify your git config is compatible with the version of git returned by the command above.
* Verify your Vim supports signs (`:echo has('signs')` should give `1`).
* Verify your file is being tracked by git and has unstaged changes.
> Why is the whole file marked as added when I edit it?
#### When the whole file is marked as added
* If you use zsh, and you set `CDPATH`, make sure `CDPATH` doesn't include the current directory.
#### When signs take a few seconds to appear
* Try reducing `updatetime`, e.g. `set updatetime=100`.
#### When signs don't update after focusing Vim
* Your terminal probably isn't reporting focus events. Either try installing [Terminus][] or set `let g:gitgutter_terminal_reports_focus=0`.
### Shameless Plug
@ -561,4 +528,4 @@ Copyright Andrew Stewart, AirBlade Software Ltd. Released under the MIT licence
[pathogen]: https://github.com/tpope/vim-pathogen
[siv]: http://pluralsight.com/training/Courses/TableOfContents/smash-into-vim
[airblade]: http://airbladesoftware.com/peepcode-vim
[macvim]: http://code.google.com/p/macvim/
[terminus]: https://github.com/wincent/terminus

View file

@ -1,67 +1,54 @@
let s:nomodeline = (v:version > 703 || (v:version == 703 && has('patch442'))) ? '<nomodeline>' : ''
let s:t_string = type('')
" Primary functions {{{
function! gitgutter#all() abort
for buffer_id in gitgutter#utility#dedup(tabpagebuflist())
let file = expand('#' . buffer_id . ':p')
function! gitgutter#all(force) abort
for bufnr in s:uniq(tabpagebuflist())
let file = expand('#'.bufnr.':p')
if !empty(file)
call gitgutter#process_buffer(buffer_id, 0)
call gitgutter#init_buffer(bufnr)
call gitgutter#process_buffer(bufnr, a:force)
endif
endfor
endfunction
" bufnr: (integer) the buffer to process.
" realtime: (boolean) when truthy, do a realtime diff; otherwise do a disk-based diff.
function! gitgutter#process_buffer(bufnr, realtime) abort
call gitgutter#utility#use_known_shell()
call gitgutter#utility#set_buffer(a:bufnr)
if gitgutter#utility#is_active()
if g:gitgutter_sign_column_always
call gitgutter#sign#add_dummy_sign()
" Finds the file's path relative to the repo root.
function! gitgutter#init_buffer(bufnr)
if gitgutter#utility#is_active(a:bufnr)
let p = gitgutter#utility#repo_path(a:bufnr, 0)
if type(p) != s:t_string || empty(p)
call gitgutter#utility#set_repo_path(a:bufnr)
endif
endif
endfunction
function! gitgutter#process_buffer(bufnr, force) abort
" NOTE a:bufnr is not necessarily the current buffer.
if gitgutter#utility#is_active(a:bufnr)
if a:force || s:has_fresh_changes(a:bufnr)
let diff = ''
try
if !a:realtime || gitgutter#utility#has_fresh_changes()
let diff = gitgutter#diff#run_diff(a:realtime || gitgutter#utility#has_unsaved_changes(), 0)
if diff != 'async'
call gitgutter#handle_diff(diff)
endif
endif
catch /diff failed/
call gitgutter#debug#log('diff failed')
call gitgutter#hunk#reset()
let diff = gitgutter#diff#run_diff(a:bufnr, 0)
catch /gitgutter not tracked/
call gitgutter#debug#log('Not tracked: '.gitgutter#utility#file(a:bufnr))
catch /gitgutter diff failed/
call gitgutter#debug#log('Diff failed: '.gitgutter#utility#file(a:bufnr))
call gitgutter#hunk#reset(a:bufnr)
endtry
execute "silent doautocmd" s:nomodeline "User GitGutter"
else
call gitgutter#hunk#reset()
if diff != 'async'
call gitgutter#diff#handler(a:bufnr, diff)
endif
call gitgutter#utility#restore_shell()
endif
endif
endfunction
function! gitgutter#handle_diff(diff) abort
call gitgutter#debug#log(a:diff)
call gitgutter#utility#setbufvar(gitgutter#utility#bufnr(), 'tracked', 1)
call gitgutter#hunk#set_hunks(gitgutter#diff#parse_diff(a:diff))
let modified_lines = gitgutter#diff#process_hunks(gitgutter#hunk#hunks())
if len(modified_lines) > g:gitgutter_max_signs
call gitgutter#utility#warn_once('exceeded maximum number of signs (configured by g:gitgutter_max_signs).', 'max_signs')
call gitgutter#sign#clear_signs()
return
endif
if g:gitgutter_signs || g:gitgutter_highlight_lines
call gitgutter#sign#update_signs(modified_lines)
endif
call gitgutter#utility#save_last_seen_change()
endfunction
function! gitgutter#disable() abort
" get list of all buffers (across all tabs)
let buflist = []
@ -69,13 +56,10 @@ function! gitgutter#disable() abort
call extend(buflist, tabpagebuflist(i + 1))
endfor
for buffer_id in gitgutter#utility#dedup(buflist)
let file = expand('#' . buffer_id . ':p')
for bufnr in s:uniq(buflist)
let file = expand('#'.bufnr.':p')
if !empty(file)
call gitgutter#utility#set_buffer(buffer_id)
call gitgutter#sign#clear_signs()
call gitgutter#sign#remove_dummy_sign(1)
call gitgutter#hunk#reset()
call s:clear(bufnr)
endif
endfor
@ -84,7 +68,7 @@ endfunction
function! gitgutter#enable() abort
let g:gitgutter_enabled = 1
call gitgutter#all()
call gitgutter#all(1)
endfunction
function! gitgutter#toggle() abort
@ -97,163 +81,33 @@ endfunction
" }}}
" Line highlights {{{
function! gitgutter#line_highlights_disable() abort
let g:gitgutter_highlight_lines = 0
call gitgutter#highlight#define_sign_line_highlights()
if !g:gitgutter_signs
call gitgutter#sign#clear_signs()
call gitgutter#sign#remove_dummy_sign(0)
endif
redraw!
function! s:has_fresh_changes(bufnr) abort
return getbufvar(a:bufnr, 'changedtick') != gitgutter#utility#getbufvar(a:bufnr, 'tick')
endfunction
function! gitgutter#line_highlights_enable() abort
let old_highlight_lines = g:gitgutter_highlight_lines
let g:gitgutter_highlight_lines = 1
call gitgutter#highlight#define_sign_line_highlights()
if !old_highlight_lines && !g:gitgutter_signs
call gitgutter#all()
endif
redraw!
function! s:reset_tick(bufnr) abort
call gitgutter#utility#setbufvar(a:bufnr, 'tick', 0)
endfunction
function! gitgutter#line_highlights_toggle() abort
if g:gitgutter_highlight_lines
call gitgutter#line_highlights_disable()
else
call gitgutter#line_highlights_enable()
endif
function! s:clear(bufnr)
call gitgutter#sign#clear_signs(a:bufnr)
call gitgutter#sign#remove_dummy_sign(a:bufnr, 1)
call gitgutter#hunk#reset(a:bufnr)
call s:reset_tick(a:bufnr)
endfunction
" }}}
" Signs {{{
function! gitgutter#signs_enable() abort
let old_signs = g:gitgutter_signs
let g:gitgutter_signs = 1
call gitgutter#highlight#define_sign_text_highlights()
if !old_signs && !g:gitgutter_highlight_lines
call gitgutter#all()
if exists('*uniq') " Vim 7.4.218
function! s:uniq(list)
return uniq(sort(a:list))
endfunction
else
function! s:uniq(list)
let processed = []
for e in a:list
if index(processed, e) == -1
call add(processed, e)
endif
endfunction
function! gitgutter#signs_disable() abort
let g:gitgutter_signs = 0
call gitgutter#highlight#define_sign_text_highlights()
if !g:gitgutter_highlight_lines
call gitgutter#sign#clear_signs()
call gitgutter#sign#remove_dummy_sign(0)
endif
endfunction
function! gitgutter#signs_toggle() abort
if g:gitgutter_signs
call gitgutter#signs_disable()
else
call gitgutter#signs_enable()
endif
endfunction
" }}}
" Hunks {{{
function! gitgutter#stage_hunk() abort
call gitgutter#utility#use_known_shell()
if gitgutter#utility#is_active()
" Ensure the working copy of the file is up to date.
" It doesn't make sense to stage a hunk otherwise.
noautocmd silent write
let diff = gitgutter#diff#run_diff(0, 1)
call gitgutter#handle_diff(diff)
if empty(gitgutter#hunk#current_hunk())
call gitgutter#utility#warn('cursor is not in a hunk')
else
let diff_for_hunk = gitgutter#diff#generate_diff_for_hunk(diff, 'stage')
call gitgutter#utility#system(gitgutter#utility#command_in_directory_of_file(g:gitgutter_git_executable.' apply --cached --unidiff-zero - '), diff_for_hunk)
" refresh gitgutter's view of buffer
silent execute "GitGutter"
endif
silent! call repeat#set("\<Plug>GitGutterStageHunk", -1)<CR>
endif
call gitgutter#utility#restore_shell()
endfunction
function! gitgutter#undo_hunk() abort
call gitgutter#utility#use_known_shell()
if gitgutter#utility#is_active()
" Ensure the working copy of the file is up to date.
" It doesn't make sense to stage a hunk otherwise.
noautocmd silent write
let diff = gitgutter#diff#run_diff(0, 1)
call gitgutter#handle_diff(diff)
if empty(gitgutter#hunk#current_hunk())
call gitgutter#utility#warn('cursor is not in a hunk')
else
let diff_for_hunk = gitgutter#diff#generate_diff_for_hunk(diff, 'undo')
call gitgutter#utility#system(gitgutter#utility#command_in_directory_of_file(g:gitgutter_git_executable.' apply --reverse --unidiff-zero - '), diff_for_hunk)
" reload file preserving screen line position
" CTRL-Y and CTRL-E treat negative counts as positive counts.
let x = line('w0')
silent edit
let y = line('w0')
let z = x - y
if z > 0
execute "normal! ".z."\<C-E>"
else
execute "normal! ".z."\<C-Y>"
endif
endif
silent! call repeat#set("\<Plug>GitGutterUndoHunk", -1)<CR>
endif
call gitgutter#utility#restore_shell()
endfunction
function! gitgutter#preview_hunk() abort
call gitgutter#utility#use_known_shell()
if gitgutter#utility#is_active()
" Ensure the working copy of the file is up to date.
" It doesn't make sense to stage a hunk otherwise.
noautocmd silent write
let diff = gitgutter#diff#run_diff(0, 1)
call gitgutter#handle_diff(diff)
if empty(gitgutter#hunk#current_hunk())
call gitgutter#utility#warn('cursor is not in a hunk')
else
let diff_for_hunk = gitgutter#diff#generate_diff_for_hunk(diff, 'preview')
silent! wincmd P
if !&previewwindow
noautocmd execute 'bo' &previewheight 'new'
set previewwindow
endif
setlocal noro modifiable filetype=diff buftype=nofile bufhidden=delete noswapfile
execute "%delete_"
call append(0, split(diff_for_hunk, "\n"))
noautocmd wincmd p
endif
endif
call gitgutter#utility#restore_shell()
endfunction
" }}}
endfor
return processed
endfunction
endif

View file

@ -11,10 +11,13 @@ function! gitgutter#async#available()
endfunction
function! gitgutter#async#execute(cmd) abort
function! gitgutter#async#execute(cmd, bufnr, handler) abort
call gitgutter#debug#log('[async] '.a:cmd)
let options = {
\ 'stdoutbuffer': [],
\ 'buffer': gitgutter#utility#bufnr()
\ 'buffer': a:bufnr,
\ 'handler': a:handler
\ }
let command = s:build_command(a:cmd)
@ -58,33 +61,13 @@ function! s:on_stdout_nvim(_job_id, data, _event) dict abort
endfunction
function! s:on_stderr_nvim(_job_id, _data, _event) dict abort
" Backward compatibility for nvim < 0.2.0
if !has('nvim-0.2.0')
let current_buffer = gitgutter#utility#bufnr()
call gitgutter#utility#set_buffer(self.buffer)
if gitgutter#utility#is_active()
call gitgutter#hunk#reset()
endif
call gitgutter#utility#set_buffer(current_buffer)
return
endif
call s:buffer_exec(self.buffer, function('gitgutter#hunk#reset'))
call self.handler.err(self.buffer)
endfunction
function! s:on_exit_nvim(_job_id, _data, _event) dict abort
" Backward compatibility for nvim < 0.2.0
if !has('nvim-0.2.0')
let current_buffer = gitgutter#utility#bufnr()
call gitgutter#utility#set_buffer(self.buffer)
if gitgutter#utility#is_active()
call gitgutter#handle_diff(gitgutter#utility#stringify(self.stdoutbuffer))
function! s:on_exit_nvim(_job_id, exit_code, _event) dict abort
if !a:exit_code
call self.handler.out(self.buffer, join(self.stdoutbuffer, "\n"))
endif
call gitgutter#utility#set_buffer(current_buffer)
return
endif
call s:buffer_exec(self.buffer, function('gitgutter#handle_diff', [gitgutter#utility#stringify(self.stdoutbuffer)]))
endfunction
@ -92,22 +75,15 @@ function! s:on_stdout_vim(_channel, data) dict abort
call add(self.stdoutbuffer, a:data)
endfunction
function! s:on_stderr_vim(_channel, _data) dict abort
call s:buffer_exec(self.buffer, function('gitgutter#hunk#reset'))
function! s:on_stderr_vim(channel, _data) dict abort
call self.handler.err(self.buffer)
try
call ch_close(a:channel) " so close_cb and its 'out' handler are not triggered
catch /E906/
" noop
endtry
endfunction
function! s:on_exit_vim(_channel) dict abort
call s:buffer_exec(self.buffer, function('gitgutter#handle_diff', [gitgutter#utility#stringify(self.stdoutbuffer)]))
endfunction
function! s:buffer_exec(buffer, fn)
let current_buffer = gitgutter#utility#bufnr()
call gitgutter#utility#set_buffer(a:buffer)
if gitgutter#utility#is_active()
call a:fn()
endif
call gitgutter#utility#set_buffer(current_buffer)
call self.handler.out(self.buffer, join(self.stdoutbuffer, "\n"))
endfunction

View file

@ -12,67 +12,67 @@ function! gitgutter#debug#debug()
setlocal bufhidden=delete
setlocal noswapfile
call gitgutter#debug#vim_version()
call gitgutter#debug#separator()
call s:vim_version()
call s:separator()
call gitgutter#debug#git_version()
call gitgutter#debug#separator()
call s:git_version()
call s:separator()
call gitgutter#debug#grep_version()
call gitgutter#debug#separator()
call s:grep_version()
call s:separator()
call gitgutter#debug#option('updatetime')
call gitgutter#debug#option('shell')
call gitgutter#debug#option('shellcmdflag')
call gitgutter#debug#option('shellpipe')
call gitgutter#debug#option('shellquote')
call gitgutter#debug#option('shellredir')
call gitgutter#debug#option('shellslash')
call gitgutter#debug#option('shelltemp')
call gitgutter#debug#option('shelltype')
call gitgutter#debug#option('shellxescape')
call gitgutter#debug#option('shellxquote')
call s:option('updatetime')
call s:option('shell')
call s:option('shellcmdflag')
call s:option('shellpipe')
call s:option('shellquote')
call s:option('shellredir')
call s:option('shellslash')
call s:option('shelltemp')
call s:option('shelltype')
call s:option('shellxescape')
call s:option('shellxquote')
endfunction
function! gitgutter#debug#separator()
call gitgutter#debug#output('')
function! s:separator()
call s:output('')
endfunction
function! gitgutter#debug#vim_version()
function! s:vim_version()
redir => version_info
silent execute 'version'
redir END
call gitgutter#debug#output(split(version_info, '\n')[0:2])
call s:output(split(version_info, '\n')[0:2])
endfunction
function! gitgutter#debug#git_version()
function! s:git_version()
let v = system(g:gitgutter_git_executable.' --version')
call gitgutter#debug#output( substitute(v, '\n$', '', '') )
call s:output( substitute(v, '\n$', '', '') )
endfunction
function! gitgutter#debug#grep_version()
function! s:grep_version()
let v = system('grep --version')
call gitgutter#debug#output( substitute(v, '\n$', '', '') )
call s:output( substitute(v, '\n$', '', '') )
let v = system('grep --help')
call gitgutter#debug#output( substitute(v, '\%x00', '', 'g') )
call s:output( substitute(v, '\%x00', '', 'g') )
endfunction
function! gitgutter#debug#option(name)
function! s:option(name)
if exists('+' . a:name)
let v = eval('&' . a:name)
call gitgutter#debug#output(a:name . '=' . v)
call s:output(a:name . '=' . v)
" redir => output
" silent execute "verbose set " . a:name . "?"
" redir END
" call gitgutter#debug#output(a:name . '=' . output)
" call s:output(a:name . '=' . output)
else
call gitgutter#debug#output(a:name . ' [n/a]')
call s:output(a:name . ' [n/a]')
end
endfunction
function! gitgutter#debug#output(text)
function! s:output(text)
call append(line('$'), a:text)
endfunction

View file

@ -1,152 +1,176 @@
if exists('g:gitgutter_grep_command')
let s:grep_available = 1
let s:grep_command = g:gitgutter_grep_command
else
let s:grep_available = executable('grep')
if s:grep_available
let s:grep_command = 'grep'
if $GREP_OPTIONS =~# '--color=always'
let s:grep_command .= ' --color=never'
endif
endif
endif
let s:nomodeline = (v:version > 703 || (v:version == 703 && has('patch442'))) ? '<nomodeline>' : ''
let s:hunk_re = '^@@ -\(\d\+\),\?\(\d*\) +\(\d\+\),\?\(\d*\) @@'
let s:c_flag = gitgutter#utility#git_supports_command_line_config_override()
" True for git v1.7.2+.
function! s:git_supports_command_line_config_override() abort
call system(g:gitgutter_git_executable.' -c foo.bar=baz --version')
return !v:shell_error
endfunction
let s:c_flag = s:git_supports_command_line_config_override()
let s:temp_index = tempname()
let s:temp_buffer = tempname()
" Returns a diff of the buffer.
"
" The way to get the diff depends on whether the buffer is saved or unsaved.
"
" * Saved: the buffer contents is the same as the file on disk in the working
" tree so we simply do:
"
" git diff myfile
"
" * Unsaved: the buffer contents is not the same as the file on disk so we
" need to pass two instances of the file to git-diff:
" The buffer contents is not the same as the file on disk so we need to pass
" two instances of the file to git-diff:
"
" git diff myfileA myfileB
"
" The first instance is the file in the index which we obtain with:
" where myfileA comes from
"
" git show :myfile > myfileA
"
" The second instance is the buffer contents. Ideally we would pass this to
" git-diff on stdin via the second argument to vim's system() function.
" Unfortunately git-diff does not do CRLF conversion for input received on
" stdin, and git-show never performs CRLF conversion, so repos with CRLF
" conversion report that every line is modified due to mismatching EOLs.
"
" Instead, we write the buffer contents to a temporary file - myfileB in this
" example. Note the file extension must be preserved for the CRLF
" conversion to work.
"
" Before diffing a buffer for the first time, we check whether git knows about
" the file:
"
" git ls-files --error-unmatch myfile
" and myfileB is the buffer contents.
"
" After running the diff we pass it through grep where available to reduce
" subsequent processing by the plugin. If grep is not available the plugin
" does the filtering instead.
function! gitgutter#diff#run_diff(realtime, preserve_full_diff) abort
"
"
" Regarding line endings:
"
" git-show does not convert line endings.
" git-diff FILE FILE does convert line endings for the given files.
"
" If a file has CRLF line endings and git's core.autocrlf is true,
" the file in git's object store will have LF line endings. Writing
" it out via git-show will produce a file with LF line endings.
"
" If this last file is one of the files passed to git-diff, git-diff will
" convert its line endings to CRLF before diffing -- which is what we want --
" but also by default output a warning on stderr.
"
" warning: LF will be replace by CRLF in <temp file>.
" The file will have its original line endings in your working directory.
"
" When running the diff asynchronously, the warning message triggers the stderr
" callbacks which assume the overall command has failed and reset all the
" signs. As this is not what we want, and we can safely ignore the warning,
" we turn it off by passing the '-c "core.safecrlf=false"' argument to
" git-diff.
"
" When writing the temporary files we preserve the original file's extension
" so that repos using .gitattributes to control EOL conversion continue to
" convert correctly.
function! gitgutter#diff#run_diff(bufnr, preserve_full_diff) abort
while gitgutter#utility#repo_path(a:bufnr, 0) == -1
sleep 5m
endwhile
if gitgutter#utility#repo_path(a:bufnr, 0) == -2
throw 'gitgutter not tracked'
endif
" Wrap compound commands in parentheses to make Windows happy.
" bash doesn't mind the parentheses.
let cmd = '('
let bufnr = gitgutter#utility#bufnr()
let tracked = gitgutter#utility#getbufvar(bufnr, 'tracked', 0) " i.e. tracked by git
if !tracked
" Don't bother trying to realtime-diff an untracked file.
" NOTE: perhaps we should pull this guard up to the caller?
if a:realtime
throw 'diff failed'
else
let cmd .= g:gitgutter_git_executable.' ls-files --error-unmatch '.gitgutter#utility#shellescape(gitgutter#utility#filename()).' && ('
endif
endif
" Append buffer number to avoid race conditions between writing and reading
" the files when asynchronously processing multiple buffers.
"
" Without the buffer number, index_file would have a race in the shell
" between the second process writing it (with git-show) and the first
" reading it (with git-diff).
let index_file = s:temp_index.'.'.a:bufnr
if a:realtime
let blob_name = g:gitgutter_diff_base.':'.gitgutter#utility#shellescape(gitgutter#utility#file_relative_to_repo_root())
let blob_file = s:temp_index
let buff_file = s:temp_buffer
let extension = gitgutter#utility#extension()
" Without the buffer number, buff_file would have a race between the
" second gitgutter#process_buffer() writing the file (synchronously, below)
" and the first gitgutter#process_buffer()'s async job reading it (with
" git-diff).
let buff_file = s:temp_buffer.'.'.a:bufnr
let extension = gitgutter#utility#extension(a:bufnr)
if !empty(extension)
let blob_file .= '.'.extension
let index_file .= '.'.extension
let buff_file .= '.'.extension
endif
let cmd .= g:gitgutter_git_executable.' show '.blob_name.' > '.blob_file.' && '
" Writing the whole buffer resets the '[ and '] marks and also the
" 'modified' flag (if &cpoptions includes '+'). These are unwanted
" side-effects so we save and restore the values ourselves.
let modified = getbufvar(bufnr, "&mod")
let op_mark_start = getpos("'[")
let op_mark_end = getpos("']")
" Write file from index to temporary file.
let index_name = g:gitgutter_diff_base.':'.gitgutter#utility#repo_path(a:bufnr, 1)
let cmd .= g:gitgutter_git_executable.' --no-pager show '.index_name.' > '.index_file.' && '
let current_buffer = bufnr('')
execute 'buffer '.bufnr
execute 'keepalt noautocmd silent write!' buff_file
execute 'buffer '.current_buffer
" Write buffer to temporary file.
" Note: this is synchronous.
call s:write_buffer(a:bufnr, buff_file)
call setbufvar(bufnr, "&mod", modified)
call setpos("'[", op_mark_start)
call setpos("']", op_mark_end)
endif
let cmd .= g:gitgutter_git_executable
" Call git-diff with the temporary files.
let cmd .= g:gitgutter_git_executable.' --no-pager'
if s:c_flag
let cmd .= ' -c "diff.autorefreshindex=0"'
let cmd .= ' -c "diff.noprefix=false"'
let cmd .= ' -c "core.safecrlf=false"'
endif
let cmd .= ' diff --no-ext-diff --no-color -U0 '.g:gitgutter_diff_args.' '
let cmd .= ' diff --no-ext-diff --no-color -U0 '.g:gitgutter_diff_args.' -- '.index_file.' '.buff_file
if a:realtime
let cmd .= ' -- '.blob_file.' '.buff_file
else
let cmd .= g:gitgutter_diff_base.' -- '.gitgutter#utility#shellescape(gitgutter#utility#filename())
" Pipe git-diff output into grep.
if !a:preserve_full_diff && !empty(g:gitgutter_grep)
let cmd .= ' | '.g:gitgutter_grep.' '.gitgutter#utility#shellescape('^@@ ')
endif
if !a:preserve_full_diff && s:grep_available
let cmd .= ' | '.s:grep_command.' '.gitgutter#utility#shellescape('^@@ ')
endif
if (!a:preserve_full_diff && s:grep_available) || a:realtime
" grep exits with 1 when no matches are found; diff exits with 1 when
" grep exits with 1 when no matches are found; git-diff exits with 1 when
" differences are found. However we want to treat non-matches and
" differences as non-erroneous behaviour; so we OR the command with one
" which always exits with success (0).
let cmd .= ' || exit 0'
endif
let cmd .= ')'
if !tracked
let cmd .= ')'
endif
let cmd = gitgutter#utility#cd_cmd(a:bufnr, cmd)
let cmd = gitgutter#utility#command_in_directory_of_file(cmd)
if g:gitgutter_async && gitgutter#async#available() && !a:preserve_full_diff
call gitgutter#async#execute(cmd)
if g:gitgutter_async && gitgutter#async#available()
call gitgutter#async#execute(cmd, a:bufnr, {
\ 'out': function('gitgutter#diff#handler'),
\ 'err': function('gitgutter#hunk#reset'),
\ })
return 'async'
else
let diff = gitgutter#utility#system(cmd)
if gitgutter#utility#shell_error()
" A shell error indicates the file is not tracked by git (unless something bizarre is going on).
throw 'diff failed'
if v:shell_error
call gitgutter#debug#log(diff)
throw 'gitgutter diff failed'
endif
return diff
endif
endfunction
function! gitgutter#diff#handler(bufnr, diff) abort
call gitgutter#debug#log(a:diff)
call gitgutter#hunk#set_hunks(a:bufnr, gitgutter#diff#parse_diff(a:diff))
let modified_lines = gitgutter#diff#process_hunks(a:bufnr, gitgutter#hunk#hunks(a:bufnr))
let signs_count = len(modified_lines)
if signs_count > g:gitgutter_max_signs
call gitgutter#utility#warn_once(a:bufnr, printf(
\ 'exceeded maximum number of signs (%d > %d, configured by g:gitgutter_max_signs).',
\ signs_count, g:gitgutter_max_signs), 'max_signs')
call gitgutter#sign#clear_signs(a:bufnr)
else
if g:gitgutter_signs || g:gitgutter_highlight_lines
call gitgutter#sign#update_signs(a:bufnr, modified_lines)
endif
endif
call s:save_last_seen_change(a:bufnr)
if exists('#User#GitGutter')
let g:gitgutter_hook_context = {'bufnr': a:bufnr}
execute 'doautocmd' s:nomodeline 'User GitGutter'
unlet g:gitgutter_hook_context
endif
endfunction
function! gitgutter#diff#parse_diff(diff) abort
let hunks = []
for line in split(a:diff, '\n')
@ -171,69 +195,71 @@ function! gitgutter#diff#parse_hunk(line) abort
end
endfunction
function! gitgutter#diff#process_hunks(hunks) abort
" This function is public so it may be used by other plugins
" e.g. vim-signature.
function! gitgutter#diff#process_hunks(bufnr, hunks) abort
let modified_lines = []
for hunk in a:hunks
call extend(modified_lines, gitgutter#diff#process_hunk(hunk))
call extend(modified_lines, s:process_hunk(a:bufnr, hunk))
endfor
return modified_lines
endfunction
" Returns [ [<line_number (number)>, <name (string)>], ...]
function! gitgutter#diff#process_hunk(hunk) abort
function! s:process_hunk(bufnr, hunk) abort
let modifications = []
let from_line = a:hunk[0]
let from_count = a:hunk[1]
let to_line = a:hunk[2]
let to_count = a:hunk[3]
if gitgutter#diff#is_added(from_count, to_count)
call gitgutter#diff#process_added(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_added(to_count)
if s:is_added(from_count, to_count)
call s:process_added(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_added(a:bufnr, to_count)
elseif gitgutter#diff#is_removed(from_count, to_count)
call gitgutter#diff#process_removed(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_removed(from_count)
elseif s:is_removed(from_count, to_count)
call s:process_removed(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_removed(a:bufnr, from_count)
elseif gitgutter#diff#is_modified(from_count, to_count)
call gitgutter#diff#process_modified(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_modified(to_count)
elseif s:is_modified(from_count, to_count)
call s:process_modified(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_modified(a:bufnr, to_count)
elseif gitgutter#diff#is_modified_and_added(from_count, to_count)
call gitgutter#diff#process_modified_and_added(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_added(to_count - from_count)
call gitgutter#hunk#increment_lines_modified(from_count)
elseif s:is_modified_and_added(from_count, to_count)
call s:process_modified_and_added(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_added(a:bufnr, to_count - from_count)
call gitgutter#hunk#increment_lines_modified(a:bufnr, from_count)
elseif gitgutter#diff#is_modified_and_removed(from_count, to_count)
call gitgutter#diff#process_modified_and_removed(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_modified(to_count)
call gitgutter#hunk#increment_lines_removed(from_count - to_count)
elseif s:is_modified_and_removed(from_count, to_count)
call s:process_modified_and_removed(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_modified(a:bufnr, to_count)
call gitgutter#hunk#increment_lines_removed(a:bufnr, from_count - to_count)
endif
return modifications
endfunction
function! gitgutter#diff#is_added(from_count, to_count) abort
function! s:is_added(from_count, to_count) abort
return a:from_count == 0 && a:to_count > 0
endfunction
function! gitgutter#diff#is_removed(from_count, to_count) abort
function! s:is_removed(from_count, to_count) abort
return a:from_count > 0 && a:to_count == 0
endfunction
function! gitgutter#diff#is_modified(from_count, to_count) abort
function! s:is_modified(from_count, to_count) abort
return a:from_count > 0 && a:to_count > 0 && a:from_count == a:to_count
endfunction
function! gitgutter#diff#is_modified_and_added(from_count, to_count) abort
function! s:is_modified_and_added(from_count, to_count) abort
return a:from_count > 0 && a:to_count > 0 && a:from_count < a:to_count
endfunction
function! gitgutter#diff#is_modified_and_removed(from_count, to_count) abort
function! s:is_modified_and_removed(from_count, to_count) abort
return a:from_count > 0 && a:to_count > 0 && a:from_count > a:to_count
endfunction
function! gitgutter#diff#process_added(modifications, from_count, to_count, to_line) abort
function! s:process_added(modifications, from_count, to_count, to_line) abort
let offset = 0
while offset < a:to_count
let line_number = a:to_line + offset
@ -242,7 +268,7 @@ function! gitgutter#diff#process_added(modifications, from_count, to_count, to_l
endwhile
endfunction
function! gitgutter#diff#process_removed(modifications, from_count, to_count, to_line) abort
function! s:process_removed(modifications, from_count, to_count, to_line) abort
if a:to_line == 0
call add(a:modifications, [1, 'removed_first_line'])
else
@ -250,7 +276,7 @@ function! gitgutter#diff#process_removed(modifications, from_count, to_count, to
endif
endfunction
function! gitgutter#diff#process_modified(modifications, from_count, to_count, to_line) abort
function! s:process_modified(modifications, from_count, to_count, to_line) abort
let offset = 0
while offset < a:to_count
let line_number = a:to_line + offset
@ -259,7 +285,7 @@ function! gitgutter#diff#process_modified(modifications, from_count, to_count, t
endwhile
endfunction
function! gitgutter#diff#process_modified_and_added(modifications, from_count, to_count, to_line) abort
function! s:process_modified_and_added(modifications, from_count, to_count, to_line) abort
let offset = 0
while offset < a:from_count
let line_number = a:to_line + offset
@ -273,7 +299,7 @@ function! gitgutter#diff#process_modified_and_added(modifications, from_count, t
endwhile
endfunction
function! gitgutter#diff#process_modified_and_removed(modifications, from_count, to_count, to_line) abort
function! s:process_modified_and_removed(modifications, from_count, to_count, to_line) abort
let offset = 0
while offset < a:to_count
let line_number = a:to_line + offset
@ -283,28 +309,14 @@ function! gitgutter#diff#process_modified_and_removed(modifications, from_count,
let a:modifications[-1] = [a:to_line + offset - 1, 'modified_removed']
endfunction
" Generates a zero-context diff for the current hunk.
"
" diff - the full diff for the buffer
" type - stage | undo | preview
function! gitgutter#diff#generate_diff_for_hunk(diff, type) abort
let diff_for_hunk = gitgutter#diff#discard_hunks(a:diff, a:type == 'stage' || a:type == 'undo')
if a:type == 'stage' || a:type == 'undo'
let diff_for_hunk = gitgutter#diff#adjust_hunk_summary(diff_for_hunk, a:type == 'stage')
endif
return diff_for_hunk
endfunction
" Returns the diff with all hunks discarded except the current.
"
" diff - the diff to process
" keep_header - truthy to keep the diff header and hunk summary, falsy to discard it
function! gitgutter#diff#discard_hunks(diff, keep_header) abort
" Returns a diff for the current hunk.
function! gitgutter#diff#hunk_diff(bufnr, full_diff)
let modified_diff = []
let keep_line = a:keep_header
for line in split(a:diff, '\n')
let keep_line = 1
" Don't keepempty when splitting because the diff we want may not be the
" final one. Instead add trailing NL at end of function.
for line in split(a:full_diff, '\n')
let hunk_info = gitgutter#diff#parse_hunk(line)
if len(hunk_info) == 4 " start of new hunk
let keep_line = gitgutter#hunk#cursor_in_hunk(hunk_info)
@ -313,36 +325,32 @@ function! gitgutter#diff#discard_hunks(diff, keep_header) abort
call add(modified_diff, line)
endif
endfor
if a:keep_header
return gitgutter#utility#stringify(modified_diff)
else
" Discard hunk summary too.
return gitgutter#utility#stringify(modified_diff[1:])
endif
return join(modified_diff, "\n")."\n"
endfunction
" Adjust hunk summary (from's / to's line number) to ignore changes above/before this one.
"
" diff_for_hunk - a diff containing only the hunk of interest
" staging - truthy if the hunk is to be staged, falsy if it is to be undone
"
" TODO: push this down to #discard_hunks?
function! gitgutter#diff#adjust_hunk_summary(diff_for_hunk, staging) abort
let line_adjustment = gitgutter#hunk#line_adjustment_for_current_hunk()
let adj_diff = []
for line in split(a:diff_for_hunk, '\n')
if match(line, s:hunk_re) != -1
if a:staging
" increment 'to' line number
let line = substitute(line, '+\@<=\(\d\+\)', '\=submatch(1)+line_adjustment', '')
else
" decrement 'from' line number
let line = substitute(line, '-\@<=\(\d\+\)', '\=submatch(1)-line_adjustment', '')
function! s:write_buffer(bufnr, file)
let bufcontents = getbufline(a:bufnr, 1, '$')
if getbufvar(a:bufnr, '&fileformat') ==# 'dos'
call map(bufcontents, 'v:val."\r"')
endif
let fenc = getbufvar(a:bufnr, '&fileencoding')
if fenc !=# &encoding
call map(bufcontents, 'iconv(v:val, &encoding, "'.fenc.'")')
endif
call add(adj_diff, line)
endfor
return gitgutter#utility#stringify(adj_diff)
if getbufvar(a:bufnr, '&bomb')
let bufcontents[0]=''.bufcontents[0]
endif
call writefile(bufcontents, a:file)
endfunction
function! s:save_last_seen_change(bufnr) abort
call gitgutter#utility#setbufvar(a:bufnr, 'tick', getbufvar(a:bufnr, 'changedtick'))
endfunction

View file

@ -1,3 +1,37 @@
function! gitgutter#highlight#line_disable() abort
let g:gitgutter_highlight_lines = 0
call s:define_sign_line_highlights()
if !g:gitgutter_signs
call gitgutter#sign#clear_signs(bufnr(''))
call gitgutter#sign#remove_dummy_sign(bufnr(''), 0)
endif
redraw!
endfunction
function! gitgutter#highlight#line_enable() abort
let old_highlight_lines = g:gitgutter_highlight_lines
let g:gitgutter_highlight_lines = 1
call s:define_sign_line_highlights()
if !old_highlight_lines && !g:gitgutter_signs
call gitgutter#all(1)
endif
redraw!
endfunction
function! gitgutter#highlight#line_toggle() abort
if g:gitgutter_highlight_lines
call gitgutter#highlight#line_disable()
else
call gitgutter#highlight#line_enable()
endif
endfunction
function! gitgutter#highlight#define_sign_column_highlight() abort
if g:gitgutter_override_sign_column_highlight
highlight! link SignColumn LineNr
@ -7,7 +41,7 @@ function! gitgutter#highlight#define_sign_column_highlight() abort
endfunction
function! gitgutter#highlight#define_highlights() abort
let [guibg, ctermbg] = gitgutter#highlight#get_background_colors('SignColumn')
let [guibg, ctermbg] = s:get_background_colors('SignColumn')
" Highlights used by the signs.
@ -42,12 +76,12 @@ function! gitgutter#highlight#define_signs() abort
sign define GitGutterLineModifiedRemoved
sign define GitGutterDummy
call gitgutter#highlight#define_sign_text()
call s:define_sign_text()
call gitgutter#highlight#define_sign_text_highlights()
call gitgutter#highlight#define_sign_line_highlights()
call s:define_sign_line_highlights()
endfunction
function! gitgutter#highlight#define_sign_text() abort
function! s:define_sign_text() abort
execute "sign define GitGutterLineAdded text=" . g:gitgutter_sign_added
execute "sign define GitGutterLineModified text=" . g:gitgutter_sign_modified
execute "sign define GitGutterLineRemoved text=" . g:gitgutter_sign_removed
@ -75,7 +109,7 @@ function! gitgutter#highlight#define_sign_text_highlights() abort
endif
endfunction
function! gitgutter#highlight#define_sign_line_highlights() abort
function! s:define_sign_line_highlights() abort
if g:gitgutter_highlight_lines
sign define GitGutterLineAdded linehl=GitGutterAddLine
sign define GitGutterLineModified linehl=GitGutterChangeLine
@ -91,22 +125,22 @@ function! gitgutter#highlight#define_sign_line_highlights() abort
endif
endfunction
function! gitgutter#highlight#get_background_colors(group) abort
function! s:get_background_colors(group) abort
redir => highlight
silent execute 'silent highlight ' . a:group
redir END
let link_matches = matchlist(highlight, 'links to \(\S\+\)')
if len(link_matches) > 0 " follow the link
return gitgutter#highlight#get_background_colors(link_matches[1])
return s:get_background_colors(link_matches[1])
endif
let ctermbg = gitgutter#highlight#match_highlight(highlight, 'ctermbg=\([0-9A-Za-z]\+\)')
let guibg = gitgutter#highlight#match_highlight(highlight, 'guibg=\([#0-9A-Za-z]\+\)')
let ctermbg = s:match_highlight(highlight, 'ctermbg=\([0-9A-Za-z]\+\)')
let guibg = s:match_highlight(highlight, 'guibg=\([#0-9A-Za-z]\+\)')
return [guibg, ctermbg]
endfunction
function! gitgutter#highlight#match_highlight(highlight, pattern) abort
function! s:match_highlight(highlight, pattern) abort
let matches = matchlist(a:highlight, a:pattern)
if len(matches) == 0
return 'NONE'

View file

@ -1,15 +1,15 @@
function! gitgutter#hunk#set_hunks(hunks) abort
call gitgutter#utility#setbufvar(gitgutter#utility#bufnr(), 'hunks', a:hunks)
call s:reset_summary()
function! gitgutter#hunk#set_hunks(bufnr, hunks) abort
call gitgutter#utility#setbufvar(a:bufnr, 'hunks', a:hunks)
call s:reset_summary(a:bufnr)
endfunction
function! gitgutter#hunk#hunks() abort
return gitgutter#utility#getbufvar(gitgutter#utility#bufnr(), 'hunks', [])
function! gitgutter#hunk#hunks(bufnr) abort
return gitgutter#utility#getbufvar(a:bufnr, 'hunks', [])
endfunction
function! gitgutter#hunk#reset() abort
call gitgutter#utility#setbufvar(gitgutter#utility#bufnr(), 'hunks', [])
call s:reset_summary()
function! gitgutter#hunk#reset(bufnr) abort
call gitgutter#utility#setbufvar(a:bufnr, 'hunks', [])
call s:reset_summary(a:bufnr)
endfunction
@ -17,37 +17,35 @@ function! gitgutter#hunk#summary(bufnr) abort
return gitgutter#utility#getbufvar(a:bufnr, 'summary', [0,0,0])
endfunction
function! s:reset_summary() abort
call gitgutter#utility#setbufvar(gitgutter#utility#bufnr(), 'summary', [0,0,0])
function! s:reset_summary(bufnr) abort
call gitgutter#utility#setbufvar(a:bufnr, 'summary', [0,0,0])
endfunction
function! gitgutter#hunk#increment_lines_added(count) abort
let bufnr = gitgutter#utility#bufnr()
let summary = gitgutter#hunk#summary(bufnr)
function! gitgutter#hunk#increment_lines_added(bufnr, count) abort
let summary = gitgutter#hunk#summary(a:bufnr)
let summary[0] += a:count
call gitgutter#utility#setbufvar(bufnr, 'summary', summary)
call gitgutter#utility#setbufvar(a:bufnr, 'summary', summary)
endfunction
function! gitgutter#hunk#increment_lines_modified(count) abort
let bufnr = gitgutter#utility#bufnr()
let summary = gitgutter#hunk#summary(bufnr)
function! gitgutter#hunk#increment_lines_modified(bufnr, count) abort
let summary = gitgutter#hunk#summary(a:bufnr)
let summary[1] += a:count
call gitgutter#utility#setbufvar(bufnr, 'summary', summary)
call gitgutter#utility#setbufvar(a:bufnr, 'summary', summary)
endfunction
function! gitgutter#hunk#increment_lines_removed(count) abort
let bufnr = gitgutter#utility#bufnr()
let summary = gitgutter#hunk#summary(bufnr)
function! gitgutter#hunk#increment_lines_removed(bufnr, count) abort
let summary = gitgutter#hunk#summary(a:bufnr)
let summary[2] += a:count
call gitgutter#utility#setbufvar(bufnr, 'summary', summary)
call gitgutter#utility#setbufvar(a:bufnr, 'summary', summary)
endfunction
function! gitgutter#hunk#next_hunk(count) abort
if gitgutter#utility#is_active()
let bufnr = bufnr('')
if gitgutter#utility#is_active(bufnr)
let current_line = line('.')
let hunk_count = 0
for hunk in gitgutter#hunk#hunks()
for hunk in gitgutter#hunk#hunks(bufnr)
if hunk[2] > current_line
let hunk_count += 1
if hunk_count == a:count
@ -61,10 +59,11 @@ function! gitgutter#hunk#next_hunk(count) abort
endfunction
function! gitgutter#hunk#prev_hunk(count) abort
if gitgutter#utility#is_active()
let bufnr = bufnr('')
if gitgutter#utility#is_active(bufnr)
let current_line = line('.')
let hunk_count = 0
for hunk in reverse(copy(gitgutter#hunk#hunks()))
for hunk in reverse(copy(gitgutter#hunk#hunks(bufnr)))
if hunk[2] < current_line
let hunk_count += 1
if hunk_count == a:count
@ -80,10 +79,11 @@ endfunction
" Returns the hunk the cursor is currently in or an empty list if the cursor
" isn't in a hunk.
function! gitgutter#hunk#current_hunk() abort
function! s:current_hunk() abort
let bufnr = bufnr('')
let current_hunk = []
for hunk in gitgutter#hunk#hunks()
for hunk in gitgutter#hunk#hunks(bufnr)
if gitgutter#hunk#cursor_in_hunk(hunk)
let current_hunk = hunk
break
@ -107,22 +107,8 @@ function! gitgutter#hunk#cursor_in_hunk(hunk) abort
return 0
endfunction
" Returns the number of lines the current hunk is offset from where it would
" be if any changes above it in the file didn't exist.
function! gitgutter#hunk#line_adjustment_for_current_hunk() abort
let adj = 0
for hunk in gitgutter#hunk#hunks()
if gitgutter#hunk#cursor_in_hunk(hunk)
break
else
let adj += hunk[1] - hunk[3]
endif
endfor
return adj
endfunction
function! gitgutter#hunk#text_object(inner) abort
let hunk = gitgutter#hunk#current_hunk()
let hunk = s:current_hunk()
if empty(hunk)
return
@ -141,3 +127,155 @@ function! gitgutter#hunk#text_object(inner) abort
execute 'normal! 'first_line.'GV'.last_line.'G'
endfunction
function! gitgutter#hunk#stage() abort
call s:hunk_op(function('s:stage'))
silent! call repeat#set("\<Plug>GitGutterStageHunk", -1)<CR>
endfunction
function! gitgutter#hunk#undo() abort
call s:hunk_op(function('s:undo'))
silent! call repeat#set("\<Plug>GitGutterUndoHunk", -1)<CR>
endfunction
function! gitgutter#hunk#preview() abort
call s:hunk_op(function('s:preview'))
silent! call repeat#set("\<Plug>GitGutterPreviewHunk", -1)<CR>
endfunction
function! s:hunk_op(op)
let bufnr = bufnr('')
if gitgutter#utility#is_active(bufnr)
" Get a (synchronous) diff.
let [async, g:gitgutter_async] = [g:gitgutter_async, 0]
let diff = gitgutter#diff#run_diff(bufnr, 1)
let g:gitgutter_async = async
call gitgutter#hunk#set_hunks(bufnr, gitgutter#diff#parse_diff(diff))
if empty(s:current_hunk())
call gitgutter#utility#warn('cursor is not in a hunk')
else
call a:op(gitgutter#diff#hunk_diff(bufnr, diff))
endif
endif
endfunction
function! s:stage(hunk_diff)
let bufnr = bufnr('')
let diff = s:adjust_header(bufnr, a:hunk_diff)
" Apply patch to index.
call gitgutter#utility#system(
\ gitgutter#utility#cd_cmd(bufnr, g:gitgutter_git_executable.' apply --cached --unidiff-zero - '),
\ diff)
" Refresh gitgutter's view of buffer.
call gitgutter#process_buffer(bufnr, 1)
endfunction
function! s:undo(hunk_diff)
" Apply reverse patch to buffer.
let hunk = gitgutter#diff#parse_hunk(split(a:hunk_diff, '\n')[4])
let lines = map(split(a:hunk_diff, '\n')[5:], 'v:val[1:]')
let lnum = hunk[2]
let added_only = hunk[1] == 0 && hunk[3] > 0
let removed_only = hunk[1] > 0 && hunk[3] == 0
if removed_only
call append(lnum, lines)
elseif added_only
execute lnum .','. (lnum+len(lines)-1) .'d'
else
call append(lnum-1, lines[0:hunk[1]])
execute (lnum+hunk[1]) .','. (lnum+hunk[1]+hunk[3]) .'d'
endif
endfunction
function! s:preview(hunk_diff)
let hunk_lines = split(s:discard_header(a:hunk_diff), "\n")
let hunk_lines_length = len(hunk_lines)
let previewheight = min([hunk_lines_length, &previewheight])
silent! wincmd P
if !&previewwindow
noautocmd execute 'bo' previewheight 'new'
set previewwindow
else
execute 'resize' previewheight
endif
setlocal noreadonly modifiable filetype=diff buftype=nofile bufhidden=delete noswapfile
execute "%delete_"
call append(0, hunk_lines)
normal! gg
setlocal readonly nomodifiable
noautocmd wincmd p
endfunction
function! s:adjust_header(bufnr, hunk_diff)
let filepath = gitgutter#utility#repo_path(a:bufnr, 0)
return s:adjust_hunk_summary(s:fix_file_references(filepath, a:hunk_diff))
endfunction
" Replaces references to temp files with the actual file.
function! s:fix_file_references(filepath, hunk_diff)
let lines = split(a:hunk_diff, '\n')
let left_prefix = matchstr(lines[2], '[abciow12]').'/'
let right_prefix = matchstr(lines[3], '[abciow12]').'/'
let quote = lines[0][11] == '"' ? '"' : ''
let left_file = quote.left_prefix.a:filepath.quote
let right_file = quote.right_prefix.a:filepath.quote
let lines[0] = 'diff --git '.left_file.' '.right_file
let lines[2] = '--- '.left_file
let lines[3] = '+++ '.right_file
return join(lines, "\n")."\n"
endfunction
if $VIM_GITGUTTER_TEST
function! gitgutter#hunk#fix_file_references(filepath, hunk_diff)
return s:fix_file_references(a:filepath, a:hunk_diff)
endfunction
endif
function! s:adjust_hunk_summary(hunk_diff) abort
let line_adjustment = s:line_adjustment_for_current_hunk()
let diff = split(a:hunk_diff, '\n', 1)
let diff[4] = substitute(diff[4], '+\@<=\(\d\+\)', '\=submatch(1)+line_adjustment', '')
return join(diff, "\n")
endfunction
function! s:discard_header(hunk_diff)
return join(split(a:hunk_diff, '\n', 1)[5:], "\n")
endfunction
" Returns the number of lines the current hunk is offset from where it would
" be if any changes above it in the file didn't exist.
function! s:line_adjustment_for_current_hunk() abort
let bufnr = bufnr('')
let adj = 0
for hunk in gitgutter#hunk#hunks(bufnr)
if gitgutter#hunk#cursor_in_hunk(hunk)
break
else
let adj += hunk[1] - hunk[3]
endif
endfor
return adj
endfunction

View file

@ -10,14 +10,43 @@ let s:dummy_sign_id = s:first_sign_id - 1
let s:supports_star = v:version > 703 || (v:version == 703 && has("patch596"))
" Removes gitgutter's signs (excluding dummy sign) from the buffer being processed.
function! gitgutter#sign#clear_signs() abort
let bufnr = gitgutter#utility#bufnr()
call gitgutter#sign#find_current_signs()
function! gitgutter#sign#enable() abort
let old_signs = g:gitgutter_signs
let sign_ids = map(values(gitgutter#utility#getbufvar(bufnr, 'gitgutter_signs')), 'v:val.id')
call gitgutter#sign#remove_signs(sign_ids, 1)
call gitgutter#utility#setbufvar(bufnr, 'gitgutter_signs', {})
let g:gitgutter_signs = 1
call gitgutter#highlight#define_sign_text_highlights()
if !old_signs && !g:gitgutter_highlight_lines
call gitgutter#all(1)
endif
endfunction
function! gitgutter#sign#disable() abort
let g:gitgutter_signs = 0
call gitgutter#highlight#define_sign_text_highlights()
if !g:gitgutter_highlight_lines
call gitgutter#sign#clear_signs(bufnr(''))
call gitgutter#sign#remove_dummy_sign(bufnr(''), 0)
endif
endfunction
function! gitgutter#sign#toggle() abort
if g:gitgutter_signs
call gitgutter#sign#disable()
else
call gitgutter#sign#enable()
endif
endfunction
" Removes gitgutter's signs (excluding dummy sign) from the buffer being processed.
function! gitgutter#sign#clear_signs(bufnr) abort
call s:find_current_signs(a:bufnr)
let sign_ids = map(values(gitgutter#utility#getbufvar(a:bufnr, 'gitgutter_signs')), 'v:val.id')
call s:remove_signs(a:bufnr, sign_ids, 1)
call gitgutter#utility#setbufvar(a:bufnr, 'gitgutter_signs', {})
endfunction
@ -25,39 +54,37 @@ endfunction
"
" modified_lines: list of [<line_number (number)>, <name (string)>]
" where name = 'added|removed|modified|modified_removed'
function! gitgutter#sign#update_signs(modified_lines) abort
call gitgutter#sign#find_current_signs()
function! gitgutter#sign#update_signs(bufnr, modified_lines) abort
call s:find_current_signs(a:bufnr)
let new_gitgutter_signs_line_numbers = map(copy(a:modified_lines), 'v:val[0]')
let obsolete_signs = gitgutter#sign#obsolete_gitgutter_signs_to_remove(new_gitgutter_signs_line_numbers)
let obsolete_signs = s:obsolete_gitgutter_signs_to_remove(a:bufnr, new_gitgutter_signs_line_numbers)
let flicker_possible = s:remove_all_old_signs && !empty(a:modified_lines)
if flicker_possible
call gitgutter#sign#add_dummy_sign()
call s:add_dummy_sign(a:bufnr)
endif
call gitgutter#sign#remove_signs(obsolete_signs, s:remove_all_old_signs)
call gitgutter#sign#upsert_new_gitgutter_signs(a:modified_lines)
call s:remove_signs(a:bufnr, obsolete_signs, s:remove_all_old_signs)
call s:upsert_new_gitgutter_signs(a:bufnr, a:modified_lines)
if flicker_possible
call gitgutter#sign#remove_dummy_sign(0)
call gitgutter#sign#remove_dummy_sign(a:bufnr, 0)
endif
endfunction
function! gitgutter#sign#add_dummy_sign() abort
let bufnr = gitgutter#utility#bufnr()
if !gitgutter#utility#getbufvar(bufnr, 'dummy_sign')
execute "sign place" s:dummy_sign_id "line=" . 9999 "name=GitGutterDummy buffer=" . bufnr
call gitgutter#utility#setbufvar(bufnr, 'dummy_sign', 1)
function! s:add_dummy_sign(bufnr) abort
if !gitgutter#utility#getbufvar(a:bufnr, 'dummy_sign')
execute "sign place" s:dummy_sign_id "line=" . 9999 "name=GitGutterDummy buffer=" . a:bufnr
call gitgutter#utility#setbufvar(a:bufnr, 'dummy_sign', 1)
endif
endfunction
function! gitgutter#sign#remove_dummy_sign(force) abort
let bufnr = gitgutter#utility#bufnr()
if gitgutter#utility#getbufvar(bufnr, 'dummy_sign') && (a:force || !g:gitgutter_sign_column_always)
execute "sign unplace" s:dummy_sign_id "buffer=" . bufnr
call gitgutter#utility#setbufvar(bufnr, 'dummy_sign', 0)
function! gitgutter#sign#remove_dummy_sign(bufnr, force) abort
if gitgutter#utility#getbufvar(a:bufnr, 'dummy_sign') && (a:force || !g:gitgutter_sign_column_always)
execute "sign unplace" s:dummy_sign_id "buffer=" . a:bufnr
call gitgutter#utility#setbufvar(a:bufnr, 'dummy_sign', 0)
endif
endfunction
@ -67,14 +94,13 @@ endfunction
"
function! gitgutter#sign#find_current_signs() abort
let bufnr = gitgutter#utility#bufnr()
function! s:find_current_signs(bufnr) abort
let gitgutter_signs = {} " <line_number (string)>: {'id': <id (number)>, 'name': <name (string)>}
let other_signs = [] " [<line_number (number),...]
let dummy_sign_placed = 0
redir => signs
silent execute "sign place buffer=" . bufnr
silent execute "sign place buffer=" . a:bufnr
redir END
for sign_line in filter(split(signs, '\n')[2:], 'v:val =~# "="')
@ -101,19 +127,18 @@ function! gitgutter#sign#find_current_signs() abort
end
endfor
call gitgutter#utility#setbufvar(bufnr, 'dummy_sign', dummy_sign_placed)
call gitgutter#utility#setbufvar(bufnr, 'gitgutter_signs', gitgutter_signs)
call gitgutter#utility#setbufvar(bufnr, 'other_signs', other_signs)
call gitgutter#utility#setbufvar(a:bufnr, 'dummy_sign', dummy_sign_placed)
call gitgutter#utility#setbufvar(a:bufnr, 'gitgutter_signs', gitgutter_signs)
call gitgutter#utility#setbufvar(a:bufnr, 'other_signs', other_signs)
endfunction
" Returns a list of [<id (number)>, ...]
" Sets `s:remove_all_old_signs` as a side-effect.
function! gitgutter#sign#obsolete_gitgutter_signs_to_remove(new_gitgutter_signs_line_numbers) abort
let bufnr = gitgutter#utility#bufnr()
function! s:obsolete_gitgutter_signs_to_remove(bufnr, new_gitgutter_signs_line_numbers) abort
let signs_to_remove = [] " list of [<id (number)>, ...]
let remove_all_signs = 1
let old_gitgutter_signs = gitgutter#utility#getbufvar(bufnr, 'gitgutter_signs')
let old_gitgutter_signs = gitgutter#utility#getbufvar(a:bufnr, 'gitgutter_signs')
for line_number in keys(old_gitgutter_signs)
if index(a:new_gitgutter_signs_line_numbers, str2nr(line_number)) == -1
call add(signs_to_remove, old_gitgutter_signs[line_number].id)
@ -126,13 +151,12 @@ function! gitgutter#sign#obsolete_gitgutter_signs_to_remove(new_gitgutter_signs_
endfunction
function! gitgutter#sign#remove_signs(sign_ids, all_signs) abort
let bufnr = gitgutter#utility#bufnr()
if a:all_signs && s:supports_star && empty(gitgutter#utility#getbufvar(bufnr, 'other_signs'))
let dummy_sign_present = gitgutter#utility#getbufvar(bufnr, 'dummy_sign')
execute "sign unplace * buffer=" . bufnr
function! s:remove_signs(bufnr, sign_ids, all_signs) abort
if a:all_signs && s:supports_star && empty(gitgutter#utility#getbufvar(a:bufnr, 'other_signs'))
let dummy_sign_present = gitgutter#utility#getbufvar(a:bufnr, 'dummy_sign')
execute "sign unplace * buffer=" . a:bufnr
if dummy_sign_present
execute "sign place" s:dummy_sign_id "line=" . 9999 "name=GitGutterDummy buffer=" . bufnr
execute "sign place" s:dummy_sign_id "line=" . 9999 "name=GitGutterDummy buffer=" . a:bufnr
endif
else
for id in a:sign_ids
@ -142,22 +166,21 @@ function! gitgutter#sign#remove_signs(sign_ids, all_signs) abort
endfunction
function! gitgutter#sign#upsert_new_gitgutter_signs(modified_lines) abort
let bufnr = gitgutter#utility#bufnr()
let other_signs = gitgutter#utility#getbufvar(bufnr, 'other_signs')
let old_gitgutter_signs = gitgutter#utility#getbufvar(bufnr, 'gitgutter_signs')
function! s:upsert_new_gitgutter_signs(bufnr, modified_lines) abort
let other_signs = gitgutter#utility#getbufvar(a:bufnr, 'other_signs')
let old_gitgutter_signs = gitgutter#utility#getbufvar(a:bufnr, 'gitgutter_signs')
for line in a:modified_lines
let line_number = line[0] " <number>
if index(other_signs, line_number) == -1 " don't clobber others' signs
let name = gitgutter#utility#highlight_name_for_change(line[1])
let name = s:highlight_name_for_change(line[1])
if !has_key(old_gitgutter_signs, line_number) " insert
let id = gitgutter#sign#next_sign_id()
execute "sign place" id "line=" . line_number "name=" . name "buffer=" . bufnr
let id = s:next_sign_id()
execute "sign place" id "line=" . line_number "name=" . name "buffer=" . a:bufnr
else " update if sign has changed
let old_sign = old_gitgutter_signs[line_number]
if old_sign.name !=# name
execute "sign place" old_sign.id "name=" . name "buffer=" . bufnr
execute "sign place" old_sign.id "name=" . name "buffer=" . a:bufnr
end
endif
endif
@ -166,7 +189,7 @@ function! gitgutter#sign#upsert_new_gitgutter_signs(modified_lines) abort
endfunction
function! gitgutter#sign#next_sign_id() abort
function! s:next_sign_id() abort
let next_id = s:next_sign_id
let s:next_sign_id += 1
return next_id
@ -177,3 +200,20 @@ endfunction
function! gitgutter#sign#reset()
let s:next_sign_id = s:first_sign_id
endfunction
function! s:highlight_name_for_change(text) abort
if a:text ==# 'added'
return 'GitGutterLineAdded'
elseif a:text ==# 'removed'
return 'GitGutterLineRemoved'
elseif a:text ==# 'removed_first_line'
return 'GitGutterLineRemovedFirstLine'
elseif a:text ==# 'modified'
return 'GitGutterLineModified'
elseif a:text ==# 'modified_removed'
return 'GitGutterLineModifiedRemoved'
endif
endfunction

View file

@ -1,11 +1,18 @@
let s:file = ''
let s:using_xolox_shell = -1
let s:exit_code = 0
function! gitgutter#utility#supports_overscore_sign()
if s:windows()
return &encoding ==? 'utf-8'
else
return &termencoding ==? &encoding || &termencoding == ''
endif
endfunction
function! gitgutter#utility#setbufvar(buffer, varname, val)
let dict = get(getbufvar(a:buffer, ''), 'gitgutter', {})
let needs_setting = empty(dict)
let dict[a:varname] = a:val
if needs_setting
call setbufvar(a:buffer, 'gitgutter', dict)
endif
endfunction
function! gitgutter#utility#getbufvar(buffer, varname, ...)
@ -26,11 +33,11 @@ function! gitgutter#utility#warn(message) abort
let v:warningmsg = a:message
endfunction
function! gitgutter#utility#warn_once(message, key) abort
if empty(gitgutter#utility#getbufvar(s:bufnr, a:key))
call gitgutter#utility#setbufvar(s:bufnr, a:key, '1')
function! gitgutter#utility#warn_once(bufnr, message, key) abort
if empty(gitgutter#utility#getbufvar(a:bufnr, a:key))
call gitgutter#utility#setbufvar(a:bufnr, a:key, '1')
echohl WarningMsg
redraw | echo 'vim-gitgutter: ' . a:message
redraw | echom 'vim-gitgutter: ' . a:message
echohl None
let v:warningmsg = a:message
endif
@ -38,188 +45,166 @@ endfunction
" Returns truthy when the buffer's file should be processed; and falsey when it shouldn't.
" This function does not and should not make any system calls.
function! gitgutter#utility#is_active() abort
function! gitgutter#utility#is_active(bufnr) abort
return g:gitgutter_enabled &&
\ !pumvisible() &&
\ gitgutter#utility#is_file_buffer() &&
\ gitgutter#utility#exists_file() &&
\ gitgutter#utility#not_git_dir()
\ s:is_file_buffer(a:bufnr) &&
\ s:exists_file(a:bufnr) &&
\ s:not_git_dir(a:bufnr)
endfunction
function! gitgutter#utility#not_git_dir() abort
return gitgutter#utility#full_path_to_directory_of_file() !~ '[/\\]\.git\($\|[/\\]\)'
function! s:not_git_dir(bufnr) abort
return s:dir(a:bufnr) !~ '[/\\]\.git\($\|[/\\]\)'
endfunction
function! gitgutter#utility#is_file_buffer() abort
return empty(getbufvar(s:bufnr, '&buftype'))
function! s:is_file_buffer(bufnr) abort
return empty(getbufvar(a:bufnr, '&buftype'))
endfunction
" A replacement for the built-in `shellescape(arg)`.
"
" Recent versions of Vim handle shell escaping pretty well. However older
" versions aren't as good. This attempts to do the right thing.
"
" See:
" https://github.com/tpope/vim-fugitive/blob/8f0b8edfbd246c0026b7a2388e1d883d579ac7f6/plugin/fugitive.vim#L29-L37
" From tpope/vim-fugitive
function! s:winshell()
return &shell =~? 'cmd' || exists('+shellslash') && !&shellslash
endfunction
" From tpope/vim-fugitive
function! gitgutter#utility#shellescape(arg) abort
if a:arg =~ '^[A-Za-z0-9_/.-]\+$'
return a:arg
elseif &shell =~# 'cmd' || gitgutter#utility#using_xolox_shell()
elseif s:winshell()
return '"' . substitute(substitute(a:arg, '"', '""', 'g'), '%', '"%"', 'g') . '"'
else
return shellescape(a:arg)
endif
endfunction
function! gitgutter#utility#set_buffer(bufnr) abort
let s:bufnr = a:bufnr
let s:file = resolve(bufname(a:bufnr))
function! gitgutter#utility#file(bufnr)
return s:abs_path(a:bufnr, 1)
endfunction
function! gitgutter#utility#bufnr()
return s:bufnr
endfunction
function! gitgutter#utility#file()
return s:file
endfunction
function! gitgutter#utility#filename() abort
return fnamemodify(s:file, ':t')
endfunction
function! gitgutter#utility#extension() abort
return fnamemodify(s:file, ':e')
endfunction
function! gitgutter#utility#full_path_to_directory_of_file() abort
return fnamemodify(s:file, ':p:h')
endfunction
function! gitgutter#utility#directory_of_file() abort
return fnamemodify(s:file, ':h')
endfunction
function! gitgutter#utility#exists_file() abort
return filereadable(s:file)
endfunction
function! gitgutter#utility#has_unsaved_changes() abort
return getbufvar(s:bufnr, "&mod")
endfunction
function! gitgutter#utility#has_fresh_changes() abort
return getbufvar(s:bufnr, 'changedtick') != gitgutter#utility#getbufvar(s:bufnr, 'last_tick')
endfunction
function! gitgutter#utility#save_last_seen_change() abort
call gitgutter#utility#setbufvar(s:bufnr, 'last_tick', getbufvar(s:bufnr, 'changedtick'))
endfunction
function! gitgutter#utility#shell_error() abort
return gitgutter#utility#using_xolox_shell() ? s:exit_code : v:shell_error
endfunction
function! gitgutter#utility#using_xolox_shell() abort
if s:using_xolox_shell == -1
if !g:gitgutter_avoid_cmd_prompt_on_windows
let s:using_xolox_shell = 0
" Although xolox/vim-shell works on both windows and unix we only want to use
" it on windows.
elseif has('win32') || has('win64') || has('win32unix')
let s:using_xolox_shell = exists('g:xolox#misc#version') && exists('g:xolox#shell#version')
else
let s:using_xolox_shell = 0
endif
endif
return s:using_xolox_shell
" Not shellescaped
function! gitgutter#utility#extension(bufnr) abort
return fnamemodify(s:abs_path(a:bufnr, 0), ':e')
endfunction
function! gitgutter#utility#system(cmd, ...) abort
call gitgutter#debug#log(a:cmd, a:000)
if gitgutter#utility#using_xolox_shell()
let options = {'command': a:cmd, 'check': 0}
if a:0 > 0
let options['stdin'] = a:1
endif
let ret = xolox#misc#os#exec(options)
let output = join(ret.stdout, "\n")
let s:exit_code = ret.exit_code
else
call s:use_known_shell()
silent let output = (a:0 == 0) ? system(a:cmd) : system(a:cmd, a:1)
endif
call s:restore_shell()
return output
endfunction
function! gitgutter#utility#file_relative_to_repo_root() abort
let file_path_relative_to_repo_root = gitgutter#utility#getbufvar(s:bufnr, 'repo_relative_path')
if empty(file_path_relative_to_repo_root)
let dir_path_relative_to_repo_root = gitgutter#utility#system(gitgutter#utility#command_in_directory_of_file(g:gitgutter_git_executable.' rev-parse --show-prefix'))
let dir_path_relative_to_repo_root = gitgutter#utility#strip_trailing_new_line(dir_path_relative_to_repo_root)
let file_path_relative_to_repo_root = dir_path_relative_to_repo_root . gitgutter#utility#filename()
call gitgutter#utility#setbufvar(s:bufnr, 'repo_relative_path', file_path_relative_to_repo_root)
" Path of file relative to repo root.
"
" * empty string - not set
" * non-empty string - path
" * -1 - pending
" * -2 - not tracked by git
function! gitgutter#utility#repo_path(bufnr, shellesc) abort
let p = gitgutter#utility#getbufvar(a:bufnr, 'path')
return a:shellesc ? gitgutter#utility#shellescape(p) : p
endfunction
function! gitgutter#utility#set_repo_path(bufnr) abort
" Values of path:
" * non-empty string - path
" * -1 - pending
" * -2 - not tracked by git
call gitgutter#utility#setbufvar(a:bufnr, 'path', -1)
let cmd = gitgutter#utility#cd_cmd(a:bufnr, g:gitgutter_git_executable.' ls-files --error-unmatch --full-name '.gitgutter#utility#shellescape(s:filename(a:bufnr)))
if g:gitgutter_async && gitgutter#async#available()
if has('lambda')
call gitgutter#async#execute(cmd, a:bufnr, {
\ 'out': {bufnr, path -> gitgutter#utility#setbufvar(bufnr, 'path', s:strip_trailing_new_line(path))},
\ 'err': {bufnr -> gitgutter#utility#setbufvar(bufnr, 'path', -2)},
\ })
else
if has('nvim') && !has('nvim-0.2.0')
call gitgutter#async#execute(cmd, a:bufnr, {
\ 'out': function('s:set_path'),
\ 'err': function('s:not_tracked_by_git')
\ })
else
call gitgutter#async#execute(cmd, a:bufnr, {
\ 'out': function('s:set_path'),
\ 'err': function('s:set_path', [-2])
\ })
endif
endif
else
let path = gitgutter#utility#system(cmd)
if v:shell_error
call gitgutter#utility#setbufvar(a:bufnr, 'path', -2)
else
call gitgutter#utility#setbufvar(a:bufnr, 'path', s:strip_trailing_new_line(path))
endif
return file_path_relative_to_repo_root
endfunction
function! gitgutter#utility#command_in_directory_of_file(cmd) abort
return 'cd '.gitgutter#utility#shellescape(gitgutter#utility#directory_of_file()).' && '.a:cmd
endfunction
function! gitgutter#utility#highlight_name_for_change(text) abort
if a:text ==# 'added'
return 'GitGutterLineAdded'
elseif a:text ==# 'removed'
return 'GitGutterLineRemoved'
elseif a:text ==# 'removed_first_line'
return 'GitGutterLineRemovedFirstLine'
elseif a:text ==# 'modified'
return 'GitGutterLineModified'
elseif a:text ==# 'modified_removed'
return 'GitGutterLineModifiedRemoved'
endif
endfunction
" Dedups list in-place.
" Assumes list has no empty entries.
function! gitgutter#utility#dedup(list)
return filter(sort(a:list), 'index(a:list, v:val, v:key + 1) == -1')
if has('nvim') && !has('nvim-0.2.0')
function! s:not_tracked_by_git(bufnr)
call s:set_path(a:bufnr, -2)
endfunction
endif
function! s:set_path(bufnr, path)
if a:bufnr == -2
let [bufnr, path] = [a:path, a:bufnr]
call gitgutter#utility#setbufvar(bufnr, 'path', path)
else
call gitgutter#utility#setbufvar(a:bufnr, 'path', s:strip_trailing_new_line(a:path))
endif
endfunction
function! gitgutter#utility#strip_trailing_new_line(line) abort
function! gitgutter#utility#cd_cmd(bufnr, cmd) abort
let cd = s:unc_path(a:bufnr) ? 'pushd' : (s:windows() ? 'cd /d' : 'cd')
return cd.' '.s:dir(a:bufnr).' && '.a:cmd
endfunction
function! s:unc_path(bufnr)
return s:abs_path(a:bufnr, 0) =~ '^\\\\'
endfunction
function! s:use_known_shell() abort
if has('unix') && &shell !=# 'sh'
let [s:shell, s:shellcmdflag, s:shellredir] = [&shell, &shellcmdflag, &shellredir]
let &shell = 'sh'
set shellcmdflag=-c shellredir=>%s\ 2>&1
endif
endfunction
function! s:restore_shell() abort
if has('unix') && exists('s:shell')
let [&shell, &shellcmdflag, &shellredir] = [s:shell, s:shellcmdflag, s:shellredir]
endif
endfunction
function! s:abs_path(bufnr, shellesc)
let p = resolve(expand('#'.a:bufnr.':p'))
return a:shellesc ? gitgutter#utility#shellescape(p) : p
endfunction
function! s:dir(bufnr) abort
return gitgutter#utility#shellescape(fnamemodify(s:abs_path(a:bufnr, 0), ':h'))
endfunction
" Not shellescaped.
function! s:filename(bufnr) abort
return fnamemodify(s:abs_path(a:bufnr, 0), ':t')
endfunction
function! s:exists_file(bufnr) abort
return filereadable(s:abs_path(a:bufnr, 0))
endfunction
function! s:strip_trailing_new_line(line) abort
return substitute(a:line, '\n$', '', '')
endfunction
" True for git v1.7.2+.
function! gitgutter#utility#git_supports_command_line_config_override() abort
call system(g:gitgutter_git_executable.' -c foo.bar=baz --version')
return !v:shell_error
endfunction
function! gitgutter#utility#stringify(list) abort
return join(a:list, "\n")."\n"
endfunction
function! gitgutter#utility#use_known_shell() abort
if has('unix')
if &shell !=# 'sh'
let s:shell = &shell
let s:shellcmdflag = &shellcmdflag
let s:shellredir = &shellredir
let &shell = 'sh'
set shellcmdflag=-c
set shellredir=>%s\ 2>&1
endif
endif
endfunction
function! gitgutter#utility#restore_shell() abort
if has('unix')
if exists('s:shell')
let &shell = s:shell
let &shellcmdflag = s:shellcmdflag
let &shellredir = s:shellredir
endif
endif
function! s:windows()
return has('win64') || has('win32') || has('win16')
endfunction

View file

@ -4,45 +4,50 @@
Vim Git Gutter
Author: Andy Stewart <http://airbladesoftware.com/>
Author: Andy Stewart <https://airbladesoftware.com/>
Plugin Homepage: <https://github.com/airblade/vim-gitgutter>
===============================================================================
CONTENTS *GitGutterContents*
1. Introduction ................. |GitGutterIntroduction|
2. Installation ................. |GitGutterInstallation|
3. Usage ........................ |GitGutterUsage|
4. Commands ..................... |GitGutterCommands|
5. Autocommand .................. |GitGutterAutocmd|
6. CUSTOMISATION................. |GitGutterCustomisation|
7. FAQ .......................... |GitGutterFAQ|
===============================================================================
1. INTRODUCTION *GitGutterIntroduction*
*GitGutter*
CONTENTS *gitgutter*
Vim Git Gutter is a Vim plugin which shows a git diff in the 'gutter' (sign
column). It shows whether each line has been added, modified, and where lines
have been removed.
Introduction ................. |gitgutter-introduction|
Installation ................. |gitgutter-installation|
Commands ..................... |gitgutter-commands|
Mappings ..................... |gitgutter-mappings|
Autocommand .................. |gitgutter-autocommand|
Options ...................... |gitgutter-options|
Highlights ................... |gitgutter-highlights|
FAQ .......................... |gitgutter-faq|
TROUBLESHOOTING .............. |gitgutter-troubleshooting|
This is a port of the Git Gutter plugin for Sublime Text 2.
===============================================================================
2. INSTALLATION *GitGutterInstallation*
INTRODUCTION *gitgutter-introduction*
* Pathogen:
GitGutter is a Vim plugin which shows a git diff in the 'gutter' (sign column).
It shows which lines have been added, modified, or removed. You can also
preview, stage, and undo individual hunks. The plugin also provides a hunk
text object.
The signs are always up to date and the plugin never saves your buffer.
===============================================================================
INSTALLATION *gitgutter-installation*
Pathogen:~
>
cd ~/.vim/bundle
git clone git://github.com/airblade/vim-gitgutter.git
<
* Voom:
Voom:~
Edit your plugin manifest (`voom edit`) and add:
>
airblade/vim-gitgutter
<
* VimPlug:
VimPlug:~
Place this in your .vimrc:
>
@ -53,7 +58,7 @@ Then run the following in Vim:
:source %
:PlugInstall
<
* NeoBundle:
NeoBundle:~
Place this in your .vimrc:
>
@ -64,7 +69,7 @@ Then run the following in Vim:
:source %
:NeoBundleInstall
<
* No plugin manager:
No plugin manager:~
Copy vim-gitgutter's subdirectories into your vim configuration directory:
>
@ -73,108 +78,282 @@ Copy vim-gitgutter's subdirectories into your vim configuration directory:
<
See |add-global-plugin|.
===============================================================================
3. USAGE *GitGutterUsage*
You don't have to do anything: it just works.
===============================================================================
4. COMMANDS *GitGutterCommands*
COMMANDS *gitgutter-commands*
Commands for turning Git Gutter on and off:
Commands for turning vim-gitgutter on and off:~
:GitGutterDisable *:GitGutterDisable*
Explicitly turn Git Gutter off.
*gitgutter-:GitGutterDisable*
:GitGutterDisable Turn vim-gitgutter off for all buffers.
:GitGutterEnable *:GitGutterEnable*
Explicitly turn Git Gutter on.
*gitgutter-:GitGutterEnable*
:GitGutterEnable Turn vim-gitgutter on for all buffers.
:GitGutterToggle *:GitGutterToggle*
Explicitly turn Git Gutter on if it was off and vice versa.
*gitgutter-:GitGutterToggle*
:GitGutterToggle Toggle vim-gitgutter on or off for all buffers.
:GitGutter *:GitGutter*
Update signs for the current buffer.
*gitgutter-:GitGutter*
:GitGutter Update signs for the current buffer. You shouldn't
need to run this.
:GitGutterAll *:GitGutterAll*
Update signs across all buffers.
*gitgutter-:GitGutterAll*
:GitGutterAll Update signs for all buffers. You shouldn't need to
run this.
Commands for turning signs on and off (defaults to on):
:GitGutterSignsEnable *:GitGutterSignsEnable*
Explicitly turn line signs on.
Commands for turning signs on and off (defaults to on):~
:GitGutterSignsDisable *:GitGutterSignsDisable*
Explicitly turn line signs off.
*gitgutter-:GitGutterSignsEnable*
:GitGutterSignsEnable Show signs for the diff.
:GitGutterSignsToggle *:GitGutterSignsToggle*
Explicitly turn line signs on if it was off and vice versa.
*gitgutter-:GitGutterSignsDisable*
:GitGutterSignsDisable Do not show signs for the diff.
Commands for turning line highlighting on and off (defaults to off):
*gitgutter-:GitGutterSignsToggle*
:GitGutterSignsToggle Toggle signs on or off.
:GitGutterLineHighlightsEnable *:GitGutterLineHighlightsEnable*
Explicitly turn line highlighting on.
:GitGutterLineHighlightsDisable *:GitGutterLineHighlightsDisable*
Explicitly turn line highlighting off.
Commands for turning line highlighting on and off (defaults to off):~
:GitGutterLineHighlightsToggle *:GitGutterLineHighlightsToggle*
Explicitly turn line highlighting on if it was off and vice versa.
*gitgutter-:GitGutterLineHighlightsEnable*
:GitGutterLineHighlightsEnable Turn on line highlighting.
Commands for jumping between marked hunks:
*gitgutter-:GitGutterLineHighlightsDisable*
:GitGutterLineHighlightsDisable Turn off line highlighting.
:GitGutterNextHunk *:GitGutterNextHunk*
Jump to the next marked hunk. Takes a count.
*gitgutter-:GitGutterLineHighlightsToggle*
:GitGutterLineHighlightsToggle Turn line highlighting on or off.
:GitGutterPrevHunk *:GitGutterPrevHunk*
Jump to the previous marked hunk. Takes a count.
Commands for staging or undoing individual hunks:
Commands for jumping between hunks:~
:GitGutterStageHunk *:GitGutterStageHunk*
Stage the hunk the cursor is in.
*gitgutter-:GitGutterNextHunk*
:GitGutterNextHunk Jump to the next [count] hunk.
:GitGutterUndoHunk *:GitGutterUndoHunk*
Undo the hunk the cursor is in.
*gitgutter-:GitGutterPrevHunk*
:GitGutterPrevHunk Jump to the previous [count] hunk.
Commands for operating on a hunk:~
*gitgutter-:GitGutterStageHunk*
:GitGutterStageHunk Stage the hunk the cursor is in.
*gitgutter-:GitGutterUndoHunk*
:GitGutterUndoHunk Undo the hunk the cursor is in.
*gitgutter-:GitGutterPreviewHunk*
:GitGutterPreviewHunk Preview the hunk the cursor is in.
Use |:pclose| or |CTRL-W_CTRL-Z| to close the preview
window.
:GitGutterPreviewHunk *:GitGutterPreviewHunk*
Preview the hunk the cursor is in.
Use |:pclose| or |CTRL-W_CTRL-Z| to close the preview window.
===============================================================================
5. AUTOCOMMAND *GitGutterAutocmd*
AUTOCOMMAND *gitgutter-autocommand*
User GitGutter~
After updating a buffer's signs vim-gitgutter fires a |User| |autocmd| with the
event GitGutter. You can listen for this event, for example:
>
autocmd User GitGutter call updateMyStatusLine()
<
A dictionary `g:gitgutter_hook_context` is made available during its execution,
which contains an entry `bufnr` that contains the buffer number being updated.
===============================================================================
6. CUSTOMISATION *GitGutterCustomisation*
MAPPINGS *gitgutter-mappings*
You can customise:
You can disable all these mappings with:
>
let g:gitgutter_map_keys = 0
<
- The sign column's colours
- The signs' colours and symbols
- Line highlights
- The base of the diff
- Extra arguments for git-diff
- Key mappings
- The grep executable used
- Whether or not vim-gitgutter is on initially (defaults to on)
- Whether or not signs are shown (defaults to yes)
- Whether or not line highlighting is on initially (defaults to off)
- Whether or not vim-gitgutter runs in realtime (defaults to yes)
- Whether or not vim-gitgutter runs eagerly (defaults to yes)
- Whether or not vim-gitgutter runs asynchronously (defaults to yes)
Hunk operations:~
Please note that vim-gitgutter won't override any colours or highlights you've
set in your colorscheme.
These can be repeated with `.` if you have vim-repeat installed.
SIGN COLUMN
*gitgutter-<Leader>hp*
<Leader>hp Preview the hunk under the cursor.
By default vim-gitgutter will make the sign column look like the line number
column (i.e. the |hl-LineNr| highlight group).
*gitgutter-<Leader>hs*
<Leader>hs Stage the hunk under the cursor.
*gitgutter-<Leader>hu*
<Leader>hu Undo the hunk under the cursor.
You can change these mappings like this:
>
nmap ghp <Plug>GitGutterPreviewHunk
nmap ghs <Plug>GitGutterStageHunk
nmap ghu <Plug>GitGutterUndoHunk
<
Hunk jumping:~
*gitgutter-]c*
]c Jump to the next [count] hunk.
*gitgutter-[c*
[c Jump to the previous [count] hunk.
You can change these mappings like this:
>
nmap [c <Plug>GitGutterPrevHunk
nmap ]c <Plug>GitGutterNextHunk
<
Hunk text object:~
*gitgutter-ic* *gitgutter-ac* *gitgutter-text-object*
"ic" operates on the current hunk's lines. "ac" does the same but also includes
trailing empty lines.
>
omap ic <Plug>GitGutterTextObjectInnerPending
omap ac <Plug>GitGutterTextObjectOuterPending
xmap ic <Plug>GitGutterTextObjectInnerVisual
xmap ac <Plug>GitGutterTextObjectOuterVisual
<
===============================================================================
OPTIONS *gitgutter-options*
The most important option is 'updatetime' which determines how long (in
milliseconds) the plugin will wait after you stop typing before it updates the
signs. Vim's default is 4000. I recommend 100.
Most important option:~
'updatetime'
Git:~
|g:gitgutter_git_executable|
|g:gitgutter_diff_args|
|g:gitgutter_diff_base|
Grep:~
|g:gitgutter_grep|
Signs:~
|g:gitgutter_signs|
|g:gitgutter_highlight_lines|
|g:gitgutter_max_signs|
|g:gitgutter_sign_added|
|g:gitgutter_sign_modified|
|g:gitgutter_sign_removed|
|g:gitgutter_sign_removed_first_line|
|g:gitgutter_sign_modified_removed|
|g:gitgutter_sign_column_always|
|g:gitgutter_override_sign_column_highlight|
Terminal:~
|g:gitgutter_terminal_reports_focus|
General:~
|g:gitgutter_enabled|
|g:gitgutter_map_keys|
|g:gitgutter_async|
|g:gitgutter_log|
*g:gitgutter_git_executable*
Default: 'git'
This option determines what git binary to use. Set this if git is not on your
path.
*g:gitgutter_diff_args*
Default: empty
Use this option to pass any extra arguments to git-diff. For example:
>
let g:gitgutter_diff_args = '-w'
<
*g:gitgutter_diff_base*
Default: empty
By default buffers are diffed against the index. Use this option to diff against
a revision instead. For example:
>
let g:gitgutter_diff_base = '<some commit SHA>'
<
*g:gitgutter_grep*
Default: 'grep'
The plugin pipes the output of git-diff into grep to minimise the amount of data
vim has to process. Set this option if grep is not on your path.
grep must produce plain-text output without any ANSI escape codes or colours.
Use this option to turn off colours if necessary.
>
let g:gitgutter_grep = 'grep --color=never'
<
If you do not want to use grep at all (perhaps to debug why signs are not
showing), set this option to an empty string:
>
let g:gitgutter_grep = ''
<
*g:gitgutter_signs*
Default: 1
Determines whether or not to show signs.
*g:gitgutter_highlight_lines*
Default: 0
Determines whether or not to show line highlights.
*g:gitgutter_max_signs*
Default: 500
Sets the maximum number of signs to show in a buffer. Vim is slow at updating
signs, so to avoid slowing down the GUI the number of signs is capped. When
the number of changed lines exceeds this value, the plugin removes all signs
and displays a warning message.
*g:gitgutter_sign_added*
*g:gitgutter_sign_modified*
*g:gitgutter_sign_removed*
*g:gitgutter_sign_removed_first_line*
*g:gitgutter_sign_modified_removed*
Defaults:
>
let g:gitgutter_sign_added = '+'
let g:gitgutter_sign_modified = '~'
let g:gitgutter_sign_removed = '_'
let g:gitgutter_sign_removed_first_line = '‾'
let g:gitgutter_sign_modified_removed = '~_'
<
You can use unicode characters but not images. Signs must not take up more than
2 columns.
*g:gitgutter_sign_column_always*
Default: 0
This legacy option controls whether the sign column should always be shown, even
if there are no signs to display.
From Vim 7.4.2201, use 'signcolumn' instead:
>
set signcolumn=yes
<
*g:gitgutter_override_sign_column_highlight*
Default: 1
Controls whether to make the sign column look like the line-number column (i.e.
the |hl-LineNr| highlight group).
To customise your sign column's background color, first tell vim-gitgutter to
leave it alone:
@ -186,15 +365,54 @@ And then either update your colorscheme's |hlSignColumn| highlight group or set
it in your |vimrc|:
Desired appearance Command ~
Same as line number column highlight clear SignColumn
Same as line-number column highlight clear SignColumn
User-defined (terminal Vim) highlight SignColumn ctermbg={whatever}
User-defined (graphical Vim) highlight SignColumn guibg={whatever}
SIGNS' COLOURS AND SYMBOLS
To customise the colours, set up the following highlight groups in your
*g:gitgutter_terminal_reports_focus*
Default: 1
Normally the plugin uses |FocusGained| to force-update all buffers when Vim
receives focus. However some terminals do not report focus events and so the
|FocusGained| autocommand never fires.
If this applies to you, either install something like Terminus
(https://github.com/wincent/terminus) to make |FocusGained| work or set this
option to 0.
When this option is 0, the plugin force-updates the buffer on |BufEnter|
(instead of only updating if the buffer's contents has changed since the last
update).
*g:gitgutter_enabled*
Default: 1
Controls whether or not the plugin is on at startup.
*g:gitgutter_map_keys*
Default: 1
Controls whether or not the plugin provides mappings. See |gitgutter-mapppings|.
*g:gitgutter_async*
Default: 1
Controls whether or not diffs are run in the background. This has no effect if
your Vim does not support background jobs.
*g:gitgutter_log*
Default: 0
When switched on, the plugin logs to gitgutter.log in the directory where it is
installed. Additionally it logs channel activity to channel.log.
===============================================================================
HIGHLIGHTS *gitgutter-highlights*
To change the signs' colours, set up the following highlight groups in your
colorscheme or |vimrc|:
>
GitGutterAdd " an added line
GitGutterChange " a changed line
@ -208,17 +426,7 @@ them to existing highlight groups with, say:
highlight link GitGutterAdd DiffAdd
<
To customise the symbols, add the following to your |vimrc|:
>
let g:gitgutter_sign_added = 'xx'
let g:gitgutter_sign_modified = 'yy'
let g:gitgutter_sign_removed = 'zz'
let g:gitgutter_sign_modified_removed = 'ww'
<
LINE HIGHLIGHTS
Similarly to the signs' colours, set up the following highlight groups in your
To change the line highlights, set up the following highlight groups in your
colorscheme or |vimrc|:
>
GitGutterAddLine " default: links to DiffAdd
@ -227,115 +435,85 @@ colorscheme or |vimrc|:
GitGutterChangeDeleteLine " default: links to GitGutterChangeLineDefault
<
THE BASE OF THE DIFF
By default buffers are diffed against the index. To diff against a commit
instead:
>
let g:gitgutter_diff_base = '<commit SHA>'
<
EXTRA ARGUMENTS FOR GIT-DIFF
To pass extra arguments to git-diff, add this to your |vimrc|:
>
let g:gitgutter_diff_args = '-w'
<
KEY MAPPINGS
To disable all key maps:
>
let g:gitgutter_map_keys = 0
<
To change the hunk-jumping maps (defaults shown):
>
nmap [c <Plug>GitGutterPrevHunk
nmap ]c <Plug>GitGutterNextHunk
<
To change the hunk-staging/undoing/previewing maps (defaults shown):
>
nmap <Leader>hs <Plug>GitGutterStageHunk
nmap <Leader>hu <Plug>GitGutterUndoHunk
nmap <Leader>hp <Plug>GitGutterPreviewHunk
<
To change the hunk text object maps (defaults shown):
>
omap ic <Plug>GitGutterTextObjectInnerPending
omap ac <Plug>GitGutterTextObjectOuterPending
xmap ic <Plug>GitGutterTextObjectInnerVisual
xmap ac <Plug>GitGutterTextObjectOuterVisual
<
TO USE A CUSTOM GREP COMMAND
To use a custom invocation for grep, use this:
>
let g:gitgutter_grep_command = 'grep'
<
TO TURN OFF VIM-GITGUTTER BY DEFAULT
Add to your |vimrc|
>
let g:gitgutter_enabled = 0
<
TO TURN OFF SIGNS BY DEFAULT
Add to your |vimrc|
>
let g:gitgutter_signs = 0
<
Note that the sign column will still be present if you have line highlighting
switched on.
TO TURN ON LINE HIGHLIGHTING BY DEFAULT
Add to your |vimrc|
>
let g:gitgutter_highlight_lines = 1
<
TO STOP VIM-GITGUTTER RUNNING IN REALTIME
Add to your |vimrc|
>
let g:gitgutter_realtime = 0
<
TO STOP VIM-GITGUTTER RUNNING EAGERLY
Add to your |vimrc|
>
let g:gitgutter_eager = 0
<
TO TURN OFF ASYNCHRONOUS UPDATES
By default diffs are run asynchronously. To run diffs synchronously
instead:
Add to your |vimrc|
>
let g:gitgutter_async = 0
<
===============================================================================
7. FAQ *GitGutterFAQ*
FAQ *gitgutter-faq*
a. Why are the colours in the sign column weird?
a. How do I turn off realtime updates?
Add this to your vim configuration in an |after-directory|:
>
autocmd! gitgutter CursorHold,CursorHoldI
<
b. Why can't I unstage staged changes?
This plugin is for showing changes between the working tree and the index
(and staging/undoing those changes). Unstaging a staged hunk would require
showing changes between the index and HEAD, which is out of scope.
c. Why are the colours in the sign column weird?
Your colorscheme is configuring the |hl-SignColumn| highlight group weirdly.
Please see |GitGutterCustomisation| on customising the sign column.
Please see |g:gitgutter_override_sign_column_highlight| on customising the
sign column.
b. What happens if I also use another plugin which uses signs (e.g. Syntastic)?
d. What happens if I also use another plugin which uses signs (e.g. Syntastic)?
Vim only allows one sign per line. Vim-gitgutter will not interfere with
signs it did not add.
===============================================================================
TROUBLESHOOTING *gitgutter-troubleshooting*
When no signs are showing at all:~
1. Try bypassing grep with:
>
let g:gitgutter_grep = ''
<
If it works, the problem is grep outputting ANSI escape codes. Use this
option to pass arguments to grep to turn off the escape codes.
2. Verify git is on your path:
>
:echo system('git --version')
<
3. Verify your git config is compatible with the version of git return by the
command above.
4. Verify your Vim supports signs. The following should give 1:
>
:echo has('signs')
<
5. Check whether the plugin thinks git knows about your file:
>
:echo getbufvar('','gitgutter').path
<
If the result is -2, the plugin thinks your file is not tracked by git.
When the whole file is marked as added:~
If you use zsh, and you set "CDPATH", make sure "CDPATH" does not include the
current directory.
When signs take a few seconds to appear:~
Try reducing 'updatetime':
>
set updatetime=100
<
When signs don't update after focusing Vim:~
Your terminal probably isn't reporting focus events. Either try installing
Terminus (https://github.com/wincent/terminus) or set:
>
let g:gitgutter_terminal_reports_focus = 0
<
Vim only allows one sign per line. Before adding a sign to a line,
vim-gitgutter checks whether a sign has already been added by somebody else.
If so it doesn't do anything. In other words vim-gitgutter won't overwrite
another plugin's signs. It also won't remove another plugin's signs.

View file

@ -7,14 +7,9 @@ let g:loaded_gitgutter = 1
" Initialisation {{{
" Realtime sign updates require Vim 7.3.105+.
if v:version < 703 || (v:version == 703 && !has("patch105"))
let g:gitgutter_realtime = 0
endif
" Eager updates require gettabvar()/settabvar().
if !exists("*gettabvar")
let g:gitgutter_eager = 0
call gitgutter#utility#warn('requires Vim 7.3.105')
finish
endif
function! s:set(var, default) abort
@ -33,35 +28,50 @@ call s:set('g:gitgutter_signs', 1)
call s:set('g:gitgutter_highlight_lines', 0)
call s:set('g:gitgutter_sign_column_always', 0)
if g:gitgutter_sign_column_always && exists('&signcolumn')
" Vim 7.4.2201.
set signcolumn=yes
let g:gitgutter_sign_column_always = 0
call gitgutter#utility#warn('please replace "let g:gitgutter_sign_column_always=1" with "set signcolumn=yes"')
endif
call s:set('g:gitgutter_override_sign_column_highlight', 1)
call s:set('g:gitgutter_realtime', 1)
call s:set('g:gitgutter_eager', 1)
call s:set('g:gitgutter_sign_added', '+')
call s:set('g:gitgutter_sign_modified', '~')
call s:set('g:gitgutter_sign_removed', '_')
try
if gitgutter#utility#supports_overscore_sign()
call s:set('g:gitgutter_sign_removed_first_line', '‾')
catch /E239/
let g:gitgutter_sign_removed_first_line = '_^'
endtry
else
call s:set('g:gitgutter_sign_removed_first_line', '_^')
endif
call s:set('g:gitgutter_sign_modified_removed', '~_')
call s:set('g:gitgutter_diff_args', '')
call s:set('g:gitgutter_diff_base', '')
call s:set('g:gitgutter_map_keys', 1)
call s:set('g:gitgutter_avoid_cmd_prompt_on_windows', 1)
call s:set('g:gitgutter_terminal_reports_focus', 1)
call s:set('g:gitgutter_async', 1)
call s:set('g:gitgutter_log', 0)
call s:set('g:gitgutter_git_executable', 'git')
call s:set('g:gitgutter_git_executable', 'git')
if !executable(g:gitgutter_git_executable)
call gitgutter#utility#warn('cannot find git. Please set g:gitgutter_git_executable.')
endif
let default_grep = 'grep'
call s:set('g:gitgutter_grep', default_grep)
if !empty(g:gitgutter_grep)
if executable(split(g:gitgutter_grep)[0])
if $GREP_OPTIONS =~# '--color=always'
let g:gitgutter_grep .= ' --color=never'
endif
else
if g:gitgutter_grep !=# default_grep
call gitgutter#utility#warn('cannot find '.g:gitgutter_grep.'. Please check g:gitgutter_grep.')
endif
let g:gitgutter_grep = ''
endif
endif
call gitgutter#highlight#define_sign_column_highlight()
call gitgutter#highlight#define_highlights()
call gitgutter#highlight#define_signs()
@ -70,40 +80,39 @@ call gitgutter#highlight#define_signs()
" Primary functions {{{
command -bar GitGutterAll call gitgutter#all()
command -bar GitGutter call gitgutter#process_buffer(bufnr(''), 0)
command! -bar GitGutterAll call gitgutter#all(1)
command! -bar GitGutter call gitgutter#process_buffer(bufnr(''), 1)
command -bar GitGutterDisable call gitgutter#disable()
command -bar GitGutterEnable call gitgutter#enable()
command -bar GitGutterToggle call gitgutter#toggle()
command! -bar GitGutterDisable call gitgutter#disable()
command! -bar GitGutterEnable call gitgutter#enable()
command! -bar GitGutterToggle call gitgutter#toggle()
" }}}
" Line highlights {{{
command -bar GitGutterLineHighlightsDisable call gitgutter#line_highlights_disable()
command -bar GitGutterLineHighlightsEnable call gitgutter#line_highlights_enable()
command -bar GitGutterLineHighlightsToggle call gitgutter#line_highlights_toggle()
command! -bar GitGutterLineHighlightsDisable call gitgutter#highlight#line_disable()
command! -bar GitGutterLineHighlightsEnable call gitgutter#highlight#line_enable()
command! -bar GitGutterLineHighlightsToggle call gitgutter#highlight#line_toggle()
" }}}
" Signs {{{
command -bar GitGutterSignsEnable call gitgutter#signs_enable()
command -bar GitGutterSignsDisable call gitgutter#signs_disable()
command -bar GitGutterSignsToggle call gitgutter#signs_toggle()
command! -bar GitGutterSignsEnable call gitgutter#sign#enable()
command! -bar GitGutterSignsDisable call gitgutter#sign#disable()
command! -bar GitGutterSignsToggle call gitgutter#sign#toggle()
" }}}
" Hunks {{{
command -bar -count=1 GitGutterNextHunk call gitgutter#hunk#next_hunk(<count>)
command -bar -count=1 GitGutterPrevHunk call gitgutter#hunk#prev_hunk(<count>)
command! -bar -count=1 GitGutterNextHunk call gitgutter#hunk#next_hunk(<count>)
command! -bar -count=1 GitGutterPrevHunk call gitgutter#hunk#prev_hunk(<count>)
command -bar GitGutterStageHunk call gitgutter#stage_hunk()
command -bar GitGutterUndoHunk call gitgutter#undo_hunk()
command -bar GitGutterRevertHunk echomsg 'GitGutterRevertHunk is deprecated. Use GitGutterUndoHunk'<Bar>call gitgutter#undo_hunk()
command -bar GitGutterPreviewHunk call gitgutter#preview_hunk()
command! -bar GitGutterStageHunk call gitgutter#hunk#stage()
command! -bar GitGutterUndoHunk call gitgutter#hunk#undo()
command! -bar GitGutterPreviewHunk call gitgutter#hunk#preview()
" Hunk text object
onoremap <silent> <Plug>GitGutterTextObjectInnerPending :<C-U>call gitgutter#hunk#text_object(1)<CR>
@ -130,7 +139,8 @@ xnoremap <silent> <Plug>GitGutterTextObjectOuterVisual :<C-U>call gitgutter#hun
" `line` - refers to the line number where the change starts
" `count` - refers to the number of lines the change covers
function! GitGutterGetHunks()
return gitgutter#utility#is_active() ? gitgutter#hunk#hunks() : []
let bufnr = bufnr('')
return gitgutter#utility#is_active(bufnr) ? gitgutter#hunk#hunks(bufnr) : []
endfunction
" Returns an array that contains a summary of the hunk status for the current
@ -142,7 +152,7 @@ endfunction
" }}}
command -bar GitGutterDebug call gitgutter#debug#debug()
command! -bar GitGutterDebug call gitgutter#debug#debug()
" Maps {{{
@ -169,7 +179,6 @@ if g:gitgutter_map_keys
endif
if !hasmapto('<Plug>GitGutterUndoHunk') && maparg('<Leader>hu', 'n') ==# ''
nmap <Leader>hu <Plug>GitGutterUndoHunk
nmap <Leader>hr <Plug>GitGutterUndoHunk:echomsg '<Leader>hr is deprecated. Use <Leader>hu'<CR>
endif
if !hasmapto('<Plug>GitGutterPreviewHunk') && maparg('<Leader>hp', 'n') ==# ''
nmap <Leader>hp <Plug>GitGutterPreviewHunk
@ -196,35 +205,26 @@ endif
augroup gitgutter
autocmd!
if g:gitgutter_realtime
autocmd CursorHold,CursorHoldI * call gitgutter#process_buffer(bufnr(''), 1)
endif
if g:gitgutter_eager
autocmd BufWritePost,FileChangedShellPost,ShellCmdPost * call gitgutter#process_buffer(bufnr(''), 0)
autocmd TabEnter * let t:gitgutter_didtabenter = 1
autocmd BufEnter *
\ if gettabvar(tabpagenr(), 'gitgutter_didtabenter') |
\ call settabvar(tabpagenr(), 'gitgutter_didtabenter', 0) |
\ call gitgutter#all() |
\ if exists('t:gitgutter_didtabenter') && t:gitgutter_didtabenter |
\ let t:gitgutter_didtabenter = 0 |
\ call gitgutter#all(!g:gitgutter_terminal_reports_focus) |
\ else |
\ call gitgutter#process_buffer(bufnr(''), 0) |
\ call gitgutter#init_buffer(bufnr('')) |
\ call gitgutter#process_buffer(bufnr(''), !g:gitgutter_terminal_reports_focus) |
\ endif
autocmd TabEnter * call settabvar(tabpagenr(), 'gitgutter_didtabenter', 1)
autocmd CursorHold,CursorHoldI * call gitgutter#process_buffer(bufnr(''), 0)
autocmd FileChangedShellPost,ShellCmdPost * call gitgutter#process_buffer(bufnr(''), 1)
" Ensure that all buffers are processed when opening vim with multiple files, e.g.:
"
" vim -o file1 file2
autocmd VimEnter * if winnr() != winnr('$') | :GitGutterAll | endif
autocmd VimEnter * if winnr() != winnr('$') | call gitgutter#all(0) | endif
if !has('gui_win32')
autocmd FocusGained * call gitgutter#all()
endif
else
autocmd BufRead,BufWritePost,FileChangedShellPost * call gitgutter#process_buffer(bufnr(''), 0)
endif
autocmd FocusGained * call gitgutter#all(1)
autocmd ColorScheme * call gitgutter#highlight#define_sign_column_highlight() | call gitgutter#highlight#define_highlights()

View file

@ -0,0 +1,8 @@
The quick brown fox jumps
over the lazy dog
いろはにほへとちりぬるを
わかよたれそつねならむ
うゐのおくやまけふこえて
あさきゆめみしゑひもせす

View file

@ -2,6 +2,8 @@
VIM="/Applications/MacVim.app/Contents/MacOS/Vim -v"
export VIM_GITGUTTER_TEST=1
$VIM -u NONE -U NONE -N \
--cmd 'set rtp+=../' \
--cmd 'let g:gitgutter_async=0' \

View file

@ -31,6 +31,10 @@ function s:git_diff_staged()
return split(system('git diff -U0 --staged fixture.txt'), '\n')
endfunction
function s:trigger_gitgutter()
doautocmd CursorHold
endfunction
"
" SetUp / TearDown
@ -40,7 +44,8 @@ function SetUp()
call system("git init ".s:test_repo.
\ " && cd ".s:test_repo.
\ " && cp ../fixture.txt .".
\ " && git add . && git commit -m 'initial'")
\ " && git add . && git commit -m 'initial'".
\ " && git config diff.mnemonicPrefix false")
execute ':cd' s:test_repo
edit! fixture.txt
call gitgutter#sign#reset()
@ -64,7 +69,7 @@ endfunction
function Test_add_lines()
normal ggo*
write
call s:trigger_gitgutter()
let expected = ["line=2 id=3000 name=GitGutterLineAdded"]
call assert_equal(expected, s:signs('fixture.txt'))
@ -76,7 +81,7 @@ function Test_add_lines_fish()
set shell=/usr/local/bin/fish
normal ggo*
write
call s:trigger_gitgutter()
let expected = ["line=2 id=3000 name=GitGutterLineAdded"]
call assert_equal(expected, s:signs('fixture.txt'))
@ -87,7 +92,7 @@ endfunction
function Test_modify_lines()
normal ggi*
write
call s:trigger_gitgutter()
let expected = ["line=1 id=3000 name=GitGutterLineModified"]
call assert_equal(expected, s:signs('fixture.txt'))
@ -96,7 +101,7 @@ endfunction
function Test_remove_lines()
execute '5d'
write
call s:trigger_gitgutter()
let expected = ["line=4 id=3000 name=GitGutterLineRemoved"]
call assert_equal(expected, s:signs('fixture.txt'))
@ -105,7 +110,7 @@ endfunction
function Test_remove_first_lines()
execute '1d'
write
call s:trigger_gitgutter()
let expected = ["line=1 id=3000 name=GitGutterLineRemovedFirstLine"]
call assert_equal(expected, s:signs('fixture.txt'))
@ -115,7 +120,7 @@ endfunction
function Test_edit_file_with_same_name_as_a_branch()
normal 5Gi*
call system('git checkout -b fixture.txt')
write
call s:trigger_gitgutter()
let expected = ["line=5 id=3000 name=GitGutterLineModified"]
call assert_equal(expected, s:signs('fixture.txt'))
@ -127,7 +132,7 @@ function Test_file_added_to_git()
call system('touch '.tmpfile.' && git add '.tmpfile)
execute 'edit '.tmpfile
normal ihello
write
call s:trigger_gitgutter()
let expected = ["line=1 id=3000 name=GitGutterLineAdded"]
call assert_equal(expected, s:signs('fileAddedToGit.tmp'))
@ -138,7 +143,7 @@ function Test_filename_with_equals()
call system('touch =fixture=.txt && git add =fixture=.txt')
edit =fixture=.txt
normal ggo*
write
call s:trigger_gitgutter()
let expected = [
\ 'line=1 id=3000 name=GitGutterLineAdded',
@ -152,7 +157,7 @@ function Test_filename_with_square_brackets()
call system('touch fix[tu]re.txt && git add fix[tu]re.txt')
edit fix[tu]re.txt
normal ggo*
write
call s:trigger_gitgutter()
let expected = [
\ 'line=1 id=3000 name=GitGutterLineAdded',
@ -168,7 +173,7 @@ function Test_follow_symlink()
call system('ln -nfs fixture.txt '.tmp)
execute 'edit '.tmp
6d
write
call s:trigger_gitgutter()
let expected = ['line=5 id=3000 name=GitGutterLineRemoved']
call assert_equal(expected, s:signs('symlink'))
@ -183,7 +188,7 @@ function Test_keep_alt()
call assert_equal('', bufname('#'))
normal ggx
doautocmd CursorHold
call s:trigger_gitgutter()
call assert_equal('', bufname('#'))
endfunction
@ -193,7 +198,7 @@ function Test_keep_modified()
normal 5Go*
call assert_equal(1, getbufvar('', '&modified'))
doautocmd CursorHold
call s:trigger_gitgutter()
call assert_equal(1, getbufvar('', '&modified'))
endfunction
@ -204,7 +209,7 @@ function Test_keep_op_marks()
call assert_equal([0,6,1,0], getpos("'["))
call assert_equal([0,6,2,0], getpos("']"))
doautocmd CursorHold
call s:trigger_gitgutter()
call assert_equal([0,6,1,0], getpos("'["))
call assert_equal([0,6,2,0], getpos("']"))
@ -218,26 +223,15 @@ endfunction
function Test_orphaned_signs()
execute "normal 5GoX\<CR>Y"
write
call s:trigger_gitgutter()
6d
write
call s:trigger_gitgutter()
let expected = ['line=6 id=3001 name=GitGutterLineAdded']
call assert_equal(expected, s:signs('fixture.txt'))
endfunction
function Test_sign_column_always()
let g:gitgutter_sign_column_always=1
write
let expected = ['line=9999 id=2999 name=GitGutterDummy']
call assert_equal(expected, s:signs('fixture.txt'))
let g:gitgutter_sign_column_always=0
endfunction
function Test_untracked_file_outside_repo()
let tmp = tempname()
call system('touch '.tmp)
@ -252,7 +246,7 @@ function Test_untracked_file_within_repo()
call system('touch '.tmp)
execute 'edit '.tmp
normal ggo*
doautocmd CursorHold
call s:trigger_gitgutter()
call assert_equal([], s:signs(tmp))
@ -265,7 +259,7 @@ function Test_untracked_file_square_brackets_within_repo()
call system('touch '.tmp)
execute 'edit '.tmp
normal ggo*
doautocmd CursorHold
call s:trigger_gitgutter()
call assert_equal([], s:signs(tmp))
@ -301,8 +295,19 @@ function Test_hunk_stage()
call assert_equal([], s:signs('fixture.txt'))
call assert_equal([], s:git_diff())
" Buffer is unsaved
let expected = [
\ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index ae8e546..f5c6aff 100644',
\ '--- a/fixture.txt',
\ '+++ b/fixture.txt',
\ '@@ -5 +5 @@ d',
\ '-*e',
\ '+e'
\ ]
call assert_equal(expected, s:git_diff())
" Index has been updated
let expected = [
\ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index f5c6aff..ae8e546 100644',
@ -313,6 +318,11 @@ function Test_hunk_stage()
\ '+*e'
\ ]
call assert_equal(expected, s:git_diff_staged())
" Save the buffer
write
call assert_equal([], s:git_diff())
endfunction
@ -329,6 +339,31 @@ function Test_hunk_stage_nearby_hunk()
\ ]
call assert_equal(expected, s:signs('fixture.txt'))
" Buffer is unsaved
let expected = [
\ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index 53b13df..f5c6aff 100644',
\ '--- a/fixture.txt',
\ '+++ b/fixture.txt',
\ '@@ -3,0 +4 @@ c',
\ '+d',
\ ]
call assert_equal(expected, s:git_diff())
" Index has been updated
let expected = [
\ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index f5c6aff..53b13df 100644',
\ '--- a/fixture.txt',
\ '+++ b/fixture.txt',
\ '@@ -4 +3,0 @@ c',
\ '-d',
\ ]
call assert_equal(expected, s:git_diff_staged())
" Save the buffer
write
let expected = [
\ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index 53b13df..8fdfda7 100644',
@ -340,16 +375,6 @@ function Test_hunk_stage_nearby_hunk()
\ '+z',
\ ]
call assert_equal(expected, s:git_diff())
let expected = [
\ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index f5c6aff..53b13df 100644',
\ '--- a/fixture.txt',
\ '+++ b/fixture.txt',
\ '@@ -4 +3,0 @@ c',
\ '-d',
\ ]
call assert_equal(expected, s:git_diff_staged())
endfunction
@ -359,7 +384,6 @@ function Test_hunk_undo()
normal 5Gi*
GitGutterUndoHunk
write " write file so we can verify git diff (--staged)
call assert_equal('foo', &shell)
let &shell = _shell
@ -374,8 +398,9 @@ function Test_undo_nearby_hunk()
execute "normal! 2Gox\<CR>y\<CR>z"
normal 2jdd
normal k
call s:trigger_gitgutter()
GitGutterUndoHunk
write " write file so we can verify git diff (--staged)
call s:trigger_gitgutter()
let expected = [
\ 'line=3 id=3000 name=GitGutterLineAdded',
@ -384,6 +409,13 @@ function Test_undo_nearby_hunk()
\ ]
call assert_equal(expected, s:signs('fixture.txt'))
call assert_equal([], s:git_diff())
call assert_equal([], s:git_diff_staged())
" Save the buffer
write
let expected = [
\ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index f5c6aff..3fbde56 100644',
@ -396,5 +428,148 @@ function Test_undo_nearby_hunk()
\ ]
call assert_equal(expected, s:git_diff())
call assert_equal([], s:git_diff_staged())
endfunction
function Test_write_option()
set nowrite
normal ggo*
call s:trigger_gitgutter()
let expected = ["line=2 id=3000 name=GitGutterLineAdded"]
call assert_equal(expected, s:signs('fixture.txt'))
set write
endfunction
function Test_inner_text_object()
execute "normal! 2Gox\<CR>y\<CR>z\<CR>\<CR>"
call s:trigger_gitgutter()
normal dic
call s:trigger_gitgutter()
call assert_equal([], s:signs('fixture.txt'))
call assert_equal(readfile('fixture.txt'), getline(1,'$'))
" Excludes trailing lines
normal 9Gi*
normal 10Gi*
call s:trigger_gitgutter()
execute "normal vic\<Esc>"
call assert_equal([9, 10], [line("'<"), line("'>")])
endfunction
function Test_around_text_object()
execute "normal! 2Gox\<CR>y\<CR>z\<CR>\<CR>"
call s:trigger_gitgutter()
normal dac
call s:trigger_gitgutter()
call assert_equal([], s:signs('fixture.txt'))
call assert_equal(readfile('fixture.txt'), getline(1,'$'))
" Includes trailing lines
normal 9Gi*
normal 10Gi*
call s:trigger_gitgutter()
execute "normal vac\<Esc>"
call assert_equal([9, 11], [line("'<"), line("'>")])
endfunction
function Test_user_autocmd()
autocmd User GitGutter let s:autocmd_user = g:gitgutter_hook_context.bufnr
" Verify not fired when nothing changed.
let s:autocmd_user = 0
call s:trigger_gitgutter()
call assert_equal(0, s:autocmd_user)
" Verify fired when there was a change.
normal ggo*
let bufnr = bufnr('')
call s:trigger_gitgutter()
call assert_equal(bufnr, s:autocmd_user)
endfunction
function Test_fix_file_references()
" No special characters
let hunk_diff = join([
\ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index f5c6aff..3fbde56 100644',
\ '--- a/fixture.txt',
\ '+++ b/fixture.txt',
\ '@@ -2,0 +3,1 @@ b',
\ '+x'
\ ], "\n")."\n"
let filepath = 'blah.txt'
let expected = join([
\ 'diff --git a/blah.txt b/blah.txt',
\ 'index f5c6aff..3fbde56 100644',
\ '--- a/blah.txt',
\ '+++ b/blah.txt',
\ '@@ -2,0 +3,1 @@ b',
\ '+x'
\ ], "\n")."\n"
call assert_equal(expected, gitgutter#hunk#fix_file_references(filepath, hunk_diff))
" diff.mnemonicPrefix; spaces in filename
let hunk_diff = join([
\ 'diff --git i/x/cat dog w/x/cat dog',
\ 'index f5c6aff..3fbde56 100644',
\ '--- i/x/cat dog',
\ '+++ w/x/cat dog',
\ '@@ -2,0 +3,1 @@ b',
\ '+x'
\ ], "\n")."\n"
let filepath = 'blah.txt'
let expected = join([
\ 'diff --git i/blah.txt w/blah.txt',
\ 'index f5c6aff..3fbde56 100644',
\ '--- i/blah.txt',
\ '+++ w/blah.txt',
\ '@@ -2,0 +3,1 @@ b',
\ '+x'
\ ], "\n")."\n"
call assert_equal(expected, gitgutter#hunk#fix_file_references(filepath, hunk_diff))
" Backslashes in filename; quotation marks
let hunk_diff = join([
\ 'diff --git "a/C:\\Users\\FOO~1.PAR\\AppData\\Local\\Temp\\nvimJcmSv9\\11.1.vim" "b/C:\\Users\\FOO~1.PAR\\AppData\\Local\\Temp\\nvimJcmSv9\\12.1.vim"',
\ 'index f42aeb0..4930403 100644',
\ '--- "a/C:\\Users\\FOO~1.PAR\\AppData\\Local\\Temp\\nvimJcmSv9\\11.1.vim"',
\ '+++ "b/C:\\Users\\FOO~1.PAR\\AppData\\Local\\Temp\\nvimJcmSv9\\12.1.vim"',
\ '@@ -172,0 +173 @@ stuff',
\ '+x'
\ ], "\n")."\n"
let filepath = 'init.vim'
let expected = join([
\ 'diff --git "a/init.vim" "b/init.vim"',
\ 'index f42aeb0..4930403 100644',
\ '--- "a/init.vim"',
\ '+++ "b/init.vim"',
\ '@@ -172,0 +173 @@ stuff',
\ '+x'
\ ], "\n")."\n"
call assert_equal(expected, gitgutter#hunk#fix_file_references(filepath, hunk_diff))
endfunction
function Test_encoding()
call system('cp ../cp932.txt . && git add cp932.txt')
edit ++enc=cp932 cp932.txt
call s:trigger_gitgutter()
call assert_equal([], s:signs('cp932.txt'))
endfunction

View file

@ -0,0 +1,27 @@
" Measure how long it takes to unplace signs.
"
" Source this file with `:source %` or `vim -S unplace.vim`
let num = 500
sign define Foo text=*
new
call append(0, range(1, num))
for i in range(1, num)
execute "sign place ".i." line=".i." name=Foo buffer=".bufnr('')
endfor
let start = reltime()
for i in range(1, num)
execute "sign unplace ".i
endfor
let elapsed = reltime(start)
bdelete!
echom split(reltimestr(elapsed))[0]."s to remove ".num." signs"
echom string(reltimefloat(elapsed) * 1000 / num).' ms/sign'
echom string(float2nr(num / reltimefloat(elapsed))).' sign/s'

View file

@ -0,0 +1,12 @@
---
coverage:
status:
project:
default:
target: auto
threshold: 1
base: auto
comment: false
ignore:
- "!autoload/go/*.vim$"
- "autoload/go/*_test.vim$"

View file

@ -1,5 +1,15 @@
## unplanned
## 1.17 - (March 27, 2018)
FEATURES:
* **Debugger support!** Add integrated support for the
[`delve`](https://github.com/derekparker/delve) debugger. Use
`:GoInstallBinaries` to install `dlv`, and see `:help go-debug` to get
started.
[[GH-1390]](https://github.com/fatih/vim-go/pull/1390)
IMPROVEMENTS:
* Add descriptions to neosnippet abbrevations.
@ -8,6 +18,16 @@ IMPROVEMENTS:
`gometalinter` is run automatically when saving a buffer. Whether the
location list or quickfix list is used can be customized in the usual ways.
[[GH-1652]](https://github.com/fatih/vim-go/pull/1652)
* Redraw the screen before executing blocking calls to gocode.
[[GH-1671]](https://github.com/fatih/vim-go/pull/1671)
* Add `fe` -> `fmt.Errorf()` snippet for NeoSnippet and UltiSnippets.
[[GH-1677]](https://github.com/fatih/vim-go/pull/1677)
* Use the async api when calling guru from neovim.
[[GH-1678]](https://github.com/fatih/vim-go/pull/1678)
* Use the async api when calling gocode to get type info.
[[GH-1697]](https://github.com/fatih/vim-go/pull/1697)
* Cache import path lookups to improve responsiveness.
[[GH-1713]](https://github.com/fatih/vim-go/pull/1713)
BUG FIXES:
@ -47,7 +67,17 @@ BUG FIXES:
* Show the file location of test errors when the message is empty or begins
with a newline.
[[GH-1664]](https://github.com/fatih/vim-go/pull/1664)
* Fix minisnip on Windows.
[[GH-1698]](https://github.com/fatih/vim-go/pull/1698)
* Keep alternate filename when loading an autocreate template.
[[GH-1675]](https://github.com/fatih/vim-go/pull/1675)
* Parse the column number in errors correctly in vim8 and neovim.
[[GH-1716]](https://github.com/fatih/vim-go/pull/1716)
* Fix race conditions in the terminal handling for neovim.
[[GH-1721]](https://github.com/fatih/vim-go/pull/1721)
* Put the user back in the original window regardless of the value of
`splitright` after starting a neovim terminal window.
[[GH-1725]](https://github.com/fatih/vim-go/pull/1725)
BACKWARDS INCOMPATIBILITIES:
@ -67,6 +97,9 @@ BACKWARDS INCOMPATIBILITIES:
[[GH-1557]](https://github.com/fatih/vim-go/pull/1557)
* Rename g`g:go_metalinter_excludes` to `g:go_metalinter_disabled`.
[[GH-1648]](https://github.com/fatih/vim-go/pull/1648)
* `:GoBuild` doesn't append the `-i` flag anymore due the recent Go 1.10
changes that introduced a build cache.
[[GH-1701]](https://github.com/fatih/vim-go/pull/1701)
## 1.16 - (December 29, 2017)

View file

@ -9,16 +9,16 @@
This plugin adds Go language support for Vim, with the following main features:
* Compile your package with `:GoBuild`, install it with `:GoInstall` or test it
with `:GoTest`. Run a single tests with `:GoTestFunc`).
with `:GoTest`. Run a single test with `:GoTestFunc`).
* Quickly execute your current file(s) with `:GoRun`.
* Improved syntax highlighting and folding.
* Debug programs with integrated `delve` support with `:GoDebugStart`.
* Completion support via `gocode`.
* `gofmt` or `goimports` on save keeps the cursor position and undo history.
* Go to symbol/declaration with `:GoDef`.
* Look up documentation with `:GoDoc` or `:GoDocBrowser`.
* Easily import packages via `:GoImport`, remove them via `:GoDrop`.
* Automatic `GOPATH` detection which works with `gb` and `godep`. Change or
display `GOPATH` with `:GoPath`.
* Precise type-safe renaming of identifiers with `:GoRename`.
* See which code is covered by tests with `:GoCoverage`.
* Add or remove tags on struct fields with `:GoAddTags` and `:GoRemoveTags`.
* Call `gometalinter` with `:GoMetaLinter` to invoke all possible linters
@ -28,7 +28,6 @@ This plugin adds Go language support for Vim, with the following main features:
errors, or make sure errors are checked with `:GoErrCheck`.
* Advanced source analysis tools utilizing `guru`, such as `:GoImplements`,
`:GoCallees`, and `:GoReferrers`.
* Precise type-safe renaming of identifiers with `:GoRename`.
* ... and many more! Please see [doc/vim-go.txt](doc/vim-go.txt) for more
information.

View file

@ -17,7 +17,7 @@ function! go#cmd#Build(bang, ...) abort
let args =
\ ["build"] +
\ map(copy(a:000), "expand(v:val)") +
\ ["-i", ".", "errors"]
\ [".", "errors"]
" Vim async.
if go#util#has_job()

View file

@ -0,0 +1,30 @@
func! Test_GoBuildErrors()
try
let l:filename = 'cmd/bad.go'
let l:tmp = gotest#load_fixture(l:filename)
exe 'cd ' . l:tmp . '/src/cmd'
" set the compiler type so that the errorformat option will be set
" correctly.
compiler go
let expected = [{'lnum': 4, 'bufnr': bufnr('%'), 'col': 2, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'undefined: notafunc'}]
" clear the quickfix lists
call setqflist([], 'r')
call go#cmd#Build(1)
let actual = getqflist()
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getqflist()
endwhile
call gotest#assert_quickfix(actual, l:expected)
finally
call delete(l:tmp, 'rf')
endtry
endfunc
" vim: sw=2 ts=2 et

View file

@ -1,58 +1,51 @@
let s:sock_type = (has('win32') || has('win64')) ? 'tcp' : 'unix'
function! s:gocodeCurrentBuffer() abort
let file = tempname()
call writefile(go#util#GetLines(), file)
return file
endfunction
function! s:gocodeCommand(cmd, preargs, args) abort
for i in range(0, len(a:args) - 1)
let a:args[i] = go#util#Shellescape(a:args[i])
endfor
for i in range(0, len(a:preargs) - 1)
let a:preargs[i] = go#util#Shellescape(a:preargs[i])
endfor
function! s:gocodeCommand(cmd, args) abort
let bin_path = go#path#CheckBinPath("gocode")
if empty(bin_path)
return
return []
endif
let socket_type = get(g:, 'go_gocode_socket_type', s:sock_type)
let cmd = [bin_path]
let cmd = extend(cmd, ['-sock', socket_type])
let cmd = extend(cmd, ['-f', 'vim'])
let cmd = extend(cmd, [a:cmd])
let cmd = extend(cmd, a:args)
return cmd
endfunction
function! s:sync_gocode(cmd, args, input) abort
" We might hit cache problems, as gocode doesn't handle different GOPATHs
" well. See: https://github.com/nsf/gocode/issues/239
let old_goroot = $GOROOT
let $GOROOT = go#util#env("goroot")
try
let socket_type = get(g:, 'go_gocode_socket_type', s:sock_type)
let cmd = printf('%s -sock %s %s %s %s',
\ go#util#Shellescape(bin_path),
\ socket_type,
\ join(a:preargs),
\ go#util#Shellescape(a:cmd),
\ join(a:args)
\ )
let cmd = s:gocodeCommand(a:cmd, a:args)
" gocode can sometimes be slow, so redraw now to avoid waiting for gocode
" to return before redrawing automatically.
redraw
let result = go#util#System(cmd)
let [l:result, l:err] = go#util#Exec(cmd, a:input)
finally
let $GOROOT = old_goroot
endtry
if go#util#ShellError() != 0
return "[\"0\", []]"
else
if l:err != 0
return "[0, []]"
endif
if &encoding != 'utf-8'
let result = iconv(result, 'utf-8', &encoding)
endif
return result
let l:result = iconv(l:result, 'utf-8', &encoding)
endif
return l:result
endfunction
function! s:gocodeCurrentBufferOpt(filename) abort
return '-in=' . a:filename
endfunction
" TODO(bc): reset when gocode isn't running
let s:optionsEnabled = 0
function! s:gocodeEnableOptions() abort
if s:optionsEnabled
@ -78,62 +71,161 @@ endfunction
function! s:gocodeAutocomplete() abort
call s:gocodeEnableOptions()
let filename = s:gocodeCurrentBuffer()
let result = s:gocodeCommand('autocomplete',
\ [s:gocodeCurrentBufferOpt(filename), '-f=vim'],
\ [expand('%:p'), go#util#OffsetCursor()])
call delete(filename)
return result
" use the offset as is, because the cursor position is the position for
" which autocomplete candidates are needed.
return s:sync_gocode('autocomplete',
\ [expand('%:p'), go#util#OffsetCursor()],
\ go#util#GetLines())
endfunction
" go#complete#GoInfo returns the description of the identifier under the
" cursor.
function! go#complete#GetInfo() abort
let offset = go#util#OffsetCursor()+1
let filename = s:gocodeCurrentBuffer()
let result = s:gocodeCommand('autocomplete',
\ [s:gocodeCurrentBufferOpt(filename), '-f=godit'],
\ [expand('%:p'), offset])
call delete(filename)
" first line is: Charcount,,NumberOfCandidates, i.e: 8,,1
" following lines are candiates, i.e: func foo(name string),,foo(
let out = split(result, '\n')
" no candidates are found
if len(out) == 1
return ""
endif
" only one candidate is found
if len(out) == 2
return split(out[1], ',,')[0]
endif
" to many candidates are available, pick one that maches the word under the
" cursor
let infos = []
for info in out[1:]
call add(infos, split(info, ',,')[0])
endfor
let wordMatch = '\<' . expand("<cword>") . '\>'
" escape single quotes in wordMatch before passing it to filter
let wordMatch = substitute(wordMatch, "'", "''", "g")
let filtered = filter(infos, "v:val =~ '".wordMatch."'")
if len(filtered) == 1
return filtered[0]
endif
return ""
return s:sync_info(0)
endfunction
function! go#complete#Info(auto) abort
if go#util#has_job()
return s:async_info(a:auto)
else
return s:sync_info(a:auto)
endif
endfunction
function! s:async_info(auto)
if exists("s:async_info_job")
call job_stop(s:async_info_job)
unlet s:async_info_job
endif
let state = {
\ 'exited': 0,
\ 'exit_status': 0,
\ 'closed': 0,
\ 'messages': [],
\ 'auto': a:auto
\ }
function! s:callback(chan, msg) dict
let l:msg = a:msg
if &encoding != 'utf-8'
let l:msg = iconv(l:msg, 'utf-8', &encoding)
endif
call add(self.messages, l:msg)
endfunction
function! s:exit_cb(job, exitval) dict
let self.exit_status = a:exitval
let self.exited = 1
if self.closed
call self.complete()
endif
endfunction
function! s:close_cb(ch) dict
let self.closed = 1
if self.exited
call self.complete()
endif
endfunction
function state.complete() dict
if self.exit_status != 0
return
endif
let result = s:info_filter(self.auto, join(self.messages, "\n"))
call s:info_complete(self.auto, result)
endfunction
" add 1 to the offset, so that the position at the cursor will be included
" in gocode's search
let offset = go#util#OffsetCursor()+1
" We might hit cache problems, as gocode doesn't handle different GOPATHs
" well. See: https://github.com/nsf/gocode/issues/239
let env = {
\ "GOROOT": go#util#env("goroot")
\ }
let cmd = s:gocodeCommand('autocomplete',
\ [expand('%:p'), offset])
" TODO(bc): Don't write the buffer to a file; pass the buffer directrly to
" gocode's stdin. It shouldn't be necessary to use {in_io: 'file', in_name:
" s:gocodeFile()}, but unfortunately {in_io: 'buffer', in_buf: bufnr('%')}
" should work.
let options = {
\ 'env': env,
\ 'in_io': 'file',
\ 'in_name': s:gocodeFile(),
\ 'callback': funcref("s:callback", [], state),
\ 'exit_cb': funcref("s:exit_cb", [], state),
\ 'close_cb': funcref("s:close_cb", [], state)
\ }
let s:async_info_job = job_start(cmd, options)
endfunction
function! s:gocodeFile()
let file = tempname()
call writefile(go#util#GetLines(), file)
return file
endfunction
function! s:sync_info(auto)
" auto is true if we were called by g:go_auto_type_info's autocmd
let result = go#complete#GetInfo()
if !empty(result)
" if auto, and the result is a PANIC by gocode, hide it
if a:auto && result ==# 'PANIC PANIC PANIC' | return | endif
echo "vim-go: " | echohl Function | echon result | echohl None
" add 1 to the offset, so that the position at the cursor will be included
" in gocode's search
let offset = go#util#OffsetCursor()+1
let result = s:sync_gocode('autocomplete',
\ [expand('%:p'), offset],
\ go#util#GetLines())
let result = s:info_filter(a:auto, result)
call s:info_complete(a:auto, result)
endfunction
function! s:info_filter(auto, result) abort
if empty(a:result)
return ""
endif
let l:result = eval(a:result)
if len(l:result) != 2
return ""
endif
let l:candidates = l:result[1]
if len(l:candidates) == 1
" When gocode panics in vim mode, it returns
" [0, [{'word': 'PANIC', 'abbr': 'PANIC PANIC PANIC', 'info': 'PANIC PANIC PANIC'}]]
if a:auto && l:candidates[0].info ==# "PANIC PANIC PANIC"
return ""
endif
return l:candidates[0].info
endif
let filtered = []
let wordMatch = '\<' . expand("<cword>") . '\>'
" escape single quotes in wordMatch before passing it to filter
let wordMatch = substitute(wordMatch, "'", "''", "g")
let filtered = filter(l:candidates, "v:val.info =~ '".wordMatch."'")
if len(l:filtered) != 1
return ""
endif
return l:filtered[0].info
endfunction
function! s:info_complete(auto, result) abort
if !empty(a:result)
echo "vim-go: " | echohl Function | echon a:result | echohl None
endif
endfunction
@ -142,20 +234,22 @@ function! s:trim_bracket(val) abort
return a:val
endfunction
let s:completions = ""
function! go#complete#Complete(findstart, base) abort
"findstart = 1 when we need to get the text length
if a:findstart == 1
execute "silent let g:gocomplete_completions = " . s:gocodeAutocomplete()
return col('.') - g:gocomplete_completions[0] - 1
execute "silent let s:completions = " . s:gocodeAutocomplete()
return col('.') - s:completions[0] - 1
"findstart = 0 when we need to return the list of completions
else
let s = getline(".")[col('.') - 1]
if s =~ '[(){}\{\}]'
return map(copy(g:gocomplete_completions[1]), 's:trim_bracket(v:val)')
return map(copy(s:completions[1]), 's:trim_bracket(v:val)')
endif
return g:gocomplete_completions[1]
return s:completions[1]
endif
endf
endfunction
function! go#complete#ToggleAutoTypeInfo() abort
if get(g:, "go_auto_type_info", 0)
@ -168,5 +262,4 @@ function! go#complete#ToggleAutoTypeInfo() abort
call go#util#EchoProgress("auto type info enabled")
endfunction
" vim: sw=2 ts=2 et

View file

@ -45,7 +45,7 @@ function! go#coverage#Buffer(bang, ...) abort
let l:tmpname = tempname()
if get(g:, 'go_echo_command_info', 1)
echon "vim-go: " | echohl Identifier | echon "testing ..." | echohl None
call go#util#EchoProgress("testing...")
endif
if go#util#has_job()

View file

@ -0,0 +1,904 @@
scriptencoding utf-8
if !exists('g:go_debug_windows')
let g:go_debug_windows = {
\ 'stack': 'leftabove 20vnew',
\ 'out': 'botright 10new',
\ 'vars': 'leftabove 30vnew',
\ }
endif
if !exists('g:go_debug_address')
let g:go_debug_address = '127.0.0.1:8181'
endif
if !exists('s:state')
let s:state = {
\ 'rpcid': 1,
\ 'running': 0,
\ 'breakpoint': {},
\ 'currentThread': {},
\ 'localVars': {},
\ 'functionArgs': {},
\ 'message': [],
\ 'is_test': 0,
\}
if go#util#HasDebug('debugger-state')
let g:go_debug_diag = s:state
endif
endif
if !exists('s:start_args')
let s:start_args = []
endif
function! s:groutineID() abort
return s:state['currentThread'].goroutineID
endfunction
function! s:exit(job, status) abort
if has_key(s:state, 'job')
call remove(s:state, 'job')
endif
call s:clearState()
if a:status > 0
call go#util#EchoError(s:state['message'])
endif
endfunction
function! s:logger(prefix, ch, msg) abort
let l:cur_win = bufwinnr('')
let l:log_win = bufwinnr(bufnr('__GODEBUG_OUTPUT__'))
if l:log_win == -1
return
endif
exe l:log_win 'wincmd w'
try
setlocal modifiable
if getline(1) == ''
call setline('$', a:prefix . a:msg)
else
call append('$', a:prefix . a:msg)
endif
normal! G
setlocal nomodifiable
finally
exe l:cur_win 'wincmd w'
endtry
endfunction
function! s:call_jsonrpc(method, ...) abort
if go#util#HasDebug('debugger-commands')
if !exists('g:go_debug_commands')
let g:go_debug_commands = []
endif
echom 'sending to dlv ' . a:method
endif
if len(a:000) > 0 && type(a:000[0]) == v:t_func
let Cb = a:000[0]
let args = a:000[1:]
else
let Cb = v:none
let args = a:000
endif
let s:state['rpcid'] += 1
let req_json = json_encode({
\ 'id': s:state['rpcid'],
\ 'method': a:method,
\ 'params': args,
\})
try
" Use callback
if type(Cb) == v:t_func
let s:ch = ch_open('127.0.0.1:8181', {'mode': 'nl', 'callback': Cb})
call ch_sendraw(s:ch, req_json)
if go#util#HasDebug('debugger-commands')
let g:go_debug_commands = add(g:go_debug_commands, {
\ 'request': req_json,
\ 'response': Cb,
\ })
endif
return
endif
let ch = ch_open('127.0.0.1:8181', {'mode': 'nl', 'timeout': 20000})
call ch_sendraw(ch, req_json)
let resp_json = ch_readraw(ch)
if go#util#HasDebug('debugger-commands')
let g:go_debug_commands = add(g:go_debug_commands, {
\ 'request': req_json,
\ 'response': resp_json,
\ })
endif
let obj = json_decode(resp_json)
if type(obj) == v:t_dict && has_key(obj, 'error') && !empty(obj.error)
throw obj.error
endif
return obj
catch
throw substitute(v:exception, '^Vim', '', '')
endtry
endfunction
" Update the location of the current breakpoint or line we're halted on based on
" response from dlv.
function! s:update_breakpoint(res) abort
if type(a:res) ==# v:t_none
return
endif
let state = a:res.result.State
if !has_key(state, 'currentThread')
return
endif
let s:state['currentThread'] = state.currentThread
let bufs = filter(map(range(1, winnr('$')), '[v:val,bufname(winbufnr(v:val))]'), 'v:val[1]=~"\.go$"')
if len(bufs) == 0
return
endif
exe bufs[0][0] 'wincmd w'
let filename = state.currentThread.file
let linenr = state.currentThread.line
let oldfile = fnamemodify(expand('%'), ':p:gs!\\!/!')
if oldfile != filename
silent! exe 'edit' filename
endif
silent! exe 'norm!' linenr.'G'
silent! normal! zvzz
silent! sign unplace 9999
silent! exe 'sign place 9999 line=' . linenr . ' name=godebugcurline file=' . filename
endfunction
" Populate the stacktrace window.
function! s:show_stacktrace(res) abort
if !has_key(a:res, 'result')
return
endif
let l:stack_win = bufwinnr(bufnr('__GODEBUG_STACKTRACE__'))
if l:stack_win == -1
return
endif
let l:cur_win = bufwinnr('')
exe l:stack_win 'wincmd w'
try
setlocal modifiable
silent %delete _
for i in range(len(a:res.result.Locations))
let loc = a:res.result.Locations[i]
call setline(i+1, printf('%s - %s:%d', loc.function.name, fnamemodify(loc.file, ':p'), loc.line))
endfor
finally
setlocal nomodifiable
exe l:cur_win 'wincmd w'
endtry
endfunction
" Populate the variable window.
function! s:show_variables() abort
let l:var_win = bufwinnr(bufnr('__GODEBUG_VARIABLES__'))
if l:var_win == -1
return
endif
let l:cur_win = bufwinnr('')
exe l:var_win 'wincmd w'
try
setlocal modifiable
silent %delete _
let v = []
let v += ['# Local Variables']
if type(get(s:state, 'localVars', [])) is type([])
for c in s:state['localVars']
let v += split(s:eval_tree(c, 0), "\n")
endfor
endif
let v += ['']
let v += ['# Function Arguments']
if type(get(s:state, 'functionArgs', [])) is type([])
for c in s:state['functionArgs']
let v += split(s:eval_tree(c, 0), "\n")
endfor
endif
call setline(1, v)
finally
setlocal nomodifiable
exe l:cur_win 'wincmd w'
endtry
endfunction
function! s:clearState() abort
let s:state['currentThread'] = {}
let s:state['localVars'] = {}
let s:state['functionArgs'] = {}
let s:state['message'] = []
silent! sign unplace 9999
endfunction
function! s:stop() abort
call s:clearState()
if has_key(s:state, 'job')
call job_stop(s:state['job'])
call remove(s:state, 'job')
endif
endfunction
function! go#debug#Stop() abort
" Remove signs.
for k in keys(s:state['breakpoint'])
let bt = s:state['breakpoint'][k]
if bt.id >= 0
silent exe 'sign unplace ' . bt.id
endif
endfor
" Remove all commands and add back the default commands.
for k in map(split(execute('command GoDebug'), "\n")[1:], 'matchstr(v:val, "^\\s*\\zs\\S\\+")')
exe 'delcommand' k
endfor
command! -nargs=* -complete=customlist,go#package#Complete GoDebugStart call go#debug#Start(0, <f-args>)
command! -nargs=* -complete=customlist,go#package#Complete GoDebugTest call go#debug#Start(1, <f-args>)
command! -nargs=? GoDebugBreakpoint call go#debug#Breakpoint(<f-args>)
" Remove all mappings.
for k in map(split(execute('map <Plug>(go-debug-'), "\n")[1:], 'matchstr(v:val, "^n\\s\\+\\zs\\S\\+")')
exe 'unmap' k
endfor
call s:stop()
let bufs = filter(map(range(1, winnr('$')), '[v:val,bufname(winbufnr(v:val))]'), 'v:val[1]=~"\.go$"')
if len(bufs) > 0
exe bufs[0][0] 'wincmd w'
else
wincmd p
endif
silent! exe bufwinnr(bufnr('__GODEBUG_STACKTRACE__')) 'wincmd c'
silent! exe bufwinnr(bufnr('__GODEBUG_VARIABLES__')) 'wincmd c'
silent! exe bufwinnr(bufnr('__GODEBUG_OUTPUT__')) 'wincmd c'
set noballooneval
set balloonexpr=
endfunction
function! s:goto_file() abort
let m = matchlist(getline('.'), ' - \(.*\):\([0-9]\+\)$')
if m[1] == ''
return
endif
let bufs = filter(map(range(1, winnr('$')), '[v:val,bufname(winbufnr(v:val))]'), 'v:val[1]=~"\.go$"')
if len(bufs) == 0
return
endif
exe bufs[0][0] 'wincmd w'
let filename = m[1]
let linenr = m[2]
let oldfile = fnamemodify(expand('%'), ':p:gs!\\!/!')
if oldfile != filename
silent! exe 'edit' filename
endif
silent! exe 'norm!' linenr.'G'
silent! normal! zvzz
endfunction
function! s:delete_expands()
let nr = line('.')
while 1
let l = getline(nr+1)
if empty(l) || l =~ '^\S'
return
endif
silent! exe (nr+1) . 'd _'
endwhile
silent! exe 'norm!' nr.'G'
endfunction
function! s:expand_var() abort
" Get name from struct line.
let name = matchstr(getline('.'), '^[^:]\+\ze: [a-zA-Z0-9\.·]\+{\.\.\.}$')
" Anonymous struct
if name == ''
let name = matchstr(getline('.'), '^[^:]\+\ze: struct {.\{-}}$')
endif
if name != ''
setlocal modifiable
let not_open = getline(line('.')+1) !~ '^ '
let l = line('.')
call s:delete_expands()
if not_open
call append(l, split(s:eval(name), "\n")[1:])
endif
silent! exe 'norm!' l.'G'
setlocal nomodifiable
return
endif
" Expand maps
let m = matchlist(getline('.'), '^[^:]\+\ze: map.\{-}\[\(\d\+\)\]$')
if len(m) > 0 && m[1] != ''
setlocal modifiable
let not_open = getline(line('.')+1) !~ '^ '
let l = line('.')
call s:delete_expands()
if not_open
" TODO: Not sure how to do this yet... Need to get keys of the map.
" let vs = ''
" for i in range(0, min([10, m[1]-1]))
" let vs .= ' ' . s:eval(printf("%s[%s]", m[0], ))
" endfor
" call append(l, split(vs, "\n"))
endif
silent! exe 'norm!' l.'G'
setlocal nomodifiable
return
endif
" Expand string.
let m = matchlist(getline('.'), '^\([^:]\+\)\ze: \(string\)\[\([0-9]\+\)\]\(: .\{-}\)\?$')
if len(m) > 0 && m[1] != ''
setlocal modifiable
let not_open = getline(line('.')+1) !~ '^ '
let l = line('.')
call s:delete_expands()
if not_open
let vs = ''
for i in range(0, min([10, m[3]-1]))
let vs .= ' ' . s:eval(m[1] . '[' . i . ']')
endfor
call append(l, split(vs, "\n"))
endif
silent! exe 'norm!' l.'G'
setlocal nomodifiable
return
endif
" Expand slice.
let m = matchlist(getline('.'), '^\([^:]\+\)\ze: \(\[\]\w\{-}\)\[\([0-9]\+\)\]$')
if len(m) > 0 && m[1] != ''
setlocal modifiable
let not_open = getline(line('.')+1) !~ '^ '
let l = line('.')
call s:delete_expands()
if not_open
let vs = ''
for i in range(0, min([10, m[3]-1]))
let vs .= ' ' . s:eval(m[1] . '[' . i . ']')
endfor
call append(l, split(vs, "\n"))
endif
silent! exe 'norm!' l.'G'
setlocal nomodifiable
return
endif
endfunction
function! s:start_cb(ch, json) abort
let res = json_decode(a:json)
if type(res) == v:t_dict && has_key(res, 'error') && !empty(res.error)
throw res.error
endif
if empty(res) || !has_key(res, 'result')
return
endif
for bt in res.result.Breakpoints
if bt.id >= 0
let s:state['breakpoint'][bt.id] = bt
exe 'sign place '. bt.id .' line=' . bt.line . ' name=godebugbreakpoint file=' . bt.file
endif
endfor
let oldbuf = bufnr('%')
silent! only!
let winnum = bufwinnr(bufnr('__GODEBUG_STACKTRACE__'))
if winnum != -1
return
endif
if exists('g:go_debug_windows["stack"]') && g:go_debug_windows['stack'] != ''
exe 'silent ' . g:go_debug_windows['stack']
silent file `='__GODEBUG_STACKTRACE__'`
setlocal buftype=nofile bufhidden=wipe nomodified nobuflisted noswapfile nowrap nonumber nocursorline
setlocal filetype=godebugstacktrace
nmap <buffer> <cr> :<c-u>call <SID>goto_file()<cr>
nmap <buffer> q <Plug>(go-debug-stop)
endif
if exists('g:go_debug_windows["out"]') && g:go_debug_windows['out'] != ''
exe 'silent ' . g:go_debug_windows['out']
silent file `='__GODEBUG_OUTPUT__'`
setlocal buftype=nofile bufhidden=wipe nomodified nobuflisted noswapfile nowrap nonumber nocursorline
setlocal filetype=godebugoutput
nmap <buffer> q <Plug>(go-debug-stop)
endif
if exists('g:go_debug_windows["vars"]') && g:go_debug_windows['vars'] != ''
exe 'silent ' . g:go_debug_windows['vars']
silent file `='__GODEBUG_VARIABLES__'`
setlocal buftype=nofile bufhidden=wipe nomodified nobuflisted noswapfile nowrap nonumber nocursorline
setlocal filetype=godebugvariables
call append(0, ["# Local Variables", "", "# Function Arguments"])
nmap <buffer> <silent> <cr> :<c-u>call <SID>expand_var()<cr>
nmap <buffer> q <Plug>(go-debug-stop)
endif
silent! delcommand GoDebugStart
silent! delcommand GoDebugTest
command! -nargs=0 GoDebugContinue call go#debug#Stack('continue')
command! -nargs=0 GoDebugNext call go#debug#Stack('next')
command! -nargs=0 GoDebugStep call go#debug#Stack('step')
command! -nargs=0 GoDebugStepOut call go#debug#Stack('stepOut')
command! -nargs=0 GoDebugRestart call go#debug#Restart()
command! -nargs=0 GoDebugStop call go#debug#Stop()
command! -nargs=* GoDebugSet call go#debug#Set(<f-args>)
command! -nargs=1 GoDebugPrint call go#debug#Print(<q-args>)
nnoremap <silent> <Plug>(go-debug-breakpoint) :<C-u>call go#debug#Breakpoint()<CR>
nnoremap <silent> <Plug>(go-debug-next) :<C-u>call go#debug#Stack('next')<CR>
nnoremap <silent> <Plug>(go-debug-step) :<C-u>call go#debug#Stack('step')<CR>
nnoremap <silent> <Plug>(go-debug-stepout) :<C-u>call go#debug#Stack('stepout')<CR>
nnoremap <silent> <Plug>(go-debug-continue) :<C-u>call go#debug#Stack('continue')<CR>
nnoremap <silent> <Plug>(go-debug-stop) :<C-u>call go#debug#Stop()<CR>
nnoremap <silent> <Plug>(go-debug-print) :<C-u>call go#debug#Print(expand('<cword>'))<CR>
nmap <F5> <Plug>(go-debug-continue)
nmap <F6> <Plug>(go-debug-print)
nmap <F9> <Plug>(go-debug-breakpoint)
nmap <F10> <Plug>(go-debug-next)
nmap <F11> <Plug>(go-debug-step)
set balloonexpr=go#debug#BalloonExpr()
set ballooneval
exe bufwinnr(oldbuf) 'wincmd w'
endfunction
function! s:err_cb(ch, msg) abort
call go#util#EchoError(a:msg)
let s:state['message'] += [a:msg]
endfunction
function! s:out_cb(ch, msg) abort
call go#util#EchoProgress(a:msg)
let s:state['message'] += [a:msg]
" TODO: why do this in this callback?
if stridx(a:msg, g:go_debug_address) != -1
call ch_setoptions(a:ch, {
\ 'out_cb': function('s:logger', ['OUT: ']),
\ 'err_cb': function('s:logger', ['ERR: ']),
\})
" Tell dlv about the breakpoints that the user added before delve started.
let l:breaks = copy(s:state.breakpoint)
let s:state['breakpoint'] = {}
for l:bt in values(l:breaks)
call go#debug#Breakpoint(bt.line)
endfor
call s:call_jsonrpc('RPCServer.ListBreakpoints', function('s:start_cb'))
endif
endfunction
" Start the debug mode. The first argument is the package name to compile and
" debug, anything else will be passed to the running program.
function! go#debug#Start(is_test, ...) abort
if has('nvim')
call go#util#EchoError('This feature only works in Vim for now; Neovim is not (yet) supported. Sorry :-(')
return
endif
if !go#util#has_job()
call go#util#EchoError('This feature requires Vim 8.0.0087 or newer with +job.')
return
endif
" It's already running.
if has_key(s:state, 'job') && job_status(s:state['job']) == 'run'
return
endif
let s:start_args = a:000
if go#util#HasDebug('debugger-state')
let g:go_debug_diag = s:state
endif
" cd in to test directory; this is also what running "go test" does.
if a:is_test
lcd %:p:h
endif
let s:state.is_test = a:is_test
let dlv = go#path#CheckBinPath("dlv")
if empty(dlv)
return
endif
try
if len(a:000) > 0
let l:pkgname = a:1
" Expand .; otherwise this won't work from a tmp dir.
if l:pkgname[0] == '.'
let l:pkgname = go#package#FromPath(getcwd()) . l:pkgname[1:]
endif
else
let l:pkgname = go#package#FromPath(getcwd())
endif
let l:args = []
if len(a:000) > 1
let l:args = ['--'] + a:000[1:]
endif
let l:cmd = [
\ dlv,
\ (a:is_test ? 'test' : 'debug'),
\ '--output', tempname(),
\ '--headless',
\ '--api-version', '2',
\ '--log',
\ '--listen', g:go_debug_address,
\ '--accept-multiclient',
\]
if get(g:, 'go_build_tags', '') isnot ''
let l:cmd += ['--build-flags', '--tags=' . g:go_build_tags]
endif
let l:cmd += l:args
call go#util#EchoProgress('Starting GoDebug...')
let s:state['message'] = []
let s:state['job'] = job_start(l:cmd, {
\ 'out_cb': function('s:out_cb'),
\ 'err_cb': function('s:err_cb'),
\ 'exit_cb': function('s:exit'),
\ 'stoponexit': 'kill',
\})
catch
call go#util#EchoError(v:exception)
endtry
endfunction
" Translate a reflect kind constant to a human string.
function! s:reflect_kind(k)
" Kind constants from Go's reflect package.
return [
\ 'Invalid Kind',
\ 'Bool',
\ 'Int',
\ 'Int8',
\ 'Int16',
\ 'Int32',
\ 'Int64',
\ 'Uint',
\ 'Uint8',
\ 'Uint16',
\ 'Uint32',
\ 'Uint64',
\ 'Uintptr',
\ 'Float32',
\ 'Float64',
\ 'Complex64',
\ 'Complex128',
\ 'Array',
\ 'Chan',
\ 'Func',
\ 'Interface',
\ 'Map',
\ 'Ptr',
\ 'Slice',
\ 'String',
\ 'Struct',
\ 'UnsafePointer',
\ ][a:k]
endfunction
function! s:eval_tree(var, nest) abort
if a:var.name =~ '^\~'
return ''
endif
let nest = a:nest
let v = ''
let kind = s:reflect_kind(a:var.kind)
if !empty(a:var.name)
let v .= repeat(' ', nest) . a:var.name . ': '
if kind == 'Bool'
let v .= printf("%s\n", a:var.value)
elseif kind == 'Struct'
" Anonymous struct
if a:var.type[:8] == 'struct { '
let v .= printf("%s\n", a:var.type)
else
let v .= printf("%s{...}\n", a:var.type)
endif
elseif kind == 'String'
let v .= printf("%s[%d]%s\n", a:var.type, a:var.len,
\ len(a:var.value) > 0 ? ': ' . a:var.value : '')
elseif kind == 'Slice' || kind == 'String' || kind == 'Map' || kind == 'Array'
let v .= printf("%s[%d]\n", a:var.type, a:var.len)
elseif kind == 'Chan' || kind == 'Func' || kind == 'Interface'
let v .= printf("%s\n", a:var.type)
elseif kind == 'Ptr'
" TODO: We can do something more useful here.
let v .= printf("%s\n", a:var.type)
elseif kind == 'Complex64' || kind == 'Complex128'
let v .= printf("%s%s\n", a:var.type, a:var.value)
" Int, Float
else
let v .= printf("%s(%s)\n", a:var.type, a:var.value)
endif
else
let nest -= 1
endif
if index(['Chan', 'Complex64', 'Complex128'], kind) == -1 && a:var.type != 'error'
for c in a:var.children
let v .= s:eval_tree(c, nest+1)
endfor
endif
return v
endfunction
function! s:eval(arg) abort
try
let res = s:call_jsonrpc('RPCServer.State')
let goroutineID = res.result.State.currentThread.goroutineID
let res = s:call_jsonrpc('RPCServer.Eval', {
\ 'expr': a:arg,
\ 'scope': {'GoroutineID': goroutineID}
\ })
return s:eval_tree(res.result.Variable, 0)
catch
call go#util#EchoError(v:exception)
return ''
endtry
endfunction
function! go#debug#BalloonExpr() abort
silent! let l:v = s:eval(v:beval_text)
return l:v
endfunction
function! go#debug#Print(arg) abort
try
echo substitute(s:eval(a:arg), "\n$", "", 0)
catch
call go#util#EchoError(v:exception)
endtry
endfunction
function! s:update_variables() abort
" FollowPointers requests pointers to be automatically dereferenced.
" MaxVariableRecurse is how far to recurse when evaluating nested types.
" MaxStringLen is the maximum number of bytes read from a string
" MaxArrayValues is the maximum number of elements read from an array, a slice or a map.
" MaxStructFields is the maximum number of fields read from a struct, -1 will read all fields.
let l:cfg = {
\ 'scope': {'GoroutineID': s:groutineID()},
\ 'cfg': {'MaxStringLen': 20, 'MaxArrayValues': 20}
\ }
try
let res = s:call_jsonrpc('RPCServer.ListLocalVars', l:cfg)
let s:state['localVars'] = res.result['Variables']
catch
call go#util#EchoError(v:exception)
endtry
try
let res = s:call_jsonrpc('RPCServer.ListFunctionArgs', l:cfg)
let s:state['functionArgs'] = res.result['Args']
catch
call go#util#EchoError(v:exception)
endtry
call s:show_variables()
endfunction
function! go#debug#Set(symbol, value) abort
try
let res = s:call_jsonrpc('RPCServer.State')
let goroutineID = res.result.State.currentThread.goroutineID
call s:call_jsonrpc('RPCServer.Set', {
\ 'symbol': a:symbol,
\ 'value': a:value,
\ 'scope': {'GoroutineID': goroutineID}
\ })
catch
call go#util#EchoError(v:exception)
endtry
call s:update_variables()
endfunction
function! s:update_stacktrace() abort
try
let res = s:call_jsonrpc('RPCServer.Stacktrace', {'id': s:groutineID(), 'depth': 5})
call s:show_stacktrace(res)
catch
call go#util#EchoError(v:exception)
endtry
endfunction
function! s:stack_cb(ch, json) abort
let s:stack_name = ''
let res = json_decode(a:json)
if type(res) == v:t_dict && has_key(res, 'error') && !empty(res.error)
call go#util#EchoError(res.error)
call s:clearState()
call go#debug#Restart()
return
endif
if empty(res) || !has_key(res, 'result')
return
endif
call s:update_breakpoint(res)
call s:update_stacktrace()
call s:update_variables()
endfunction
" Send a command to change the cursor location to Delve.
"
" a:name must be one of continue, next, step, or stepOut.
function! go#debug#Stack(name) abort
let l:name = a:name
" Run continue if the program hasn't started yet.
if s:state.running is 0
let s:state.running = 1
let l:name = 'continue'
endif
" Add a breakpoint to the main.Main if the user didn't define any.
if len(s:state['breakpoint']) is 0
if go#debug#Breakpoint() isnot 0
let s:state.running = 0
return
endif
endif
try
" TODO: document why this is needed.
if l:name is# 'next' && get(s:, 'stack_name', '') is# 'next'
call s:call_jsonrpc('RPCServer.CancelNext')
endif
let s:stack_name = l:name
call s:call_jsonrpc('RPCServer.Command', function('s:stack_cb'), {'name': l:name})
catch
call go#util#EchoError(v:exception)
endtry
endfunction
function! go#debug#Restart() abort
try
call job_stop(s:state['job'])
while has_key(s:state, 'job') && job_status(s:state['job']) is# 'run'
sleep 50m
endwhile
let l:breaks = s:state['breakpoint']
let s:state = {
\ 'rpcid': 1,
\ 'running': 0,
\ 'breakpoint': {},
\ 'currentThread': {},
\ 'localVars': {},
\ 'functionArgs': {},
\ 'message': [],
\}
" Preserve breakpoints.
for bt in values(l:breaks)
" TODO: should use correct filename
exe 'sign unplace '. bt.id .' file=' . bt.file
call go#debug#Breakpoint(bt.line)
endfor
call call('go#debug#Start', s:start_args)
catch
call go#util#EchoError(v:exception)
endtry
endfunction
" Report if debugger mode is active.
function! s:isActive()
return len(s:state['message']) > 0
endfunction
" Toggle breakpoint. Returns 0 on success and 1 on failure.
function! go#debug#Breakpoint(...) abort
let l:filename = fnamemodify(expand('%'), ':p:gs!\\!/!')
" Get line number from argument.
if len(a:000) > 0
let linenr = str2nr(a:1)
if linenr is 0
call go#util#EchoError('not a number: ' . a:1)
return 0
endif
else
let linenr = line('.')
endif
try
" Check if we already have a breakpoint for this line.
let found = v:none
for k in keys(s:state.breakpoint)
let bt = s:state.breakpoint[k]
if bt.file == l:filename && bt.line == linenr
let found = bt
break
endif
endfor
" Remove breakpoint.
if type(found) == v:t_dict
call remove(s:state['breakpoint'], bt.id)
exe 'sign unplace '. found.id .' file=' . found.file
if s:isActive()
let res = s:call_jsonrpc('RPCServer.ClearBreakpoint', {'id': found.id})
endif
" Add breakpoint.
else
if s:isActive()
let res = s:call_jsonrpc('RPCServer.CreateBreakpoint', {'Breakpoint': {'file': l:filename, 'line': linenr}})
let bt = res.result.Breakpoint
exe 'sign place '. bt.id .' line=' . bt.line . ' name=godebugbreakpoint file=' . bt.file
let s:state['breakpoint'][bt.id] = bt
else
let id = len(s:state['breakpoint']) + 1
let s:state['breakpoint'][id] = {'id': id, 'file': l:filename, 'line': linenr}
exe 'sign place '. id .' line=' . linenr . ' name=godebugbreakpoint file=' . l:filename
endif
endif
catch
call go#util#EchoError(v:exception)
return 1
endtry
return 0
endfunction
sign define godebugbreakpoint text=> texthl=GoDebugBreakpoint
sign define godebugcurline text== linehl=GoDebugCurrent texthl=GoDebugCurrent
fun! s:hi()
hi GoDebugBreakpoint term=standout ctermbg=117 ctermfg=0 guibg=#BAD4F5 guifg=Black
hi GoDebugCurrent term=reverse ctermbg=12 ctermfg=7 guibg=DarkBlue guifg=White
endfun
augroup vim-go-breakpoint
autocmd!
autocmd ColorScheme * call s:hi()
augroup end
call s:hi()
" vim: sw=2 ts=2 et

View file

@ -148,7 +148,6 @@ function! go#fmt#update_file(source, target)
if has_key(l:list_title, "title") && l:list_title['title'] == "Format"
call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
endif
endfunction

View file

@ -78,7 +78,7 @@ function! s:guru_cmd(args) range abort
let scopes = go#util#StripTrailingSlash(scopes)
" create shell-safe entries of the list
if !go#util#has_job() | let scopes = go#util#Shelllist(scopes) | endif
if !has("nvim") && !go#util#has_job() | let scopes = go#util#Shelllist(scopes) | endif
" guru expect a comma-separated list of patterns, construct it
let l:scope = join(scopes, ",")
@ -129,7 +129,7 @@ function! s:sync_guru(args) abort
endif
if has_key(a:args, 'custom_parse')
call a:args.custom_parse(go#util#ShellError(), out)
call a:args.custom_parse(go#util#ShellError(), out, a:args.mode)
else
call s:parse_guru_output(go#util#ShellError(), out, a:args.mode)
endif
@ -137,6 +137,33 @@ function! s:sync_guru(args) abort
return out
endfunc
" use vim or neovim job api as appropriate
function! s:job_start(cmd, start_options) abort
if go#util#has_job()
return job_start(a:cmd, a:start_options)
endif
let opts = {'stdout_buffered': v:true, 'stderr_buffered': v:true}
function opts.on_stdout(job_id, data, event) closure
call a:start_options.callback(a:job_id, join(a:data, "\n"))
endfunction
function opts.on_stderr(job_id, data, event) closure
call a:start_options.callback(a:job_id, join(a:data, "\n"))
endfunction
function opts.on_exit(job_id, exit_code, event) closure
call a:start_options.exit_cb(a:job_id, a:exit_code)
call a:start_options.close_cb(a:job_id)
endfunction
" use a shell for input redirection if needed
let cmd = a:cmd
if has_key(a:start_options, 'in_io') && a:start_options.in_io ==# 'file' && !empty(a:start_options.in_name)
let cmd = ['/bin/sh', '-c', join(a:cmd, ' ') . ' <' . a:start_options.in_name]
endif
return jobstart(cmd, opts)
endfunction
" async_guru runs guru in async mode with the given arguments
function! s:async_guru(args) abort
let result = s:guru_cmd(a:args)
@ -145,8 +172,6 @@ function! s:async_guru(args) abort
return
endif
let status_dir = expand('%:p:h')
let statusline_type = printf("%s", a:args.mode)
if !has_key(a:args, 'disable_progress')
if a:args.needs_scope
@ -155,59 +180,63 @@ function! s:async_guru(args) abort
endif
endif
let messages = []
function! s:callback(chan, msg) closure
call add(messages, a:msg)
let state = {
\ 'status_dir': expand('%:p:h'),
\ 'statusline_type': printf("%s", a:args.mode),
\ 'mode': a:args.mode,
\ 'status': {},
\ 'exitval': 0,
\ 'closed': 0,
\ 'exited': 0,
\ 'messages': [],
\ 'parse' : get(a:args, 'custom_parse', funcref("s:parse_guru_output"))
\ }
function! s:callback(chan, msg) dict
call add(self.messages, a:msg)
endfunction
let status = {}
let exitval = 0
let closed = 0
let exited = 0
function! s:exit_cb(job, exitval) closure
let exited = 1
function! s:exit_cb(job, exitval) dict
let self.exited = 1
let status = {
\ 'desc': 'last status',
\ 'type': statusline_type,
\ 'type': self.statusline_type,
\ 'state': "finished",
\ }
if a:exitval
let exitval = a:exitval
let self.exitval = a:exitval
let status.state = "failed"
endif
call go#statusline#Update(status_dir, status)
call go#statusline#Update(self.status_dir, status)
if closed
call s:complete()
if self.closed
call self.complete()
endif
endfunction
function! s:close_cb(ch) closure
let closed = 1
function! s:close_cb(ch) dict
let self.closed = 1
if exited
call s:complete()
if self.exited
call self.complete()
endif
endfunction
function! s:complete() closure
let out = join(messages, "\n")
function state.complete() dict
let out = join(self.messages, "\n")
if has_key(a:args, 'custom_parse')
call a:args.custom_parse(exitval, out)
else
call s:parse_guru_output(exitval, out, a:args.mode)
endif
call self.parse(self.exitval, out, self.mode)
endfunction
" explicitly bind the callbacks to state so that self within them always
" refers to state. See :help Partial for more information.
let start_options = {
\ 'callback': funcref("s:callback"),
\ 'exit_cb': funcref("s:exit_cb"),
\ 'close_cb': funcref("s:close_cb"),
\ 'callback': function('s:callback', [], state),
\ 'exit_cb': function('s:exit_cb', [], state),
\ 'close_cb': function('s:close_cb', [], state)
\ }
if has_key(result, 'stdin_content')
@ -217,18 +246,18 @@ function! s:async_guru(args) abort
let l:start_options.in_name = l:tmpname
endif
call go#statusline#Update(status_dir, {
call go#statusline#Update(state.status_dir, {
\ 'desc': "current status",
\ 'type': statusline_type,
\ 'type': state.statusline_type,
\ 'state': "analysing",
\})
return job_start(result.cmd, start_options)
return s:job_start(result.cmd, start_options)
endfunc
" run_guru runs the given guru argument
function! s:run_guru(args) abort
if go#util#has_job()
if has('nvim') || go#util#has_job()
let res = s:async_guru(a:args)
else
let res = s:sync_guru(a:args)
@ -289,7 +318,7 @@ function! go#guru#DescribeInfo() abort
return
endif
function! s:info(exit_val, output)
function! s:info(exit_val, output, mode)
if a:exit_val != 0
return
endif
@ -464,10 +493,6 @@ function! go#guru#Referrers(selected) abort
call s:run_guru(args)
endfunction
function! go#guru#SameIdsTimer() abort
call timer_start(200, function('go#guru#SameIds'), {'repeat': -1})
endfunction
function! go#guru#SameIds() abort
" we use matchaddpos() which was introduce with 7.4.330, be sure we have
" it: http://ftp.vim.org/vim/patches/7.4/7.4.330
@ -495,7 +520,7 @@ function! go#guru#SameIds() abort
call s:run_guru(args)
endfunction
function! s:same_ids_highlight(exit_val, output) abort
function! s:same_ids_highlight(exit_val, output, mode) abort
call go#guru#ClearSameIds() " run after calling guru to reduce flicker.
if a:output[0] !=# '{'

View file

@ -19,10 +19,6 @@
" list of messages received from the channel. The default value will
" process the messages and manage the error list after the job exits and
" the channel is closed.
" 'callback':
" A function to call when there is a message to read from the job's
" channel. The function will be passed two arguments: the channel and a
" message. See job-callback.
" The return value is a dictionary with these keys:
" 'callback':
@ -36,43 +32,46 @@
" job-close_cb.
function go#job#Spawn(args)
let cbs = {}
let winnr = winnr()
let dir = getcwd()
let jobdir = fnameescape(expand("%:p:h"))
let messages = []
let args = a:args.cmd
let bang = 0
let for = "_job"
let state = {
\ 'winnr': winnr(),
\ 'dir': getcwd(),
\ 'jobdir': fnameescape(expand("%:p:h")),
\ 'messages': [],
\ 'args': a:args.cmd,
\ 'bang': 0,
\ 'for': "_job",
\ 'exited': 0,
\ 'exit_status': 0,
\ 'closed': 0,
\ 'errorformat': &errorformat
\ }
if has_key(a:args, 'bang')
let l:bang = a:args.bang
let state.bang = a:args.bang
endif
if has_key(a:args, 'for')
let l:for = a:args.for
let state.for = a:args.for
endif
let l:exited = 0
let l:exit_status = 0
let l:closed = 0
function! s:NopComplete(job, exit_status, data)
" do nothing in state.complete by default.
function state.complete(job, exit_status, data)
endfunction
let Complete = funcref('s:NopComplete')
if has_key(a:args, 'complete')
let Complete = a:args.complete
let state.complete = a:args.complete
endif
function cbs.callback(chan, msg) dict closure
call add(messages, a:msg)
function! s:callback(chan, msg) dict
call add(self.messages, a:msg)
endfunction
" explicitly bind callback to state so that within it, self will
" always refer to state. See :help Partial for more information.
let cbs.callback = function('s:callback', [], state)
function cbs.exit_cb(job, exitval) dict closure
let exit_status = a:exitval
let exited = 1
function! s:exit_cb(job, exitval) dict
let self.exit_status = a:exitval
let self.exited = 1
if get(g:, 'go_echo_command_info', 1)
if a:exitval == 0
@ -82,56 +81,63 @@ function go#job#Spawn(args)
endif
endif
if closed
call Complete(a:job, exit_status, messages)
call s:show_errors(a:job, exit_status, messages)
if self.closed
call self.complete(a:job, self.exit_status, self.messages)
call self.show_errors(a:job, self.exit_status, self.messages)
endif
endfunction
" explicitly bind exit_cb to state so that within it, self will always refer
" to state. See :help Partial for more information.
let cbs.exit_cb = function('s:exit_cb', [], state)
function cbs.close_cb(ch) dict closure
let closed = 1
function! s:close_cb(ch) dict
let self.closed = 1
if exited
if self.exited
let job = ch_getjob(a:ch)
call Complete(job, exit_status, messages)
call s:show_errors(job, exit_status, messages)
call self.complete(job, self.exit_status, self.messages)
call self.show_errors(job, self.exit_status, self.messages)
endif
endfunction
" explicitly bind close_cb to state so that within it, self will
" always refer to state. See :help Partial for more information.
let cbs.close_cb = function('s:close_cb', [], state)
function! s:show_errors(job, exit_status, data) closure
let l:listtype = go#list#Type(for)
function state.show_errors(job, exit_status, data)
let l:listtype = go#list#Type(self.for)
if a:exit_status == 0
call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
return
endif
let l:listtype = go#list#Type(for)
let l:listtype = go#list#Type(self.for)
if len(a:data) == 0
call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
return
endif
let out = join(self.messages, "\n")
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
try
execute cd jobdir
let errors = go#tool#ParseErrors(a:data)
let errors = go#tool#FilterValids(errors)
" parse the errors relative to self.jobdir
execute cd self.jobdir
call go#list#ParseFormat(l:listtype, self.errorformat, out, self.for)
let errors = go#list#Get(l:listtype)
finally
execute cd . fnameescape(dir)
execute cd . fnameescape(self.dir)
endtry
if empty(errors)
" failed to parse errors, output the original content
call go#util#EchoError(messages + [dir])
call go#util#EchoError(self.messages + [self.dir])
return
endif
if winnr == winnr()
call go#list#Populate(l:listtype, errors, join(args))
if self.winnr == winnr()
call go#list#Window(l:listtype, len(errors))
if !bang
if !self.bang
call go#list#JumpToFirst(l:listtype)
endif
endif

View file

@ -62,6 +62,7 @@ function! s:spawn(bang, desc, for, args) abort
\ 'status_dir' : status_dir,
\ 'started_at' : started_at,
\ 'for' : a:for,
\ 'errorformat': &errorformat,
\ }
" execute go build in the files directory
@ -125,7 +126,6 @@ function! s:on_exit(job_id, exit_status, event) dict abort
let l:listtype = go#list#Type(self.for)
if a:exit_status == 0
call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
let self.state = "SUCCESS"
@ -143,8 +143,9 @@ function! s:on_exit(job_id, exit_status, event) dict abort
call go#util#EchoError("[" . self.status_type . "] FAILED")
endif
let errors = go#tool#ParseErrors(std_combined)
let errors = go#tool#FilterValids(errors)
" parse the errors relative to self.jobdir
call go#list#ParseFormat(l:listtype, self.errorformat, std_combined, self.for)
let errors = go#list#Get(l:listtype)
execute cd . fnameescape(dir)
@ -156,7 +157,6 @@ function! s:on_exit(job_id, exit_status, event) dict abort
" if we are still in the same windows show the list
if self.winnr == winnr()
call go#list#Populate(l:listtype, errors, self.desc)
call go#list#Window(l:listtype, len(errors))
if !empty(errors) && !self.bang
call go#list#JumpToFirst(l:listtype)

View file

@ -105,7 +105,6 @@ function! go#lint#Gometa(autosave, ...) abort
if l:err == 0
call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
echon "vim-go: " | echohl Function | echon "[metalinter] PASS" | echohl None
else
" GoMetaLinter can output one of the two, so we look for both:
@ -147,7 +146,7 @@ function! go#lint#Golint(...) abort
endif
let l:listtype = go#list#Type("GoLint")
call go#list#Parse(l:listtype, out)
call go#list#Parse(l:listtype, out, "GoLint")
let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors))
call go#list#JumpToFirst(l:listtype)
@ -166,8 +165,9 @@ function! go#lint#Vet(bang, ...) abort
let l:listtype = go#list#Type("GoVet")
if go#util#ShellError() != 0
let errors = go#tool#ParseErrors(split(out, '\n'))
call go#list#Populate(l:listtype, errors, 'Vet')
let errorformat="%-Gexit status %\\d%\\+," . &errorformat
call go#list#ParseFormat(l:listtype, l:errorformat, out, "GoVet")
let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors))
if !empty(errors) && !a:bang
call go#list#JumpToFirst(l:listtype)
@ -175,7 +175,6 @@ function! go#lint#Vet(bang, ...) abort
echon "vim-go: " | echohl ErrorMsg | echon "[vet] FAIL" | echohl None
else
call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
redraw | echon "vim-go: " | echohl Function | echon "[vet] PASS" | echohl None
endif
endfunction
@ -228,7 +227,6 @@ function! go#lint#Errcheck(...) abort
endif
else
call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
echon "vim-go: " | echohl Function | echon "[errcheck] PASS" | echohl None
endif
@ -246,10 +244,18 @@ function! go#lint#ToggleMetaLinterAutoSave() abort
endfunction
function! s:lint_job(args, autosave)
let status_dir = expand('%:p:h')
let started_at = reltime()
let state = {
\ 'status_dir': expand('%:p:h'),
\ 'started_at': reltime(),
\ 'messages': [],
\ 'exited': 0,
\ 'closed': 0,
\ 'exit_status': 0,
\ 'winnr': winnr(),
\ 'autosave': a:autosave
\ }
call go#statusline#Update(status_dir, {
call go#statusline#Update(state.status_dir, {
\ 'desc': "current status",
\ 'type': "gometalinter",
\ 'state': "analysing",
@ -259,26 +265,18 @@ function! s:lint_job(args, autosave)
call go#cmd#autowrite()
if a:autosave
let l:listtype = go#list#Type("GoMetaLinterAutoSave")
let state.listtype = go#list#Type("GoMetaLinterAutoSave")
else
let l:listtype = go#list#Type("GoMetaLinter")
let state.listtype = go#list#Type("GoMetaLinter")
endif
let l:errformat = '%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m'
let l:messages = []
let l:exited = 0
let l:closed = 0
let l:exit_status = 0
let l:winnr = winnr()
function! s:callback(chan, msg) closure
call add(messages, a:msg)
function! s:callback(chan, msg) dict closure
call add(self.messages, a:msg)
endfunction
function! s:exit_cb(job, exitval) closure
let exited = 1
let exit_status = a:exitval
function! s:exit_cb(job, exitval) dict
let self.exited = 1
let self.exit_status = a:exitval
let status = {
\ 'desc': 'last status',
@ -290,50 +288,63 @@ function! s:lint_job(args, autosave)
let status.state = "failed"
endif
let elapsed_time = reltimestr(reltime(started_at))
let elapsed_time = reltimestr(reltime(self.started_at))
" strip whitespace
let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '')
let status.state .= printf(" (%ss)", elapsed_time)
call go#statusline#Update(status_dir, status)
call go#statusline#Update(self.status_dir, status)
if closed
call s:show_errors()
if self.closed
call self.show_errors()
endif
endfunction
function! s:close_cb(ch) closure
let closed = 1
function! s:close_cb(ch) dict
let self.closed = 1
if exited
call s:show_errors()
if self.exited
call self.show_errors()
endif
endfunction
function! s:show_errors() closure
function state.show_errors()
let l:winnr = winnr()
" make sure the current window is the window from which gometalinter was
" run when the listtype is locationlist so that the location list for the
" correct window will be populated.
if l:listtype == 'locationlist'
exe l:winnr . "wincmd w"
if self.listtype == 'locationlist'
exe self.winnr . "wincmd w"
endif
let l:errorformat = '%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m'
call go#list#ParseFormat(l:listtype, l:errorformat, messages, 'GoMetaLinter')
call go#list#ParseFormat(self.listtype, l:errorformat, self.messages, 'GoMetaLinter')
let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors))
let errors = go#list#Get(self.listtype)
call go#list#Window(self.listtype, len(errors))
" move to the window that was active before processing the errors, because
" the user may have moved around within the window or even moved to a
" different window since saving. Moving back to current window as of the
" start of this function avoids the perception that the quickfix window
" steals focus when linting takes a while.
if self.autosave
exe l:winnr . "wincmd w"
endif
if get(g:, 'go_echo_command_info', 1)
call go#util#EchoSuccess("linting finished")
endif
endfunction
" explicitly bind the callbacks to state so that self within them always
" refers to state. See :help Partial for more information.
let start_options = {
\ 'callback': funcref("s:callback"),
\ 'exit_cb': funcref("s:exit_cb"),
\ 'close_cb': funcref("s:close_cb"),
\ 'callback': funcref("s:callback", [], state),
\ 'exit_cb': funcref("s:exit_cb", [], state),
\ 'close_cb': funcref("s:close_cb", [], state),
\ }
call job_start(a:args.cmd, start_options)

View file

@ -3,7 +3,7 @@ func! Test_Gometa() abort
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
let expected = [
\ {'lnum': 5, 'bufnr': 3, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingFooDoc should have comment or be unexported (golint)'}
\ {'lnum': 5, 'bufnr': bufnr('%')+1, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingFooDoc should have comment or be unexported (golint)'}
\ ]
" clear the quickfix lists
@ -37,7 +37,7 @@ func! Test_GometaWithDisabled() abort
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
let expected = [
\ {'lnum': 5, 'bufnr': 3, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingFooDoc should have comment or be unexported (golint)'}
\ {'lnum': 5, 'bufnr': bufnr('%')+1, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingFooDoc should have comment or be unexported (golint)'}
\ ]
" clear the quickfix lists
@ -71,7 +71,7 @@ func! Test_GometaAutoSave() abort
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
let expected = [
\ {'lnum': 5, 'bufnr': 2, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingDoc should have comment or be unexported (golint)'}
\ {'lnum': 5, 'bufnr': bufnr('%'), 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingDoc should have comment or be unexported (golint)'}
\ ]
let winnr = winnr()
@ -102,4 +102,30 @@ func! Test_GometaAutoSave() abort
let g:go_metalinter_autosave_enabled = orig_go_metalinter_autosave_enabled
endfunc
func! Test_Vet()
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
silent exe 'e ' . $GOPATH . '/src/vet/vet.go'
compiler go
let expected = [
\ {'lnum': 7, 'bufnr': bufnr('%'), 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'arg str for printf verb %d of wrong type: string'}
\ ]
let winnr = winnr()
" clear the location lists
call setqflist([], 'r')
call go#lint#Vet(1)
let actual = getqflist()
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getqflist()
endwhile
call gotest#assert_quickfix(actual, expected)
endfunc
" vim: sw=2 ts=2 et

View file

@ -18,14 +18,7 @@ function! go#list#Window(listtype, ...) abort
" location list increases/decreases, cwindow will not resize when a new
" updated height is passed. lopen in the other hand resizes the screen.
if !a:0 || a:1 == 0
let autoclose_window = get(g:, 'go_list_autoclose', 1)
if autoclose_window
if a:listtype == "locationlist"
lclose
else
cclose
endif
endif
call go#list#Close(a:listtype)
return
endif
@ -79,13 +72,7 @@ function! go#list#ParseFormat(listtype, errformat, items, title) abort
" parse and populate the location list
let &errorformat = a:errformat
try
if a:listtype == "locationlist"
lgetexpr a:items
if has("patch-7.4.2200") | call setloclist(0, [], 'a', {'title': a:title}) | endif
else
cgetexpr a:items
if has("patch-7.4.2200") | call setqflist([], 'a', {'title': a:title}) | endif
endif
call go#list#Parse(a:listtype, a:items, a:title)
finally
"restore back
let &errorformat = old_errorformat
@ -94,11 +81,13 @@ endfunction
" Parse parses the given items based on the global errorformat and
" populates the list.
function! go#list#Parse(listtype, items) abort
function! go#list#Parse(listtype, items, title) abort
if a:listtype == "locationlist"
lgetexpr a:items
if has("patch-7.4.2200") | call setloclist(0, [], 'a', {'title': a:title}) | endif
else
cgetexpr a:items
if has("patch-7.4.2200") | call setqflist([], 'a', {'title': a:title}) | endif
endif
endfunction
@ -111,13 +100,29 @@ function! go#list#JumpToFirst(listtype) abort
endif
endfunction
" Clean cleans the location list
" Clean cleans and closes the location list
function! go#list#Clean(listtype) abort
if a:listtype == "locationlist"
lex []
else
cex []
endif
call go#list#Close(a:listtype)
endfunction
" Close closes the location list
function! go#list#Close(listtype) abort
let autoclose_window = get(g:, 'go_list_autoclose', 1)
if !autoclose_window
return
endif
if a:listtype == "locationlist"
lclose
else
cclose
endif
endfunction
function! s:listtype(listtype) abort

View file

@ -54,8 +54,14 @@ function! go#package#Paths() abort
return dirs
endfunction
let s:import_paths = {}
" ImportPath returns the import path in the current directory it was executed
function! go#package#ImportPath() abort
let dir = expand("%:p:h")
if has_key(s:import_paths, dir)
return s:import_paths[dir]
endif
let out = go#tool#ExecuteInDir("go list")
if go#util#ShellError() != 0
return -1
@ -69,6 +75,8 @@ function! go#package#ImportPath() abort
return -1
endif
let s:import_paths[dir] = import_path
return import_path
endfunction

View file

@ -72,20 +72,22 @@ function! go#rename#Rename(bang, ...) abort
endfunction
function s:rename_job(args)
let exited = 0
let closed = 0
let exitval = 0
let messages = []
let state = {
\ 'exited': 0,
\ 'closed': 0,
\ 'exitval': 0,
\ 'messages': [],
\ 'status_dir': expand('%:p:h'),
\ 'bang': a:args.bang
\ }
function! s:callback(chan, msg) closure
call add(messages, a:msg)
function! s:callback(chan, msg) dict
call add(self.messages, a:msg)
endfunction
let status_dir = expand('%:p:h')
function! s:exit_cb(job, exitval) closure
let exited = 1
let exitval = a:exitval
function! s:exit_cb(job, exitval) dict
let self.exited = 1
let self.exitval = a:exitval
let status = {
\ 'desc': 'last status',
@ -97,28 +99,30 @@ function s:rename_job(args)
let status.state = "failed"
endif
call go#statusline#Update(status_dir, status)
call go#statusline#Update(self.status_dir, status)
if closed
call s:parse_errors(a:exitval, a:args.bang, messages)
if self.closed
call s:parse_errors(self.exitval, self.bang, self.messages)
endif
endfunction
function! s:close_cb(ch) closure
let closed = 1
function! s:close_cb(ch) dict
let self.closed = 1
if exited
call s:parse_errors(exitval, a:args.bang, messages)
if self.exited
call s:parse_errors(self.exitval, self.bang, self.messages)
endif
endfunction
" explicitly bind the callbacks to state so that self within them always
" refers to state. See :help Partial for more information.
let start_options = {
\ 'callback': funcref("s:callback"),
\ 'exit_cb': funcref("s:exit_cb"),
\ 'close_cb': funcref("s:close_cb"),
\ 'callback': funcref("s:callback", [], state),
\ 'exit_cb': funcref("s:exit_cb", [], state),
\ 'close_cb': funcref("s:close_cb", [], state),
\ }
call go#statusline#Update(status_dir, {
call go#statusline#Update(state.status_dir, {
\ 'desc': "current status",
\ 'type': "gorename",
\ 'state': "started",
@ -156,7 +160,6 @@ function s:parse_errors(exit_val, bang, out)
" strip out newline on the end that gorename puts. If we don't remove, it
" will trigger the 'Hit ENTER to continue' prompt
call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
call go#util#EchoSuccess(a:out[0])
" refresh the buffer so we can see the new content

View file

@ -24,7 +24,7 @@ function! go#template#create() abort
let l:template_file = get(g:, 'go_template_file', "hello_world.go")
endif
let l:template_path = go#util#Join(l:root_dir, "templates", l:template_file)
silent exe '0r ' . fnameescape(l:template_path)
silent exe 'keepalt 0r ' . fnameescape(l:template_path)
elseif l:package_name == -1 && l:go_template_use_pkg == 1
" cwd is now the dir of the package
let l:path = fnamemodify(getcwd(), ':t')

View file

@ -2,9 +2,6 @@ if has('nvim') && !exists("g:go_term_mode")
let g:go_term_mode = 'vsplit'
endif
" s:jobs is a global reference to all jobs started with new()
let s:jobs = {}
" new creates a new terminal with the given command. Mode is set based on the
" global variable g:go_term_mode, which is by default set to :vsplit
function! go#term#new(bang, cmd) abort
@ -18,8 +15,14 @@ function! go#term#newmode(bang, cmd, mode) abort
let mode = g:go_term_mode
endif
let state = {
\ 'cmd': a:cmd,
\ 'bang' : a:bang,
\ 'winid': win_getid(winnr()),
\ 'stdout': []
\ }
" execute go build in the files directory
let l:winnr = winnr()
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd()
@ -33,30 +36,27 @@ function! go#term#newmode(bang, cmd, mode) abort
setlocal noswapfile
setlocal nobuflisted
" explicitly bind callbacks to state so that within them, self will always
" refer to state. See :help Partial for more information.
"
" Don't set an on_stderr, because it will be passed the same data as
" on_stdout. See https://github.com/neovim/neovim/issues/2836
let job = {
\ 'stderr' : [],
\ 'stdout' : [],
\ 'bang' : a:bang,
\ 'on_stdout': function('s:on_stdout'),
\ 'on_stderr': function('s:on_stderr'),
\ 'on_exit' : function('s:on_exit'),
\ 'on_stdout': function('s:on_stdout', [], state),
\ 'on_exit' : function('s:on_exit', [], state),
\ }
let id = termopen(a:cmd, job)
let state.id = termopen(a:cmd, job)
let state.termwinid = win_getid(winnr())
execute cd . fnameescape(dir)
let job.id = id
let job.cmd = a:cmd
startinsert
" resize new term if needed.
let height = get(g:, 'go_term_height', winheight(0))
let width = get(g:, 'go_term_width', winwidth(0))
" we are careful how to resize. for example it's vsplit we don't change
" the height. The below command resizes the buffer
" Adjust the window width or height depending on whether it's a vertical or
" horizontal split.
if mode =~ "vertical" || mode =~ "vsplit" || mode =~ "vnew"
exe 'vertical resize ' . width
elseif mode =~ "split" || mode =~ "new"
@ -64,77 +64,56 @@ function! go#term#newmode(bang, cmd, mode) abort
endif
" we also need to resize the pty, so there you go...
call jobresize(id, width, height)
call jobresize(state.id, width, height)
let s:jobs[id] = job
stopinsert
call win_gotoid(state.winid)
if l:winnr !=# winnr()
exe l:winnr . "wincmd w"
endif
return id
return state.id
endfunction
function! s:on_stdout(job_id, data, event) dict abort
if !has_key(s:jobs, a:job_id)
return
endif
let job = s:jobs[a:job_id]
call extend(job.stdout, a:data)
endfunction
function! s:on_stderr(job_id, data, event) dict abort
if !has_key(s:jobs, a:job_id)
return
endif
let job = s:jobs[a:job_id]
call extend(job.stderr, a:data)
call extend(self.stdout, a:data)
endfunction
function! s:on_exit(job_id, exit_status, event) dict abort
if !has_key(s:jobs, a:job_id)
return
endif
let job = s:jobs[a:job_id]
let l:listtype = go#list#Type("_term")
" usually there is always output so never branch into this clause
if empty(job.stdout)
call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
unlet s:jobs[a:job_id]
if empty(self.stdout)
call s:cleanlist(self.winid, l:listtype)
return
endif
let errors = go#tool#ParseErrors(job.stdout)
let errors = go#tool#ParseErrors(self.stdout)
let errors = go#tool#FilterValids(errors)
if !empty(errors)
" close terminal we don't need it anymore
" close terminal; we don't need it anymore
call win_gotoid(self.termwinid)
close
call go#list#Populate(l:listtype, errors, job.cmd)
call win_gotoid(self.winid)
call go#list#Populate(l:listtype, errors, self.cmd)
call go#list#Window(l:listtype, len(errors))
if !self.bang
call go#list#JumpToFirst(l:listtype)
endif
unlet s:jobs[a:job_id]
return
endif
" tests are passing clean the list and close the list. But we only can
" close them from a normal view, so jump back, close the list and then
" again jump back to the terminal
wincmd p
call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
wincmd p
call s:cleanlist(self.winid, l:listtype)
endfunction
unlet s:jobs[a:job_id]
function! s:cleanlist(winid, listtype) abort
" There are no errors. Clean and close the list. Jump to the window to which
" the location list is attached, close the list, and then jump back to the
" current window.
let winid = win_getid(winnr())
call win_gotoid(a:winid)
call go#list#Clean(a:listtype)
call win_gotoid(l:winid)
endfunction
" vim: sw=2 ts=2 et

View file

@ -0,0 +1,50 @@
func! Test_GoTermNewMode()
if !has('nvim')
return
endif
try
let l:filename = 'term/term.go'
let l:tmp = gotest#load_fixture(l:filename)
exe 'cd ' . l:tmp . '/src/term'
let expected = expand('%:p')
let cmd = "go run ". go#util#Shelljoin(go#tool#Files())
set nosplitright
call go#term#newmode(0, cmd, '')
let actual = expand('%:p')
call assert_equal(actual, l:expected)
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_GoTermNewMode_SplitRight()
if !has('nvim')
return
endif
try
let l:filename = 'term/term.go'
let l:tmp = gotest#load_fixture(l:filename)
exe 'cd ' . l:tmp . '/src/term'
let expected = expand('%:p')
let cmd = "go run ". go#util#Shelljoin(go#tool#Files())
set splitright
call go#term#newmode(0, cmd, '')
let actual = expand('%:p')
call assert_equal(actual, l:expected)
finally
call delete(l:tmp, 'rf')
set nosplitright
endtry
endfunc
" vim: sw=2 ts=2 et

View file

@ -0,0 +1,5 @@
package main
func main() {
notafunc()
}

View file

@ -0,0 +1,8 @@
package main
import "fmt"
func main() {
str := "hello world!"
fmt.Printf("%d\n", str)
}

View file

@ -0,0 +1,5 @@
package main
func main() {
println("hello, world")
}

View file

@ -94,7 +94,6 @@ function! go#test#Test(bang, compile, ...) abort
call go#util#EchoError("[test] FAIL")
else
call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
if a:compile
call go#util#EchoSuccess("[test] SUCCESS")
@ -139,9 +138,6 @@ function! go#test#Func(bang, ...) abort
endfunction
function! s:test_job(args) abort
let status_dir = expand('%:p:h')
let started_at = reltime()
let status = {
\ 'desc': 'current status',
\ 'type': "test",
@ -152,23 +148,29 @@ function! s:test_job(args) abort
let status.state = "compiling"
endif
call go#statusline#Update(status_dir, status)
" autowrite is not enabled for jobs
call go#cmd#autowrite()
let l:exited = 0
let l:closed = 0
let l:exitval = 0
let messages = []
let state = {
\ 'exited': 0,
\ 'closed': 0,
\ 'exitval': 0,
\ 'messages': [],
\ 'args': a:args,
\ 'compile_test': a:args.compile_test,
\ 'status_dir': expand('%:p:h'),
\ 'started_at': reltime()
\ }
function! s:callback(chan, msg) closure
call add(messages, a:msg)
call go#statusline#Update(state.status_dir, status)
function! s:callback(chan, msg) dict
call add(self.messages, a:msg)
endfunction
function! s:exit_cb(job, exitval) closure
let exited = 1
let exitval = a:exitval
function! s:exit_cb(job, exitval) dict
let self.exited = 1
let self.exitval = a:exitval
let status = {
\ 'desc': 'last status',
@ -176,7 +178,7 @@ function! s:test_job(args) abort
\ 'state': "pass",
\ }
if a:args.compile_test
if self.compile_test
let status.state = "success"
endif
@ -186,7 +188,7 @@ function! s:test_job(args) abort
if get(g:, 'go_echo_command_info', 1)
if a:exitval == 0
if a:args.compile_test
if self.compile_test
call go#util#EchoSuccess("[test] SUCCESS")
else
call go#util#EchoSuccess("[test] PASS")
@ -196,30 +198,32 @@ function! s:test_job(args) abort
endif
endif
let elapsed_time = reltimestr(reltime(started_at))
let elapsed_time = reltimestr(reltime(self.started_at))
" strip whitespace
let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '')
let status.state .= printf(" (%ss)", elapsed_time)
call go#statusline#Update(status_dir, status)
call go#statusline#Update(self.status_dir, status)
if closed
call s:show_errors(a:args, l:exitval, messages)
if self.closed
call s:show_errors(self.args, self.exitval, self.messages)
endif
endfunction
function! s:close_cb(ch) closure
let closed = 1
function! s:close_cb(ch) dict
let self.closed = 1
if exited
call s:show_errors(a:args, l:exitval, messages)
if self.exited
call s:show_errors(self.args, self.exitval, self.messages)
endif
endfunction
" explicitly bind the callbacks to state so that self within them always
" refers to state. See :help Partial for more information.
let start_options = {
\ 'callback': funcref("s:callback"),
\ 'exit_cb': funcref("s:exit_cb"),
\ 'close_cb': funcref("s:close_cb"),
\ 'callback': funcref("s:callback", [], state),
\ 'exit_cb': funcref("s:exit_cb", [], state),
\ 'close_cb': funcref("s:close_cb", [], state)
\ }
" pre start
@ -241,7 +245,6 @@ function! s:show_errors(args, exit_val, messages) abort
let l:listtype = go#list#Type("GoTest")
if a:exit_val == 0
call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
return
endif

View file

@ -17,6 +17,7 @@ endif
" < >
" t for tag
" Select a function in visual mode.
function! go#textobj#Function(mode) abort
let offset = go#util#OffsetCursor()
@ -98,23 +99,8 @@ function! go#textobj#Function(mode) abort
call cursor(info.rbrace.line-1, 1)
endfunction
function! go#textobj#FunctionJump(mode, direction) abort
" get count of the motion. This should be done before all the normal
" expressions below as those reset this value(because they have zero
" count!). We abstract -1 because the index starts from 0 in motion.
let l:cnt = v:count1 - 1
" set context mark so we can jump back with '' or ``
normal! m'
" select already previously selected visual content and continue from there.
" If it's the first time starts with the visual mode. This is needed so
" after selecting something in visual mode, every consecutive motion
" continues.
if a:mode == 'v'
normal! gv
endif
" Get the location of the previous or next function.
function! go#textobj#FunctionLocation(direction, cnt) abort
let offset = go#util#OffsetCursor()
let fname = shellescape(expand("%:p"))
@ -131,7 +117,7 @@ function! go#textobj#FunctionJump(mode, direction) abort
endif
let command = printf("%s -format vim -file %s -offset %s", bin_path, fname, offset)
let command .= ' -shift ' . l:cnt
let command .= ' -shift ' . a:cnt
if a:direction == 'next'
let command .= ' -mode next'
@ -154,9 +140,33 @@ function! go#textobj#FunctionJump(mode, direction) abort
call delete(l:tmpname)
endif
" convert our string dict representation into native Vim dictionary type
let result = eval(out)
if type(result) != 4 || !has_key(result, 'fn')
let l:result = json_decode(out)
if type(l:result) != 4 || !has_key(l:result, 'fn')
return 0
endif
return l:result
endfunction
function! go#textobj#FunctionJump(mode, direction) abort
" get count of the motion. This should be done before all the normal
" expressions below as those reset this value(because they have zero
" count!). We abstract -1 because the index starts from 0 in motion.
let l:cnt = v:count1 - 1
" set context mark so we can jump back with '' or ``
normal! m'
" select already previously selected visual content and continue from there.
" If it's the first time starts with the visual mode. This is needed so
" after selecting something in visual mode, every consecutive motion
" continues.
if a:mode == 'v'
normal! gv
endif
let l:result = go#textobj#FunctionLocation(a:direction, l:cnt)
if l:result is 0
return
endif

View file

@ -330,6 +330,7 @@ function! go#util#EchoWarning(msg)
call s:echo(a:msg, 'WarningMsg')
endfunction
function! go#util#EchoProgress(msg)
redraw
call s:echo(a:msg, 'Identifier')
endfunction
function! go#util#EchoInfo(msg)
@ -362,7 +363,6 @@ function! go#util#archive()
return expand("%:p:gs!\\!/!") . "\n" . strlen(l:buffer) . "\n" . l:buffer
endfunction
" Make a named temporary directory which starts with "prefix".
"
" Unfortunately Vim's tempname() is not portable enough across various systems;
@ -384,7 +384,7 @@ function! go#util#tempdir(prefix) abort
endfor
if l:dir == ''
echoerr 'Unable to find directory to store temporary directory in'
call go#util#EchoError('Unable to find directory to store temporary directory in')
return
endif
@ -395,4 +395,9 @@ function! go#util#tempdir(prefix) abort
return l:tmp
endfunction
" Report if the user enabled a debug flag in g:go_debug.
function! go#util#HasDebug(flag)
return index(get(g:, 'go_debug', []), a:flag) >= 0
endfunction
" vim: sw=2 ts=2 et

View file

@ -22,10 +22,11 @@ CONTENTS *go-contents*
6. Functions....................................|go-functions|
7. Settings.....................................|go-settings|
8. Syntax highlighting..........................|go-syntax|
9. FAQ/Troubleshooting..........................|go-troubleshooting|
10. Development..................................|go-development|
11. Donation.....................................|go-donation|
12. Credits......................................|go-credits|
9. Debugger.....................................|go-debug|
10. FAQ/Troubleshooting..........................|go-troubleshooting|
11. Development..................................|go-development|
12. Donation.....................................|go-donation|
13. Credits......................................|go-credits|
==============================================================================
INTRO *go-intro*
@ -37,16 +38,16 @@ and individual features can be toggled easily. vim-go leverages a number of
tools developed by the Go community to provide a seamless Vim experience.
* Compile your package with |:GoBuild|, install it with |:GoInstall| or
test it with |:GoTest|. Run a single tests with |:GoTestFunc|).
test it with |:GoTest|. Run a single test with |:GoTestFunc|).
* Quickly execute your current file(s) with |:GoRun|.
* Improved syntax highlighting and folding.
* Debug programs with integrated `delve` support with |:GoDebugStart|.
* Completion support via `gocode`.
* `gofmt` or `goimports` on save keeps the cursor position and undo history.
* Go to symbol/declaration with |:GoDef|.
* Look up documentation with |:GoDoc| or |:GoDocBrowser|.
* Easily import packages via |:GoImport|, remove them via |:GoDrop|.
* Automatic `GOPATH` detection which works with `gb` and `godep`. Change or
display `GOPATH` with |:GoPath|.
* Precise type-safe renaming of identifiers with |:GoRename|.
* See which code is covered by tests with |:GoCoverage|.
* Add or remove tags on struct fields with |:GoAddTags| and |:GoRemoveTags|.
* Call `gometalinter` with |:GoMetaLinter| to invoke all possible linters
@ -56,7 +57,8 @@ tools developed by the Go community to provide a seamless Vim experience.
static errors, or make sure errors are checked with |:GoErrCheck|.
* Advanced source analysis tools utilizing `guru`, such as |:GoImplements|,
|:GoCallees|, and |:GoReferrers|.
* Precise type-safe renaming of identifiers with |:GoRename|.
* Automatic `GOPATH` detection which works with `gb` and `godep`. Change or
display `GOPATH` with |:GoPath|.
* Integrated and improved snippets, supporting `ultisnips`, `neosnippet`,
and `vim-minisnip`.
* Share your current code to play.golang.org with |:GoPlay|.
@ -1100,7 +1102,7 @@ cleaned for each package after `60` seconds. This can be changed with the
*go#complete#GetInfo()*
Returns the description of the identifer under the cursor. Can be used to plug
into the statusline. This function is also used for |'g:go_auto_type_info'|.
into the statusline.
==============================================================================
SETTINGS *go-settings*
@ -1658,6 +1660,18 @@ By default "snakecase" is used. Current values are: ["snakecase",
>
let g:go_addtags_transform = 'snakecase'
<
*'g:go_debug'*
A list of options to debug; useful for development and/or reporting bugs.
Currently accepted values:
debugger-state Expose debugger state in 'g:go_debug_diag'.
debugger-commands Echo communication between vim-go and `dlv`; requests and
responses are recorded in `g:go_debug_commands`.
>
let g:go_debug = []
<
==============================================================================
SYNTAX HIGHLIGHTING *ft-go-syntax* *go-syntax*
@ -1815,6 +1829,212 @@ The `gohtmltmpl` filetype is automatically set for `*.tmpl` files; the
`gotexttmpl` is never automatically set and needs to be set manually.
==============================================================================
DEBUGGER *go-debug*
Vim-go comes with a special "debugger mode". This starts a `dlv` process in
the background and provides various commands to communicate with it.
This debugger is similar to Visual Studio or Eclipse and has the following
features:
* Show stack trace and jumps.
* List local variables.
* List function arguments.
* Expand values of struct or array/slice.
* Show balloon on the symbol.
* Show output of stdout/stderr.
* Toggle breakpoint.
* Stack operation continue/next/step out.
This feature requires Vim 8.0.0087 or newer with the |+job| feature. Neovim
does _not_ work (yet).
This requires Delve 1.0.0 or newer, and it is recommended to use Go 1.10 or
newer, as its new caching will speed up recompiles.
*go-debug-intro*
GETTING STARTED WITH THE DEBUGGER~
Use |:GoDebugStart| or |:GoDebugTest| to start the debugger. The first
argument is the package name, and any arguments after that will be passed on
to the program; for example:
>
:GoDebugStart . -someflag value
<
This may take few seconds. After the code is compiled you'll see three new
windows: the stack trace on left side, the variable list on the bottom-left,
and program output at the bottom.
You can add breakpoints with |:GoDebugBreakpoint| (<F9>) and run your program
with |:GoDebugContinue| (<F5>).
The program will halt on the breakpoint, at which point you can inspect the
program state. You can go to the next line with |:GoDebugNext| (<F10>) or step
in with |:GoDebugStep| (<F11>).
The variable window in the bottom left (`GODEBUG_VARIABLES`) will display all
local variables. Struct values are displayed as `{...}`, array/slices as
`[4]`. Use <CR> on the variable name to expand the values.
The `GODEBUG_OUTPUT` window displays output from the program and the Delve
debugger.
The `GODEBUG_STACKTRACE` window can be used to jump to different places in the
call stack.
When you're done use |:GoDebugStop| to close the debugging windows and halt
the `dlv` process, or |:GoDebugRestart| to recompile the code.
*go-debug-commands*
DEBUGGER COMMANDS~
Only |:GoDebugStart| and |:GoDebugBreakpoint| are available by default; the
rest of the commands and mappings become available after starting debug mode.
*:GoDebugStart*
:GoDebugStart [pkg] [program-args]
Start the debug mode for [pkg]; this does several things:
* Setup the debug windows according to |'g:go_debug_windows'|.
* Make the `:GoDebug*` commands and `(go-debug-*)` mappings available.
The current directory is used if [pkg] is empty. Any other arguments will
be passed to the program.
Use |:GoDebugStop| to stop `dlv` and exit debugging mode.
*:GoDebugTest*
:GoDebugTest [pkg] [program-args]
Behaves the same as |:GoDebugStart| but runs `dlv test` instead of
`dlv debug` so you can debug tests.
Use `-test.flag` to pass flags to `go test` when debugging a test; for
example `-test.v` or `-test.run TestFoo`
*:GoDebugRestart*
:GoDebugRestart
Stop the program (if running) and restart `dlv` to recompile the package.
The current window layout and breakpoints will be left intact.
*:GoDebugStop*
*(go-debug-stop)*
:GoDebugStop
Stop `dlv` and remove all debug-specific commands, mappings, and windows.
*:GoDebugBreakpoint*
*(go-debug-breakpoint)*
:GoDebugBreakpoint [linenr]
Toggle breakpoint for the [linenr]. [linenr] defaults to the current line
if it is omitted. A line with a breakpoint will have the
{godebugbreakpoint} |:sign| placed on it. The line the program is
currently halted on will have the {godebugcurline} sign.
*hl-GoDebugCurrent* *hl-GoDebugBreakpoint*
A line with a breakpoint will be highlighted with the {GoDebugBreakpoint}
group; the line the program is currently halted on will be highlighted
with {GoDebugCurrent}.
Mapped to <F9> by default.
*:GoDebugContinue*
*(go-debug-continue)*
:GoDebugContinue
Continue execution until breakpoint or program termination. It will start
the program if it hasn't been started yet.
Mapped to <F5> by default.
*:GoDebugNext*
*(go-debug-next)*
:GoDebugNext
Advance execution by one line, also called "step over" by some other
debuggers.
It will behave as |:GoDebugContinue| if the program isn't started.
Mapped to <F10> by default.
*:GoDebugStep*
*(go-debug-step)*
:GoDebugStep
Advance execution by one step, stopping at the next line of code that will
be executed (regardless of location).
It will behave as |:GoDebugContinue| if the program isn't started.
Mapped to <F11> by default.
*:GoDebugStepOut*
*(go-debug-stepout)*
:GoDebugStepOut
Run all the code in the current function and halt when the function
returns ("step out of the current function").
It will behave as |:GoDebugContinue| if the program isn't started.
*:GoDebugSet*
:GoDebugSet {var} {value}
Set the variable {var} to {value}. Example:
>
:GoDebugSet truth 42
<
This only works for `float`, `int` and variants, `uint` and variants,
`bool`, and pointers (this is a `delve` limitation, not a vim-go
limitation).
*:GoDebugPrint*
*(go-debug-print)*
:GoDebugPrint {expr}
Print the result of a Go expression.
>
:GoDebugPrint truth == 42
truth == 42 true
<
Mapped to <F6> by default, which will evaluate the <cword> under the
cursor.
*go-debug-settings*
DEBUGGER SETTINGS~
*'g:go_debug_windows'*
Controls the window layout for debugging mode. This is a |dict| with three
possible keys: "stack", "out", and "vars"; the windows will created in that
order with the commands in the value.
A window will not be created if a key is missing or empty.
Defaults:
>
let g:go_debug_windows = {
\ 'stack': 'leftabove 20vnew',
\ 'out': 'botright 10new',
\ 'vars': 'leftabove 30vnew',
\ }
<
Show only variables on the right-hand side: >
let g:go_debug_windows = {
\ 'vars': 'rightbelow 60vnew',
\ }
<
*'g:go_debug_address'*
Server address `dlv` will listen on; must be in `hostname:port` format.
Defaults to `127.0.0.1:8181`:
>
let g:go_debug_address = '127.0.0.1:8181'
<
==============================================================================
FAQ TROUBLESHOOTING *go-troubleshooting*

View file

@ -98,4 +98,11 @@ command! -nargs=0 GoKeyify call go#keyify#Keyify()
" -- fillstruct
command! -nargs=0 GoFillStruct call go#fillstruct#FillStruct()
" -- debug
if !exists(':GoDebugStart')
command! -nargs=* -complete=customlist,go#package#Complete GoDebugStart call go#debug#Start(0, <f-args>)
command! -nargs=* -complete=customlist,go#package#Complete GoDebugTest call go#debug#Start(1, <f-args>)
command! -nargs=? GoDebugBreakpoint call go#debug#Breakpoint(<f-args>)
endif
" vim: sw=2 ts=2 et

View file

@ -40,7 +40,7 @@ function! s:GoMinisnip() abort
endif
if exists('g:minisnip_dir')
let g:minisnip_dir .= ':' . globpath(&rtp, 'gosnippets/minisnip')
let g:minisnip_dir .= go#util#PathListSep() . globpath(&rtp, 'gosnippets/minisnip')
else
let g:minisnip_dir = globpath(&rtp, 'gosnippets/minisnip')
endif

View file

@ -252,6 +252,11 @@ snippet fn "fmt.Println(...)"
fmt.Println("${1:${VISUAL}}")
endsnippet
# Fmt Errorf debug
snippet fe "fmt.Errorf(...)"
fmt.Errorf("${1:${VISUAL}}")
endsnippet
# log printf
snippet lf "log.Printf(...)"
log.Printf("${1:${VISUAL}} = %+v\n", $1)

View file

@ -219,6 +219,10 @@ abbr fmt.Printf(...)
snippet fn
abbr fmt.Println(...)
fmt.Println("${1}")
# Fmt Errorf
snippet fe
abbr fmt.Errorf(...)
fmt.Errorf("${1}")
# log printf
snippet lf
abbr log.Printf(...)

View file

@ -31,6 +31,7 @@ endif
" needed by the user with GoInstallBinaries
let s:packages = {
\ 'asmfmt': ['github.com/klauspost/asmfmt/cmd/asmfmt'],
\ 'dlv': ['github.com/derekparker/delve/cmd/dlv'],
\ 'errcheck': ['github.com/kisielk/errcheck'],
\ 'fillstruct': ['github.com/davidrjenni/reftools/cmd/fillstruct'],
\ 'gocode': ['github.com/nsf/gocode', {'windows': '-ldflags -H=windowsgui'}],

View file

@ -453,7 +453,7 @@ if g:go_highlight_build_constraints != 0 || s:fold_package_comment
\ . ' contains=@goCommentGroup,@Spell'
\ . (s:fold_package_comment ? ' fold' : '')
exe 'syn region goPackageComment start=/\v\/\*.*\n(.*\n)*\s*\*\/\npackage/'
\ . ' end=/\v\n\s*package/he=e-7,me=e-7,re=e-7'
\ . ' end=/\v\*\/\n\s*package/he=e-7,me=e-7,re=e-7'
\ . ' contains=@goCommentGroup,@Spell'
\ . (s:fold_package_comment ? ' fold' : '')
hi def link goPackageComment Comment

View file

@ -0,0 +1,13 @@
if exists("b:current_syntax")
finish
endif
syn match godebugOutputErr '^ERR:.*'
syn match godebugOutputOut '^OUT:.*'
let b:current_syntax = "godebugoutput"
hi def link godebugOutputErr Comment
hi def link godebugOutputOut Normal
" vim: sw=2 ts=2 et

View file

@ -0,0 +1,11 @@
if exists("b:current_syntax")
finish
endif
syn match godebugStacktrace '^\S\+'
let b:current_syntax = "godebugoutput"
hi def link godebugStacktrace SpecialKey
" vim: sw=2 ts=2 et

View file

@ -0,0 +1,23 @@
if exists("b:current_syntax")
finish
endif
syn match godebugTitle '^#.*'
syn match godebugVariables '^\s*\S\+\ze:'
syn keyword goType chan map bool string error
syn keyword goSignedInts int int8 int16 int32 int64 rune
syn keyword goUnsignedInts byte uint uint8 uint16 uint32 uint64 uintptr
syn keyword goFloats float32 float64
syn keyword goComplexes complex64 complex128
syn keyword goBoolean true false
let b:current_syntax = "godebugvariables"
hi def link godebugTitle Underlined
hi def link godebugVariables Statement
hi def link goType Type
hi def link goBoolean Boolean
" vim: sw=2 ts=2 et

View file

@ -123,7 +123,7 @@ let g:vim_markdown_toc_autofit = 1
### Text emphasis restriction to single-lines
By default text emphasis works across multiple lines until a closing token is found. However, it's possible to restrict text emphasis to a single line (ie, for it to be applied a closing token must be found on the same line). To do so:
By default text emphasis works across multiple lines until a closing token is found. However, it's possible to restrict text emphasis to a single line (i.e., for it to be applied a closing token must be found on the same line). To do so:
```vim
let g:vim_markdown_emphasis_multiline = 0
@ -176,7 +176,7 @@ Default is `['c++=cpp', 'viml=vim', 'bash=sh', 'ini=dosini']`.
### Follow named anchors
This feature allows ge to follow named anchors in links of the form
This feature allows the `ge` command to follow named anchors in links of the form
`file#anchor` or just `#anchor`, where file may omit the `.md` extension as
usual. Two variables control its operation:

View file

@ -764,10 +764,10 @@ function! s:MarkdownClearSyntaxVariables()
endfunction
augroup Mkd
autocmd!
au BufWinEnter * call s:MarkdownRefreshSyntax(1)
au BufUnload * call s:MarkdownClearSyntaxVariables()
au BufWritePost * call s:MarkdownRefreshSyntax(0)
au InsertEnter,InsertLeave * call s:MarkdownRefreshSyntax(0)
au CursorHold,CursorHoldI * call s:MarkdownRefreshSyntax(0)
autocmd! * <buffer>
autocmd BufWinEnter <buffer> call s:MarkdownRefreshSyntax(1)
autocmd BufUnload <buffer> call s:MarkdownClearSyntaxVariables()
autocmd BufWritePost <buffer> call s:MarkdownRefreshSyntax(0)
autocmd InsertEnter,InsertLeave <buffer> call s:MarkdownRefreshSyntax(0)
autocmd CursorHold,CursorHoldI <buffer> call s:MarkdownRefreshSyntax(0)
augroup END

View file

@ -35,7 +35,7 @@ Vim command sequence: `2Gfp<C-n><C-n><C-n>cname`
### Add a cursor to each line of your visual selection
![Example2](assets/example2.gif?raw=true)
Vim command sequence: `2Gvip<C-n>i"<Right><Right><Right>",<Esc>vipJ$r]Idays = [`
Vim command sequence: `2Gvip<C-n>i"<Right><Right><Right>",<Esc>vipgJ$r]Idays = [`
### Do it backwards too! This is not just a replay of the above gif :)
![Example3](assets/example3.gif?raw=true)
@ -51,8 +51,8 @@ To see what keystrokes are used for the above examples, see [the wiki page](http
- Live update in Insert mode
- One key to rule it all! See [Quick Start](#quick-start) on what the key does in different scenarios
- Works in Normal, Insert, and Visual mode for any commands (including
multi-key commands, assuming you set `g:multicursor_insert_maps` and
`g:multicursor_normal_maps`; see Settings below for details)
multi-key commands, assuming you set `g:multicursor_normal_maps`;
see Settings below for details)
## Installation
Install using [Pathogen], [Vundle], [Neobundle], or your favorite Vim package manager.
@ -123,15 +123,6 @@ If set to 0, then pressing `g:multi_cursor_quit_key` in _Visual_ mode will not q
### ```g:multi_cursor_exit_from_insert_mode``` (Default: 1)
If set to 0, then pressing `g:multi_cursor_quit_key` in _Insert_ mode will not quit and delete all existing cursors. This is useful if you want to press Escape and go back to Normal mode, and still be able to operate on all the cursors.
### ```g:multi_cursor_insert_maps``` (Default: `{}`)
Any key in this map (values are ignored) will cause multi-cursor _Insert_ mode
to pause for `timeoutlen` waiting for map completion just like normal vim.
Otherwise keys mapped in insert mode are ignored when multiple cursors are
active. For example, setting it to `{'\':1}` will make insert-mode mappings
beginning with the default leader key work in multi-cursor mode. You have to
manually set this because vim doesn't provide a way to see which keys _start_
mappings.
### ```g:multi_cursor_normal_maps``` (Default: see below)
Default value: `{'!':1, '@':1, '=':1, 'q':1, 'r':1, 't':1, 'T':1, 'y':1, '[':1, ']':1, '\':1, 'd':1, 'f':1, 'F':1, 'g':1, '"':1, 'z':1, 'c':1, 'm':1, '<':1, '>':1}`

View file

@ -103,7 +103,7 @@ endfunction
function! s:fire_pre_triggers()
if !s:before_function_called
doautocmd User MultipleCursorsPre
silent doautocmd User MultipleCursorsPre
if exists('*Multiple_cursors_before')
exe "call Multiple_cursors_before()"
endif
@ -445,7 +445,7 @@ function! s:CursorManager.reset(restore_view, restore_setting, ...) dict
if exists('*Multiple_cursors_after')
exe "call Multiple_cursors_after()"
endif
doautocmd User MultipleCursorsPost
silent doautocmd User MultipleCursorsPost
let s:before_function_called = 0
endif
endfunction
@ -516,7 +516,6 @@ function! s:CursorManager.update_current() dict
if s:to_mode ==# 'V'
exec "normal! gvv\<Esc>"
endif
" Sets the cursor at the right place
exec "normal! gv\<Esc>"
call cur.update_visual_selection(s:get_visual_region(s:pos('.')))
@ -525,17 +524,18 @@ function! s:CursorManager.update_current() dict
" This should be executed after user input is processed, when unnamed
" register already contains the text.
call cur.save_unnamed_register()
call cur.remove_visual_selection()
elseif s:from_mode ==# 'i' && s:to_mode ==# 'n' && self.current_index != self.size() - 1
elseif s:from_mode ==# 'i' && s:to_mode ==# 'n' && self.current_index != 0
normal! h
elseif s:from_mode ==# 'n'
" Save contents of unnamed register after each operation in Normal mode.
call cur.save_unnamed_register()
endif
let vdelta = line('$') - s:saved_linecount
let pos = s:pos('.')
" If the total number of lines changed in the buffer, we need to potentially
" adjust other cursor locations
let vdelta = line('$') - s:saved_linecount
if vdelta != 0
if self.current_index != self.size() - 1
let cur_column_offset = (cur.column() - col('.')) * -1
@ -547,7 +547,7 @@ function! s:CursorManager.update_current() dict
let c = self.get(i)
" If there're other cursors on the same line, we need to adjust their
" columns. This needs to happen before we adjust their line!
if cur.line() == c.line()
if cur.line() == c.line() || cur.position == pos
if vdelta > 0
" Added a line
let hdelta = cur_column_offset
@ -583,7 +583,6 @@ function! s:CursorManager.update_current() dict
endif
endif
let pos = s:pos('.')
if cur.position == pos
return 0
endif
@ -598,7 +597,8 @@ endfunction
" Start tracking cursor updates
function! s:CursorManager.start_loop() dict
let self.starting_index = self.current_index
let self.current_index = 0
let self.starting_index = 0
endfunction
" Returns true if we're cycled through all the cursors
@ -1210,28 +1210,51 @@ function! s:wait_for_user_input(mode)
let s:saved_keys = ""
endif
if s:from_mode ==# 'i' && has_key(g:multi_cursor_insert_maps, s:last_char())
let c = getchar(0)
let char_type = type(c)
" ambiguous mappings are note supported; e.g.:
" imap jj JJ
" imap jjj JJJ
" will always trigger the 'jj' mapping
if s:from_mode ==# 'i' && mapcheck(s:char, "i") != ""
let poll_count = 0
while char_type == 0 && c == 0 && poll_count < &timeoutlen
sleep 1m
let map_dict = {}
while poll_count < &timeoutlen
let c = getchar(0)
let char_type = type(c)
let poll_count += 1
endwhile
let poll_count += 1.5
if char_type == 0 && c != 0
let s:char .= nr2char(c)
elseif char_type == 1 " char with more than 8 bits (as string)
let s:char .= c
endif
let map_dict = maparg(s:char, "i", 0, 1)
" break if chars exactly match mapping or if chars don't match beging of mapping anymore
if map_dict != {} || mapcheck(s:char, "i") == ""
if get(map_dict, 'expr', 0)
" handle case where {rhs} is a function
exec 'let char_mapping = ' . map_dict['rhs']
else
let char_mapping = get(map_dict, 'rhs', s:char)
endif
" handle case where mapping is <esc>
exec 'let s:char = "'.substitute(char_mapping, '<', '\\<', 'g').'"'
break
endif
sleep 1m
endwhile
elseif s:from_mode !=# 'i' && s:char[0] ==# ":"
call feedkeys(s:char)
call s:cm.reset(1, 1)
return
elseif s:from_mode ==# 'n'
elseif s:from_mode ==# 'n' || s:from_mode =~# 'v\|V'
while match(s:last_char(), "\\d") == 0
if match(s:char, '\(^\|\a\)0') == 0
" fixes an edge case concerning the `0` key.
" The 0 key behaves differently from [1-9].
" It's consumed immediately when it is the
" first key typed while we're waiting for input.
" References: issue #152, pull #241
break
endif
let s:char .= s:get_char()
endwhile
endif

View file

@ -157,16 +157,6 @@ quit and delete all existing cursors. This is useful if you want to press
Escape and go back to Normal mode, and still be able to operate on all the
cursors.
*g:multi_cursor_insert_maps* (Default: `{}`)
Any key in this map (values are ignored) will cause multi-cursor _Insert_ mode
to pause for `timeoutlen` waiting for map completion just like normal vim.
Otherwise keys mapped in insert mode are ignored when multiple cursors are
active. For example, setting it to `{'\':1}` will make insert-mode mappings
beginning with the default leader key work in multi-cursor mode. You have to
manually set this because vim doesn't provide a way to see which keys _start_
mappings.
*g:multi_cursor_normal_maps* (Default: see below)
Default value: `{'!':1, '@':1, '=':1, 'q':1, 'r':1, 't':1, 'T':1, 'y':1, '[':1, ']':1, '\':1, 'd':1, 'f':1, 'F':1, 'g':1, '"':1, 'z':1, 'c':1, 'm':1, '<':1, '>':1}`

View file

@ -40,12 +40,9 @@ let s:settings_if_default = {
\ 'skip_key': '<C-x>',
\ }
let s:default_insert_maps = {}
let s:default_normal_maps = {'!':1, '@':1, '=':1, 'q':1, 'r':1, 't':1, 'T':1, 'y':1, '[':1, ']':1, '\':1, 'd':1, 'f':1, 'F':1, 'g':1, '"':1, 'z':1, 'c':1, 'm':1, '<':1, '>':1}
let s:default_visual_maps = {'i':1, 'a':1, 'f':1, 'F':1, 't':1, 'T':1}
let g:multi_cursor_insert_maps =
\ get(g:, 'multi_cursor_insert_maps', s:default_insert_maps)
let g:multi_cursor_normal_maps =
\ get(g:, 'multi_cursor_normal_maps', s:default_normal_maps)
let g:multi_cursor_visual_maps =

View file

@ -136,6 +136,79 @@ describe "Multiple Cursors op pending & exit from insert|visual mode" do
EOF
end
specify "#normal mode '0': goes to 1st char of line" do
before <<-EOF
hello jan world
hello feb world
hello mar world
EOF
type '<C-n><C-n><C-n>vw0dw<Esc><Esc>'
after <<-EOF
jan world
feb world
mar world
EOF
end
specify "#normal mode 'd0': deletes backward to 1st char of line" do
before <<-EOF
hello jan world
hello feb world
hello mar world
EOF
type '<C-n><C-n><C-n>vwd0<Esc><Esc>'
after <<-EOF
jan world
feb world
mar world
EOF
end
end
describe "Multiple Cursors when using insert mapings" do
let(:filename) { 'test.txt' }
let(:options) { ['imap jj <esc>', 'imap jojo dude'] }
specify "#mapping doing <Esc>" do
before <<-EOF
hello world!
hello world!
bla bla bla
bla bla bla
EOF
type 'w<C-n><C-n>cjjidude<Esc>'
after <<-EOF
hello dude!
hello !
bla bla bla
bla bla bla
EOF
end
specify "#mapping using more than 2 characters" do
before <<-EOF
hello
hello
bla bla bla
bla bla bla
EOF
type '<C-n><C-n>A jojo<Esc>'
after <<-EOF
hello dude
hello dude
bla bla bla
bla bla bla
EOF
end
end
describe "Multiple Cursors when normal_maps is empty" do
@ -190,7 +263,53 @@ describe "Multiple Cursors when visual_maps is empty" do
end
describe "Multiple Cursors" do
describe "Multiple Cursors when changing the line count" do
let(:filename) { 'test.txt' }
let(:options) { ['set backspace=indent,eol,start'] }
specify "#backspace on first char of the line, then carriage return" do
before <<-EOF
madec
antoine
andre
joseph
EOF
type 'Gvip<C-n>i<BS><cr>'
after <<-EOF
madec
antoine
andre
joseph
EOF
end
specify "#del at EOL, then carriage return" do
before <<-EOF
madec
antoine
joseph
andre
EOF
type 'vip<C-n>A<DEL><cr>'
after <<-EOF
madec
antoine
joseph
andre
EOF
end
end
describe "Multiple Cursors misc" do
let(:filename) { 'test.txt' }
let(:options) { ['set autoindent'] }

View file

@ -100,4 +100,11 @@ snippet todo "TODO comment" bw
`!p snip.rv=get_comment_format()[0]` ${2:TODO}: $0${3: <${4:`!v strftime('%d-%m-%y')`}${5:, `!v g:snips_author`}>} `!p snip.rv=get_comment_format()[2]`
endsnippet
##########
# Misc #
##########
snippet uuid "Random UUID" w
`!p if not snip.c: import uuid; snip.rv = uuid.uuid4()`
endsnippet
# vim:ft=snippets:

View file

@ -0,0 +1,733 @@
###########################################################################
# PLSQL SNIPPETS #
###########################################################################
global !p
# Import package
import datetime
# Return the doc string for PLSQL script
def docstring_plsql(params):
comment = ""
if params:
comment = "/** Parameters\n"
# Split the arguments
args = [arg.strip() for arg in params.split(',')]
for arg in args:
comment += "* {0:30} : \n".format(arg.split(' ')[0].upper())
comment += "*/\n"
# Return the comment string
return comment
def hdr_params(params, level=0, gap=" "):
line = level * gap + "-- -----------------------------------------------"
comment = line
if params:
# Split the arguments
args = [arg.strip() for arg in params.split(',')]
for arg in args:
comment += "\n" + level * gap + "-- {0:20} : ".format(arg.split(' ')[0].upper())
# comment += line
# Return the comment string
return comment
def dyear():
""" Returns the current Year in YYYY format
"""
now = datetime.datetime.now()
rv=now.year
return rv
def today():
""" Returns the current Date in DD-MON-YYYY format
"""
now = datetime.datetime.now()
rv=now.strftime("%d-%b-%Y")
return rv
def param(var):
""" Returns the string name wrapped value """
return "'" + var + " : ' || "
endglobal
########################################
# SQL Snippets #
########################################
snippet doc "Document comment"
/*
* ${0: comment ...}
*/
endsnippet
snippet hdr "Header Documentation"
-- #############################################################################
-- # Copyright (c) `!p snip.rv = dyear()` ${1:company}
-- # All rights reserved
-- #
-- ############################################################################
-- # Application : ${2:schema}
-- # File Name: : ${3:`!p snip.rv=snip.fn`}
-- # Type : Table
-- # Exec Method : PL/SQL File
-- # Description : This script ${5:create} under the schema $2
-- #
-- # Change History
-- # -----------------------------------------------------------------------
-- # Version Date Author Remarks
-- # ======= =========== ================ ============================
-- # 1.0 `!p snip.rv = today()` Amit Maindola Initial Version
-- #############################################################################
endsnippet
snippet pkggbl "Package Global variables"
-- Declare Global Variables
g_sysdate DATE := SYSDATE;
g_delimiter VARCHAR2( 30 ) := ' ';
g_err_length_limit NUMBER := 1500;
g_package_name CONSTANT VARCHAR2(30) := '${0}';
g_proc_name VARCHAR2(100) := NULL;
excp_custom EXCEPTION;
-- Declare User Global Types
endsnippet
snippet flushca "Flush Cache"
ALTER SYSTEM FLUSH BUFFER_CACHE;
endsnippet
snippet flushsp "Flush Shared Pool"
ALTER SYSTEM FLUSH SHARED_POOL;
endsnippet
snippet err
show errors;
endsnippet
snippet sel "Select statement"
SELECT ${0:*} FROM ${1} WHERE 1 = 1;
endsnippet
snippet selc "Select statement"
SELECT COUNT(1) FROM ${1} WHERE ${0};
endsnippet
snippet wrn "Where ROWNNUM"
WHERE ROWNUM <= 10 ${0:AND}
endsnippet
snippet arn "AND ROWNNUM"
AND ROWNUM <= 10 ${0:;}
endsnippet
snippet ppram "Retuns param in wrapped format"
||`!p snip.rv = param(t[1].upper())`$1 $0
endsnippet
snippet dbo "Show output "
DBMS_OUTPUT.put_line('${0}');
endsnippet
snippet dbop "Show Parameter output "
DBMS_OUTPUT.put_line(`!p snip.rv = param(t[1].upper())`$1 $0);
endsnippet
snippet dbl "Log message in Log Table, Change procedure as defined by you"
DEBUG_LOG_PKG.WRITE_LOG(${1:'Test'},${2:$1} ,$0 );
endsnippet
snippet plog "Print Log output "
printlog(`!p snip.rv = param(t[1].upper())`$1 $0);
endsnippet
snippet dut "DBMS_OUTPUT.put_line"
DBMS_UTILITY.get_time;
endsnippet
snippet bc "Bulk collect into"
bulk collect into ${0}
endsnippet
snippet ei "Execute Immediate"
EXECUTE IMMEDIATE '${0:statement}' ;
endsnippet
snippet eitt "Execute Immediate TRUNCATE Table"
EXECUTE IMMEDIATE( 'TRUNCATE TABLE ${0:table}');
endsnippet
snippet eitp "Execute Immediate ALTER Table Truncate partition"
EXECUTE IMMEDIATE( 'ALTER TABLE ${1:table} TRUNCATE PARTITION ${0:partition}');
endsnippet
snippet prmpt "Prompt message"
PROMPT ${1:Creating }...
endsnippet
snippet crseq "Create Sequence"
DROP SEQUENCE ${1:schema}.${2:name}_s;
CREATE SEQUENCE $1.$2_s
START WITH ${3:1}
MAXVALUE 999999999999999999999999999
MINVALUE 1
NOCYCLE
NOCACHE
NOORDER;
endsnippet
snippet crsyn "Create Synonym"
CREATE OR REPLACE SYNONYM ${1:schema}.${2:name} FOR ${3:target}.${0};
endsnippet
snippet crind "Create Index"
DROP INDEX $1.$4;
CREATE INDEX $1.${4:$2_${5}}
ON ${1:schema}.${2:table}(${3}) ${6:TABLESPACE ${0} };
endsnippet
########################################
# Table Operation #
########################################
snippet drtab "Drop Table"
DROP TABLE ${1:schema}.${2:name} CASCADE CONSTRAINTS ${3:PURGE};
endsnippet
snippet crtab "Create Table"
DROP TABLE ${1:schema}.${2:name} CASCADE CONSTRAINTS PURGE;
CREATE TABLE $1.$2
(
${0}
)
${3:TABLESPACE ${4}}
;
endsnippet
snippet ccol "Add VARCHAR2 column to table"
${1:,} ${2:name} VARCHAR2(${0:100})
endsnippet
snippet dcol "Add DATE column to table"
${1:,} ${0:name} DATE
endsnippet
snippet ncol "Add NUMBER column to table"
${1:,} ${0:name} NUMBER
endsnippet
snippet at "Alter Table"
ALTER TABLE ${1:table} ${0}
endsnippet
#########################################
# Declare Types and local variable #
#########################################
snippet tr "Type record"
TYPE t_${1:rec} IS RECORD (${0:/* columns */} );
endsnippet
snippet tt "Type Table"
TYPE t_${1:tbl} IS TABLE OF ${0:table_name}%ROWTYPE INDEX BY BINARY_INTEGER;
endsnippet
snippet tc "Type Cursor"
TYPE t_${1:tbl} IS TABLE OF ${0:cur}%ROWTYPE INDEX BY BINARY_INTEGER;
endsnippet
snippet pn
p_${1} ${2:IN} NUMBER ${3:DEFAULT ${0:NULL}}
endsnippet
snippet pd
p_${1} ${2:IN} DATE ${3:DEFAULT ${0:SYSDATE}}
endsnippet
snippet pc
P_${1} ${2:IN} VARCHAR2 ${3:DEFAULT ${0:NULL}}
endsnippet
snippet ln
l_${1} NUMBER ${2: := ${3} };
endsnippet
snippet ld
l_${1} DATE ${2: := ${3} };
endsnippet
snippet lc
l_${1} VARCHAR2(${2:100}) ${3: := ${4} };
endsnippet
snippet gn
g_${1} NUMBER ${2: := ${3:10} };
endsnippet
snippet gd
g_${1} DATE ${2: := ${3:SYSDATE} };
endsnippet
snippet gc
g_${1} VARCHAR2(${2:100}) ${3: := ${4} };
endsnippet
snippet ltbl
l_tbl_${1} ${0};
endsnippet
snippet lrec
l_rec_${1} ${0};
endsnippet
#########################################
# Condition, Loops #
#########################################
snippet if "If Condition"
IF(${1}) THEN
${0};
END IF;
endsnippet
snippet ife "IF-Else Condition"
IF(${1}) THEN
${2};
ELSIF
${0};
END IF;
endsnippet
snippet els "Else Condition"
ELSIF ${1:condition} THEN
${0};
endsnippet
snippet case "Case statement"
CASE WHEN (${1}) THEN
${2}
WHEN (${3}) THEN
${4}
${0:ELSE}
END
endsnippet
snippet while "While Loop"
WHILE ${1:a} ${2:condition} ${3:b} LOOP
${0};
END LOOP;
endsnippet
snippet fori "For Loop"
FOR ${1:indx} in ${2:1}..${3:10} LOOP
${4};
END LOOP;
endsnippet
snippet fort "Table For Loop"
FOR ${1:indx} in 1..${2:ttb}.count LOOP
${0};
END LOOP;
endsnippet
snippet loop "Loop statement"
LOOP
${0};
END LOOP;
endsnippet
snippet fora "For All Loop"
IF ( ${1:ttbl}.COUNT > 0 ) THEN
BEGIN
FORALL ${2:indx} IN 1 .. $1.COUNT
-- Insert/Update
${0}
EXCEPTION --Exception Block
WHEN OTHERS THEN
l_errmsg := 'Error while Bulk updating, Error : ' || SQLERRM;
RAISE excp_custom;
END;
END IF;
endsnippet
snippet forc "For Cursor Loop"
FOR $1_rec IN ${1:cur} ${2:(${3:param})}
LOOP
${0}
END LOOP; -- End $1
endsnippet
#########################################
# Cursor Operations #
#########################################
snippet dcur "Cursor declaration"
CURSOR ${1:cur} IS
SELECT ${0}
FROM $1
WHERE 1 = 1;
endsnippet
snippet copen "Open Cursor"
OPEN ${1:cursor} ${2:( ${3:param} )};
FETCH $1
INTO ${4:record};
${0}
IF ( $1 %NOTFOUND ) THEN
CLOSE $1;
l_errmsg := 'No records fetched in cursor : $1.';
RAISE excp_custom;
END IF;
CLOSE $1;
endsnippet
snippet copenbc "Open Cursor Bulk collect"
OPEN ${1:cursor} ${2:( ${3:param} )};
FETCH $1
BULK COLLECT INTO ${4:ttbl};
CLOSE $1;
IF ( $4.count = 0 ) THEN
l_errmsg := 'No records fetched in cursor : $1.';
RAISE excp_custom;{0}
END IF;
endsnippet
#########################################
# BEGIN/DECLARE Blocks #
#########################################
snippet decl "Declare Begin block"
DECLARE
${1}
BEGIN
${0:null}
EXCEPTION --Exception Block
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('No Data Found');
WHEN OTHERS THEN
dbms_output.put_line('Error while . Error : '||sqlerrm);
END;
endsnippet
snippet begin "Begin block"
BEGIN
${0}
EXCEPTION --Exception Block
WHEN NO_DATA_FOUND THEN
printlog('No Data Found');
WHEN OTHERS THEN
printlog('Error while . Error : '||sqlerrm);
END;
endsnippet
snippet excp "Exception Block"
EXCEPTION --Exception Block
${0}
WHEN OTHERS THEN
${1};
END;
endsnippet
snippet rae "Raise Application Error"
RAISE_APPLICATION_ERROR(${1:-20000},${0:''});
endsnippet
#########################################
# Procedure/Function calling #
#########################################
snippet crjob "Submit DBMS Job"
-- Submit the job to get the output
BEGIN
DECLARE
vjob INTEGER;
BEGIN
DBMS_JOB.submit( vjob, '${1:procedure}${0:('''')};', SYSDATE );
DBMS_OUTPUT.put_line( 'Job id : ' || vjob );
COMMIT;
END;
END;
endsnippet
snippet whilejob "Submit DBMS Job with While Loop"
-- Submit the job to get the output
BEGIN
DECLARE
vjob INTEGER;
BEGIN
DBMS_JOB.submit ( vjob , '
DECLARE
l_start_date DATE := ''${1:01-Jan-2017}'';
BEGIN
WHILE l_start_date < ''${2:01-Jan-2017}''
LOOP
${3:Procedure}${0:( to_char(l_start_date,''YYYYMMDD'') )};
l_start_date := TRUNC( l_start_date + 1 );
END LOOP;
EXCEPTION --Exception Block
WHEN OTHERS THEN
DBMS_OUTPUT.put_line( ''Error while . Error : '' || SQLERRM );
END;
'
, SYSDATE
);
DBMS_OUTPUT.put_line( 'Job id : ' || vjob );
COMMIT;
END;
END;
endsnippet
#########################################
# Function creation scripts #
#########################################
snippet crprintlog "Create Printlog Procedure"
------------------------------------------------------------------------------------------------
-- PROCEDURE : PRINTLOG
-- Description : This procedure is used to print log messages in Log file, Table and Console
------------------------------------------------------------------------------------------------
PROCEDURE printlog (p_message IN VARCHAR2)
IS
l_errmsg VARCHAR2 (10000);
BEGIN
l_errmsg := SUBSTR ( p_message, 1, g_err_length_limit);
fnd_file.put_line ( fnd_file.LOG, l_errmsg); -- Debug log file
DBMS_OUTPUT.put_line (l_errmsg); -- Console output
DEBUG_LOG_PKG.WRITE_LOG(g_package_name,g_proc_name,p_message); -- Debug table
END printlog;
endsnippet
snippet crgeterr "Create get_errmsg function"
-- Form the error message for when others
FUNCTION get_errmsg( p_message IN VARCHAR2 DEFAULT NULL )
RETURN VARCHAR2
IS
BEGIN
RETURN 'Error occured in ' || g_package_name || '.' || g_proc_name || '. ' || NVL( p_message, '' ) || ' Error : ' || SQLERRM;
EXCEPTION --Exception Block
WHEN OTHERS THEN
printlog( 'Error while forming messgage. Error : ' || SQLERRM );
RETURN NULL;
END;
endsnippet
snippet crpksfunc "Create package specification function"
------------------------------------------------------------------------------------------------
-- Function : `!p snip.rv = t[1].upper()`
-- Description : This Function will ${4:description}.
`!p snip.rv=hdr_params(t[3]) `
------------------------------------------------------------------------------------------------
FUNCTION ${1:func} ${2:(${3:params})}
RETURN ${0};
endsnippet
snippet crpksproc "Create package specification procedure"
------------------------------------------------------------------------------------------------
-- PROCEDURE : `!p snip.rv = t[1].upper()`
-- Description : This Procedure will ${4:description}.
`!p snip.rv=hdr_params(t[3],0) `
------------------------------------------------------------------------------------------------
PROCEDURE ${1:proc} ${2:(${3:params})} ;
endsnippet
snippet crpkbfunc "Create package body function"
------------------------------------------------------------------------------------------------
-- Function : `!p snip.rv = t[1].upper()`
-- Description : This Function will ${8:description}.
`!p snip.rv=hdr_params(t[3],2) `
------------------------------------------------------------------------------------------------
FUNCTION ${1:func} ${2:(${3:params})}
RETURN ${4}
IS
-- Declare Cursors
-- Declare Variables
${5:l_} $4 ${6:( ${7:length} )};
BEGIN
-- Initialize
g_proc_name := '`!p snip.rv = t[1].upper()`';
${0}
-- Return value
RETURN $5 ;
EXCEPTION
WHEN OTHERS
THEN
RETURN NULL;
END $1;
endsnippet
snippet crpkbproc "Create package body procedure"
------------------------------------------------------------------------------------------------
-- PROCEDURE : `!p snip.rv = t[1].upper()`
-- Description : This Procedure will ${4:description}.
`!p snip.rv=hdr_params(t[3]) `
------------------------------------------------------------------------------------------------
PROCEDURE ${1:proc} ${2:(${3:params})}
IS
-- Declare cursors
-- Declare Out and exception variables
l_errmsg VARCHAR2( 10000 ) := null;
excp_skip EXCEPTION;
-- Declare Varibales
BEGIN
-- Initializing out parameters
g_proc_name := '`!p snip.rv = t[1].upper()`';
${0}
EXCEPTION -- Exception block of Procedure
WHEN excp_custom THEN
ROLLBACK;
printlog( l_errmsg );
WHEN OTHERS THEN
ROLLBACK;
l_errmsg := get_errmsg;
printlog( l_errmsg );
END $1;
endsnippet
snippet crpks "Create Package specification"
CREATE OR REPLACE PACKAGE ${1}.${2}
AS
-- #############################################################################
-- # Copyright (c) `!p snip.rv = dyear()` ${3}
-- # All rights reserved
-- #
-- ############################################################################
-- #
-- # Application : $1
-- # File Name: : `!p snip.rv = t[2].upper()`.pks
-- # Exec Method : PL/SQL Stored - Procedure
-- # Description : Package used for ${4}
-- #
-- # Change History
-- # -----------------------------------------------------------------------
-- # Version Date Author Remarks
-- # ======= =========== ============= ============================
-- # 1.0 `!p snip.rv = today()` Amit Maindola Initial Version
-- #
-- #
-- ############################################################################
${0}
END $2;
/
SHOW ERROR
/
endsnippet
snippet crpkb "Create package body"
CREATE OR REPLACE PACKAGE BODY ${1}.${2}
IS
-- #############################################################################
-- # Copyright (c) `!p snip.rv = dyear()` ${3}
-- # All rights reserved
-- #
-- ############################################################################
-- #
-- # Application : $1
-- # File Name: : `!p snip.rv = t[2].upper()`.pkb
-- # Exec Method : PL/SQL Stored - Procedure
-- # Description : Package used for ${4}
-- #
-- # Change History
-- # -----------------------------------------------------------------------
-- # Version Date Author Remarks
-- # ======= =========== ============= ============================
-- # 1.0 `!p snip.rv = today()` Amit Maindola Initial Version
-- #
-- #
-- ############################################################################
-- Declare Global Variables
g_sysdate DATE := SYSDATE;
g_delimiter VARCHAR2( 30 ) := ' ';
g_err_length_limit NUMBER := 1500;
g_package_name CONSTANT VARCHAR2(30) := '`!p snip.rv = t[2].upper()`';
g_proc_name VARCHAR2(100) := NULL;
excp_custom EXCEPTION;
-- Declare User Global Types
------------------------------------------------------------------------------------------------
-- PROCEDURE : PRINTLOG
-- Description : This procedure is used to print log messages
------------------------------------------------------------------------------------------------
PROCEDURE printlog( p_message IN VARCHAR2 )
IS
BEGIN
DBMS_OUTPUT.PUT_LINE( p_message );
DEBUG_LOG_PKG.WRITE_LOG(g_package_name,g_proc_name,p_message);
END printlog;
-- Form the error message for when others
FUNCTION get_errmsg( p_message IN VARCHAR2 DEFAULT NULL )
RETURN VARCHAR2
IS
BEGIN
RETURN 'Error occured in ' || g_package_name || '.' || g_proc_name || '. ' || NVL( p_message, '' ) || ' Error : ' || SQLERRM;
EXCEPTION --Exception Block
WHEN OTHERS THEN
printlog( 'Error while forming messgage. Error : ' || SQLERRM );
RETURN NULL;
END;
END $2;
/
SHOW ERROR
/
endsnippet
snippet crproc "Create procedure"
CREATE OR REPLACE PROCEDURE ${1:schema}.${2:name} ${3:( ${4:prams} )}
-- #############################################################################
-- # Copyright (c) `!p snip.rv = dyear()` ${5}
-- # All rights reserved
-- #
-- ############################################################################
-- #
-- # Application : $1
-- # File Name: : `!p snip.rv = t[2].upper()`.prc
-- # Exec Method : PL/SQL Stored - Procedure
-- # Description : Package used for ${6}
-- #
-- # Change History
-- # -----------------------------------------------------------------------
-- # Version Date Author Remarks
-- # ======= =========== ============= ============================
-- # 1.0 `!p snip.rv = today()` Amit Maindola Initial Version
-- #
-- #
-- ############################################################################
is
g_proc_name VARCHAR2(30) := '`!p snip.rv = t[2].upper()`';
l_errmsg VARCHAR2( 10000 ) := null;
excp_custom EXCEPTION;
-- Declare cursors
-- Declare Varibales
BEGIN
-- Initializing out parameters
${0}
EXCEPTION -- Exception block of Procedure
WHEN excp_custom THEN
ROLLBACK;
DEBUG_LOG_PKG.WRITE_LOG(g_proc_name,g_proc_name ,l_errmsg );
WHEN OTHERS THEN
ROLLBACK;
l_errmsg := 'Exception in procedure. '||SQLERRM;
DEBUG_LOG_PKG.WRITE_LOG(g_proc_name,g_proc_name ,l_errmsg );
END $2;
endsnippet

View file

@ -29,7 +29,7 @@ Return From Keyword ${1:${optional return value}}
endsnippet
snippet rfki "Return From Keyword If"
Return From Keyword If ${1:${condition}} ${2:${optional return value}}
Return From Keyword If '\${${1:rc}}' != '${2:abc}' ${3:${optional return value}}
endsnippet
snippet rk "Run Keyword"
@ -54,7 +54,7 @@ Run Keyword And Return ${1:${kw}} ${2:${args}}
endsnippet
snippet rkari "Run Keyword And Return If"
Run Keyword And Return If ${1:{condition}} ${2:${kw}} ${3:${args}}
Run Keyword And Return If '\${${1:rc}}' != '${2:abc}' ${3:${kw}} ${4:${args}}
endsnippet
snippet rkars "Run Keyword And Return Status"
@ -62,9 +62,12 @@ snippet rkars "Run Keyword And Return Status"
endsnippet
snippet rki "Run Keyword If"
Run Keyword If ${1:${rc} < 0} ${2:${VISUAL:Some keyword returning a value}}
... ELSE IF ${3:'${str}' == 'abc'} ${4:Another keyword}
... ELSE ${5:Final keyword}
Run Keyword If '\${${1:rc}}' != '${2:abc}'
... ${3:${VISUAL:Some keyword returning a value}}
... ELSE IF '\${${4:str}}' != '${5:def}'
... ${6:Another keyword}
... ELSE
... ${7:Final keyword}
endsnippet
snippet rkiactf "Run Keyword If Any Critical Tests Failed"
@ -102,7 +105,7 @@ Run Keywords
endsnippet
snippet rku "Run Keyword Unless"
Run Keyword Unless ${1:${condition}} ${2:${kw}} ${3:${args}}
Run Keyword Unless '\${${1:rc}}' != '${2:abc}' ${3:${kw}} ${4:${args}}
endsnippet
snippet sgv "Set Global Variable"
@ -130,7 +133,9 @@ snippet sv "Set Variable"
endsnippet
snippet svi "Set Variable If"
\${${1:var}}= Set Variable If ${2:${condition}} ${3:${value true}} ${4:${value false}}
\${${1:var}}= Set Variable If '\${${2:rc}}' != '${3:abc}'
`!p snip.rv = '...' + ' ' * (len(t[1]) + 23)` ${4:${value true}}
`!p snip.rv = '...' + ' ' * (len(t[1]) + 23)` ${5:${value false}}
endsnippet
snippet wuks "Wait Until Keyword Succeeds"

View file

@ -113,28 +113,28 @@ snippet it "Individual item" b
endsnippet
snippet part "Part" b
\part{${1:part name}}
\part{${1:part name}}%
\label{prt:${2:${1/(\w+)|\W+/(?1:\L$0\E:_)/ga}}}
$0
endsnippet
snippet cha "Chapter" b
\chapter{${1:chapter name}}
\chapter{${1:chapter name}}%
\label{cha:${2:${1/\\\w+\{(.*?)\}|\\(.)|(\w+)|([^\w\\]+)/(?4:_:\L$1$2$3\E)/ga}}}
$0
endsnippet
snippet sec "Section" b
\section{${1:section name}}
\section{${1:section name}}%
\label{sec:${2:${1/\\\w+\{(.*?)\}|\\(.)|(\w+)|([^\w\\]+)/(?4:_:\L$1$2$3\E)/ga}}}
$0
endsnippet
snippet sec* "Section" b
\section*{${1:section name}}
\section*{${1:section name}}%
\label{sec:${2:${1/\\\w+\{(.*?)\}|\\(.)|(\w+)|([^\w\\]+)/(?4:_:\L$1$2$3\E)/ga}}}
${0}
@ -142,42 +142,42 @@ endsnippet
snippet sub "Subsection" b
\subsection{${1:subsection name}}
\subsection{${1:subsection name}}%
\label{sub:${2:${1/\\\w+\{(.*?)\}|\\(.)|(\w+)|([^\w\\]+)/(?4:_:\L$1$2$3\E)/ga}}}
$0
endsnippet
snippet sub* "Subsection" b
\subsection*{${1:subsection name}}
\subsection*{${1:subsection name}}%
\label{sub:${2:${1/\\\w+\{(.*?)\}|\\(.)|(\w+)|([^\w\\]+)/(?4:_:\L$1$2$3\E)/ga}}}
${0}
endsnippet
snippet ssub "Subsubsection" b
\subsubsection{${1:subsubsection name}}
\subsubsection{${1:subsubsection name}}%
\label{ssub:${2:${1/\\\w+\{(.*?)\}|\\(.)|(\w+)|([^\w\\]+)/(?4:_:\L$1$2$3\E)/ga}}}
$0
endsnippet
snippet ssub* "Subsubsection" b
\subsubsection*{${1:subsubsection name}}
\subsubsection*{${1:subsubsection name}}%
\label{ssub:${2:${1/\\\w+\{(.*?)\}|\\(.)|(\w+)|([^\w\\]+)/(?4:_:\L$1$2$3\E)/ga}}}
${0}
endsnippet
snippet par "Paragraph" b
\paragraph{${1:paragraph name}}
\paragraph{${1:paragraph name}}%
\label{par:${2:${1/\\\w+\{(.*?)\}|\\(.)|(\w+)|([^\w\\]+)/(?4:_:\L$1$2$3\E)/ga}}}
$0
endsnippet
snippet subp "Subparagraph" b
\subparagraph{${1:subparagraph name}}
\subparagraph{${1:subparagraph name}}%
\label{par:${2:${1/\\\w+\{(.*?)\}|\\(.)|(\w+)|([^\w\\]+)/(?4:_:\L$1$2$3\E)/ga}}}
$0

View file

@ -204,6 +204,8 @@ snippet try
}catch(${1}) {
}
snippet af auto function
auto ${1:name}(${2}) -> ${3:void}
{
${0}
};

View file

@ -4,16 +4,16 @@
# PowerShell Class
snippet class
class {
[string] ${0:FirstName}
[string] ${1:FirstName}
}
# PowerShell Advanced Function
snippet function
function {0:name} {
function ${1:name} {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string] $Param1
[string] ${2:Param}
)
begin {
@ -29,30 +29,74 @@ snippet function
# PowerShell Splatting
snippet splatting
$Params = @{
${0:Param1} = 'Value1'
${1:Param2} = 'Value2'
${1:Param1} = '{2:Value1}'
${3:Param2} = '{4:Value2}'
}
${3:CommandName}
${5:CommandName} @Params
# PowerShell Enumeration
snippet enum
enum ${0:name} {
${1:item1}
${2:item2}
enum ${1:name} {
${2:item1}
${3:item2}
}
# PowerShell if..then
snippet if
if (${0:condition}) {
${1:statement}
if (${1:condition}) {
${2:statement}
}
# PowerShell if..else
snippet ife
if ( ${1:condition} ) {
${2}
}
else {
${3}
}
# PowerShell While Loop
snippet while
while (${0:condition}) {
${1:statement}
while (${1:condition}) {
${2:statement}
}
# PowerShell Filter..Sort
snippet filtersort
${0:command} | Where-Object -FilterScript { $PSItem.${1:property} -${2:operator} '${3:expression}' } | Sort-Object -Property ${4:sortproperty}
${1:command} | Where-Object -FilterScript { $PSItem.${2:property} -${3:operator} '${4:expression}' } | Sort-Object -Property ${5:sortproperty}
# PowerShell foreach
snippet foreach
foreach ( $${1:iterator} in $${2:collection} ) {
${3:statement}
}
# PowerShell export-csv
snippet epcsv
Export-CSV -NoTypeInformation -Path ${1:path}
# Powershell Comment Based Help
snippet help
<#
.SYNOPSIS
${1:Short Description}
.DESCRIPTION
${2:Full Description}
.PARAMETER ${3:Param1}
${4: $3 usage}
.EXAMPLE
${5:Example}
.NOTES
${6:notes}
.LINK
${7:online help}
#>
# Powershell switch statement
snippet switch
switch ( ${1:test} ){
${2:condition1} { ${3:action} }
${4:condition2} { ${5:action} }
default { ${6:action} }

View file

@ -1,87 +1,60 @@
# Snippets for use with VIM and http://www.vim.org/scripts/script.php?script_id=2540
#
# Please contact R.I.Pienaar <rip@devco.net> for additions and feedback,
# Please contact Jorge Vidal <im@jor.ge> for additions and feedback,
# see it in action @ http://www.devco.net/archives/2009/09/22/vim_and_puppet.php
# Many thanks to the original author R.I.Pienaar <rip@devco.net>
# Header to match http://docs.puppetlabs.com/guides/style_guide.html#puppet-doc
# Header using Puppet Strings (YARD tags) https://puppet.com/docs/puppet/latest/modules_documentation.html
# More info: https://github.com/puppetlabs/puppet-strings
snippet classheader
# == Class: ${1:`vim_snippets#Filename(expand('%:p:s?.*modules/??:h:h'), 'name')`}
# ${1:`vim_snippets#Filename(expand('%:p:s?.*modules/??:h:h'), 'class-name')`}
# ${2:A description of what this class does}
#
# ${2:Full description of class $1 here}
# @summary ${3:A short summary of the purpose of this class}
#
# === Parameters
# @param ${4:parameter1} [${5:String}]
# ${6:Explanation of what this parameter affects.}
#
# Document parameters here.
#
# [*parameter1*]
# Explanation of what this parameter affects and what it defaults to.
# e.g. "Specify one or more upstream ntp servers as an array."
#
# === Variables
#
# Here you should define a list of variables that this module would require.
#
# [*variable1*]
# Explanation of how this variable affects the funtion of this class and
# if it has a default. e.g. "The parameter enc_ntp_servers must be set by the
# External Node Classifier as a comma separated list of hostnames."
#
# === Examples
# @example Simple use
# class { '$1': }
#
# @example Use with params
# class { '$1':
# parameter1 => [ 'just', 'an', 'example', ]
# $$4 => '${7:undef}',
# }
#
# === Authors
# @author ${8:`g:snips_author`} <${9:`g:snips_email`}>
#
# `g:snips_author` <`g:snips_email`>
# @note Copyright `strftime("%Y")` $8
#
# === Copyright
#
# Copyright `strftime("%Y")` `g:snips_author`
#
class $1 (${3}){
${4}
class $1(
$$4 = undef,
) {
${0}
}
snippet defheader
# == Define: ${1:`vim_snippets#Filename(expand('%:p:s?.*modules/??:r:s?/manifests/?::?'), 'name')`}
# ${1:`vim_snippets#Filename(expand('%:p:s?.*modules/??:h:h'), 'define-name')`}
# ${2:A description of what this define does}
#
# ${2:Full description of defined resource type $1 here}
# @summary ${3:A short summary of the purpose of this define}
#
# === Parameters
#
# Document parameters here
#
# [*namevar*]
# If there is a parameter that defaults to the value of the title string
# when not explicitly set, you must always say so. This parameter can be
# referred to as a "namevar," since it's functionally equivalent to the
# namevar of a core resource type.
#
# [*basedir*]
# Description of this variable. For example, "This parameter sets the
# base directory for this resource type. It should not contain a trailing
# slash."
#
# === Examples
#
# Provide some examples on how to use this type:
# @param ${4:parameter1} [${5:String}]
# ${6:Explanation of what this parameter affects.}
#
# @example Simple use
# $1 { 'namevar':
# basedir => '/tmp/src',
# $$4 => '${7:undef}',
# }
#
# === Authors
# @author ${8:`g:snips_author`} <${9:`g:snips_email`}>
#
# `g:snips_author` <`g:snips_email`>
# @note Copyright `strftime("%Y")` $8
#
# === Copyright
#
# Copyright `strftime("%Y")` `g:snips_author`
#
define $1(${3}) {
${4}
define $1(
$$4 = undef,
) {
${0}
}
# Language Constructs

View file

@ -115,21 +115,21 @@ snippet try Try/Except
${1:${VISUAL}}
except ${2:Exception} as ${3:e}:
${0:raise $3}
snippet try Try/Except/Else
snippet trye Try/Except/Else
try:
${1:${VISUAL}}
except ${2:Exception} as ${3:e}:
${4:raise $3}
else:
${0}
snippet try Try/Except/Finally
snippet tryf Try/Except/Finally
try:
${1:${VISUAL}}
except ${2:Exception} as ${3:e}:
${4:raise $3}
finally:
${0}
snippet try Try/Except/Else/Finally
snippet tryef Try/Except/Else/Finally
try:
${1:${VISUAL}}
except ${2:Exception} as ${3:e}:

View file

@ -42,10 +42,10 @@ snippet defcreate
if @$1.save
flash[:notice] = '$2 was successfully created.'
format.html { redirect_to(@$1) }
format.xml { render xml: @$1, status: :created, location: @$1 }
format.json { render json: @$1, status: :created, location: @$1 }
else
format.html { render action: 'new' }
format.xml { render xml: @$1.errors, status: :unprocessable_entity }
format.json { render json: @$1.errors, status: :unprocessable_entity }
end
end
end
@ -56,7 +56,7 @@ snippet defdestroy
respond_to do |format|
format.html { redirect_to($1s_url) }
format.xml { head :ok }
format.json { head :ok }
end
end
snippet defedit
@ -69,7 +69,7 @@ snippet defindex
respond_to do |format|
format.html # index.html.erb
format.xml { render xml: @$1s }
format.json { render json: @$1s }
end
end
snippet defnew
@ -78,7 +78,7 @@ snippet defnew
respond_to do |format|
format.html # new.html.erb
format.xml { render xml: @$1 }
format.json { render json: @$1 }
end
end
snippet defshow
@ -87,7 +87,7 @@ snippet defshow
respond_to do |format|
format.html # show.html.erb
format.xml { render xml: @$1 }
format.json { render json: @$1 }
end
end
snippet defupdate
@ -98,10 +98,10 @@ snippet defupdate
if @$1.update($1_params)
flash[:notice] = '$2 was successfully updated.'
format.html { redirect_to(@$1) }
format.xml { head :ok }
format.json { head :ok }
else
format.html { render action: 'edit' }
format.xml { render xml: @$1.errors, status: :unprocessable_entity }
format.json { render json: @$1.errors, status: :unprocessable_entity }
end
end
end

View file

@ -292,7 +292,9 @@ snippet block block environment
\\begin{block}{${1:title}}
${0:${VISUAL}}
\\end{block}
snippet alert alertblock environment
snippet alert alert text
\\alert{${1:${VISUAL:text}}} ${0}
snippet alertblock alertblock environment
\\begin{alertblock}{${1:title}}
${0:${VISUAL}}
\\end{alertblock}
@ -309,6 +311,12 @@ snippet col2 two-column environment
${0}
\\end{column}
\\end{columns}
snippet multicol2 two-column environment with multicol
\\begin{multicols}{2}
${1}
\columnbreak
${0}
\\end{multicols}
snippet \{ \{ \}
\\{ ${0} \\}
#delimiter

View file

@ -146,24 +146,13 @@ let g:go_fmt_command = "goimports"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" => Syntastic (syntax checker)
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Python
let g:syntastic_python_checkers=['pyflakes']
let g:ale_linters = {
\ 'javascript': ['jshint'],
\ 'python': ['flake8'],
\ 'go': ['go', 'golint', 'errcheck']
\}
" Javascript
let g:syntastic_javascript_checkers = ['jshint']
" Go
let g:syntastic_auto_loc_list = 1
let g:syntastic_go_checkers = ['go', 'golint', 'errcheck']
" Custom CoffeeScript SyntasticCheck
func! SyntasticCheckCoffeescript()
let l:filename = substitute(expand("%:p"), '\(\w\+\)\.coffee', '.coffee.\1.js', '')
execute "tabedit " . l:filename
execute "SyntasticCheck"
execute "Errors"
endfunc
nnoremap <silent> <leader>c :call SyntasticCheckCoffeescript()<cr>
nmap <silent> <leader>a <Plug>(ale_next_wrap)
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""