1
0
Fork 0
mirror of synced 2024-11-22 00:35:35 -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` * [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 * [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 * [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-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-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 * [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 endif
if exists("g:ackpreview") " if auto preview in on, remap j and k keys 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> j j<CR><C-W><C-P>
nnoremap <buffer> <silent> k k<CR><C-W><C-W> nnoremap <buffer> <silent> k k<CR><C-W><C-P>
nmap <buffer> <silent> <Down> j nmap <buffer> <silent> <Down> j
nmap <buffer> <silent> <Up> k nmap <buffer> <silent> <Up> k
endif endif

View file

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

View file

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

View file

@ -688,8 +688,8 @@ hi! link EasyMotionShade Comment
" }}} " }}}
" Sneak: {{{ " Sneak: {{{
autocmd ColorScheme gruvbox hi! link Sneak Search hi! link Sneak Search
autocmd ColorScheme gruvbox hi! link SneakLabel Search hi! link SneakLabel Search
" }}} " }}}
" Indent Guides: {{{ " 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
**Environment:** _Describe your Vim/NERDTree setup._ <!--- Describe your Vim/NERDTree setup. -->
>* Operating System: … * Operating System:
>* Vim version `:version`: … * Vim version `:version`:
>* NERDTree version `git rev-parse --short HEAD`: … * NERDTree version `git rev-parse --short HEAD`:
>* NERDTree settings applied in your vimrc, if any: * 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)
>… ### Possible Fix
<!--- If you have explored the code, share what you've found. -->
---
**Optional**
**Screenshot(s):**
>…
**Possible Fix:** _(Have you poked around in the code?)_
>…

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 |'NERDTreeCreatePrefix'| Specify a prefix to be used when creating the
NERDTree window. 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* 3.2. Customisation details *NERDTreeOptionDetails*

View file

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

View file

@ -245,8 +245,14 @@ function! s:Path.delete()
if v:shell_error != 0 if v:shell_error != 0
throw "NERDTree.PathDeletionError: Could not delete directory: '" . self.str() . "'" throw "NERDTree.PathDeletionError: Could not delete directory: '" . self.str() . "'"
endif endif
else
if exists('g:NERDTreeRemoveFileCmd')
let cmd = g:NERDTreeRemoveFileCmd . self.str({'escape': 1})
let success = system(cmd)
else else
let success = delete(self.str()) let success = delete(self.str())
endif
if success != 0 if success != 0
throw "NERDTree.PathDeletionError: Could not delete file: '" . self.str() . "'" throw "NERDTree.PathDeletionError: Could not delete file: '" . self.str() . "'"
endif 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 syn match coffeeException /\<\%(try\|catch\|finally\)\>/ display
hi def link coffeeException Exception 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 \ display
" The `own` keyword is only a keyword after `for`. " The `own` keyword is only a keyword after `for`.
syn match coffeeKeyword /\<for\s\+own\>/ contained containedin=coffeeRepeat 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 lot more than other projects, and thus I'm especially meticulous here about
maintaining a clean, readable, history. Squash and force push any requested maintaining a clean, readable, history. Squash and force push any requested
changes to a pull request. And if your [commit message changes to a pull request. And if your [commit message
sucks](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), sucks](https://commit.style), I'm not going to accept it. Period.
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 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 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 ## 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: Features:
* Shows signs for added, modified, and removed lines. * 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. * Runs the diffs asynchronously where possible.
* Ensures signs are always as up to date as possible (but without running more than necessary). * Ensures signs are always up to date.
* Quick jumping between blocks of changed lines ("hunks"). * Quick jumping between blocks of changed lines ("hunks").
* Stage/undo/preview individual hunks. * Stage/undo/preview individual hunks.
* Provides a hunk text object. * Provides a hunk text object.
@ -21,9 +23,8 @@ Features:
Constraints: Constraints:
* Supports git only. * 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`.
If you work with other version control systems, I recommend [vim-signify](https://github.com/mhinz/vim-signify).
### Screenshot ### Screenshot
@ -39,7 +40,7 @@ In the screenshot above you can see:
### Installation ### 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. You install vim-gitgutter like any other vim plugin.
@ -100,16 +101,13 @@ cp -r vim-gitgutter/* ~/.vim/
See `:help add-global-plugin`. 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 ### 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 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 #### 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). 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 ```viml
let g:gitgutter_max_signs = 500 " default value 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. 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 ### Customisation
You can customise: You can customise:
@ -351,7 +322,7 @@ If you use an alternative to grep, you can tell vim-gitgutter to use it here.
```viml ```viml
" Default: " Default:
let g:gitgutter_grep_command = 'grep' let g:gitgutter_grep = 'grep'
``` ```
#### To turn off vim-gitgutter by default #### To turn off vim-gitgutter by default
@ -490,57 +461,53 @@ nmap <silent> [c :call PrevHunkAllBuffers()<CR>
### FAQ ### 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? > 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: 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.
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.
> Why are the colours in the sign column weird? > 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. 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)? > 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. 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: Here are some things you can check:
* `:echo system("git --version")` succeeds. * 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.
* Your git config is compatible with the version of git returned by the command above. * Verify `:echo system("git --version")` succeeds.
* Your Vim supports signs (`:echo has('signs')` should give `1`). * Verify your git config is compatible with the version of git returned by the command above.
* Your file is being tracked by git and has unstaged changes. * Verify your Vim supports signs (`:echo has('signs')` should give `1`).
* If you have aliased or configured `grep` to use any flags, add `let g:gitgutter_grep_command = 'grep'` to your `~/.vimrc`. * 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. * 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 ### Shameless Plug
@ -561,4 +528,4 @@ Copyright Andrew Stewart, AirBlade Software Ltd. Released under the MIT licence
[pathogen]: https://github.com/tpope/vim-pathogen [pathogen]: https://github.com/tpope/vim-pathogen
[siv]: http://pluralsight.com/training/Courses/TableOfContents/smash-into-vim [siv]: http://pluralsight.com/training/Courses/TableOfContents/smash-into-vim
[airblade]: http://airbladesoftware.com/peepcode-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 {{{ " Primary functions {{{
function! gitgutter#all() abort function! gitgutter#all(force) abort
for buffer_id in gitgutter#utility#dedup(tabpagebuflist()) for bufnr in s:uniq(tabpagebuflist())
let file = expand('#' . buffer_id . ':p') let file = expand('#'.bufnr.':p')
if !empty(file) if !empty(file)
call gitgutter#process_buffer(buffer_id, 0) call gitgutter#init_buffer(bufnr)
call gitgutter#process_buffer(bufnr, a:force)
endif endif
endfor endfor
endfunction 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) " Finds the file's path relative to the repo root.
if gitgutter#utility#is_active() function! gitgutter#init_buffer(bufnr)
if g:gitgutter_sign_column_always if gitgutter#utility#is_active(a:bufnr)
call gitgutter#sign#add_dummy_sign() 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
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 try
if !a:realtime || gitgutter#utility#has_fresh_changes() let diff = gitgutter#diff#run_diff(a:bufnr, 0)
let diff = gitgutter#diff#run_diff(a:realtime || gitgutter#utility#has_unsaved_changes(), 0) catch /gitgutter not tracked/
if diff != 'async' call gitgutter#debug#log('Not tracked: '.gitgutter#utility#file(a:bufnr))
call gitgutter#handle_diff(diff) catch /gitgutter diff failed/
endif call gitgutter#debug#log('Diff failed: '.gitgutter#utility#file(a:bufnr))
endif call gitgutter#hunk#reset(a:bufnr)
catch /diff failed/
call gitgutter#debug#log('diff failed')
call gitgutter#hunk#reset()
endtry endtry
execute "silent doautocmd" s:nomodeline "User GitGutter"
else if diff != 'async'
call gitgutter#hunk#reset() call gitgutter#diff#handler(a:bufnr, diff)
endif endif
call gitgutter#utility#restore_shell() endif
endif
endfunction 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 function! gitgutter#disable() abort
" get list of all buffers (across all tabs) " get list of all buffers (across all tabs)
let buflist = [] let buflist = []
@ -69,13 +56,10 @@ function! gitgutter#disable() abort
call extend(buflist, tabpagebuflist(i + 1)) call extend(buflist, tabpagebuflist(i + 1))
endfor endfor
for buffer_id in gitgutter#utility#dedup(buflist) for bufnr in s:uniq(buflist)
let file = expand('#' . buffer_id . ':p') let file = expand('#'.bufnr.':p')
if !empty(file) if !empty(file)
call gitgutter#utility#set_buffer(buffer_id) call s:clear(bufnr)
call gitgutter#sign#clear_signs()
call gitgutter#sign#remove_dummy_sign(1)
call gitgutter#hunk#reset()
endif endif
endfor endfor
@ -84,7 +68,7 @@ endfunction
function! gitgutter#enable() abort function! gitgutter#enable() abort
let g:gitgutter_enabled = 1 let g:gitgutter_enabled = 1
call gitgutter#all() call gitgutter#all(1)
endfunction endfunction
function! gitgutter#toggle() abort function! gitgutter#toggle() abort
@ -97,163 +81,33 @@ endfunction
" }}} " }}}
" Line highlights {{{ function! s:has_fresh_changes(bufnr) abort
return getbufvar(a:bufnr, 'changedtick') != gitgutter#utility#getbufvar(a:bufnr, 'tick')
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!
endfunction endfunction
function! gitgutter#line_highlights_enable() abort function! s:reset_tick(bufnr) abort
let old_highlight_lines = g:gitgutter_highlight_lines call gitgutter#utility#setbufvar(a:bufnr, 'tick', 0)
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!
endfunction endfunction
function! gitgutter#line_highlights_toggle() abort function! s:clear(bufnr)
if g:gitgutter_highlight_lines call gitgutter#sign#clear_signs(a:bufnr)
call gitgutter#line_highlights_disable() call gitgutter#sign#remove_dummy_sign(a:bufnr, 1)
call gitgutter#hunk#reset(a:bufnr)
call s:reset_tick(a:bufnr)
endfunction
if exists('*uniq') " Vim 7.4.218
function! s:uniq(list)
return uniq(sort(a:list))
endfunction
else else
call gitgutter#line_highlights_enable() function! s:uniq(list)
let processed = []
for e in a:list
if index(processed, e) == -1
call add(processed, e)
endif endif
endfor
return processed
endfunction 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()
endif 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
" }}}

View file

@ -11,10 +11,13 @@ function! gitgutter#async#available()
endfunction endfunction
function! gitgutter#async#execute(cmd) abort function! gitgutter#async#execute(cmd, bufnr, handler) abort
call gitgutter#debug#log('[async] '.a:cmd)
let options = { let options = {
\ 'stdoutbuffer': [], \ 'stdoutbuffer': [],
\ 'buffer': gitgutter#utility#bufnr() \ 'buffer': a:bufnr,
\ 'handler': a:handler
\ } \ }
let command = s:build_command(a:cmd) let command = s:build_command(a:cmd)
@ -58,33 +61,13 @@ function! s:on_stdout_nvim(_job_id, data, _event) dict abort
endfunction endfunction
function! s:on_stderr_nvim(_job_id, _data, _event) dict abort function! s:on_stderr_nvim(_job_id, _data, _event) dict abort
" Backward compatibility for nvim < 0.2.0 call self.handler.err(self.buffer)
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'))
endfunction endfunction
function! s:on_exit_nvim(_job_id, _data, _event) dict abort function! s:on_exit_nvim(_job_id, exit_code, _event) dict abort
" Backward compatibility for nvim < 0.2.0 if !a:exit_code
if !has('nvim-0.2.0') call self.handler.out(self.buffer, join(self.stdoutbuffer, "\n"))
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))
endif 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 endfunction
@ -92,22 +75,15 @@ function! s:on_stdout_vim(_channel, data) dict abort
call add(self.stdoutbuffer, a:data) call add(self.stdoutbuffer, a:data)
endfunction endfunction
function! s:on_stderr_vim(_channel, _data) dict abort function! s:on_stderr_vim(channel, _data) dict abort
call s:buffer_exec(self.buffer, function('gitgutter#hunk#reset')) 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 endfunction
function! s:on_exit_vim(_channel) dict abort function! s:on_exit_vim(_channel) dict abort
call s:buffer_exec(self.buffer, function('gitgutter#handle_diff', [gitgutter#utility#stringify(self.stdoutbuffer)])) call self.handler.out(self.buffer, join(self.stdoutbuffer, "\n"))
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)
endfunction endfunction

View file

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

View file

@ -1,152 +1,176 @@
if exists('g:gitgutter_grep_command') let s:nomodeline = (v:version > 703 || (v:version == 703 && has('patch442'))) ? '<nomodeline>' : ''
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:hunk_re = '^@@ -\(\d\+\),\?\(\d*\) +\(\d\+\),\?\(\d*\) @@' 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_index = tempname()
let s:temp_buffer = tempname() let s:temp_buffer = tempname()
" Returns a diff of the buffer. " Returns a diff of the buffer.
" "
" The way to get the diff depends on whether the buffer is saved or 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:
" * 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:
" "
" git diff myfileA myfileB " 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 " git show :myfile > myfileA
" "
" The second instance is the buffer contents. Ideally we would pass this to " and myfileB is the buffer contents.
" 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
" "
" After running the diff we pass it through grep where available to reduce " 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 " subsequent processing by the plugin. If grep is not available the plugin
" does the filtering instead. " 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. " Wrap compound commands in parentheses to make Windows happy.
" bash doesn't mind the parentheses. " bash doesn't mind the parentheses.
let cmd = '(' let cmd = '('
let bufnr = gitgutter#utility#bufnr() " Append buffer number to avoid race conditions between writing and reading
let tracked = gitgutter#utility#getbufvar(bufnr, 'tracked', 0) " i.e. tracked by git " the files when asynchronously processing multiple buffers.
if !tracked "
" Don't bother trying to realtime-diff an untracked file. " Without the buffer number, index_file would have a race in the shell
" NOTE: perhaps we should pull this guard up to the caller? " between the second process writing it (with git-show) and the first
if a:realtime " reading it (with git-diff).
throw 'diff failed' let index_file = s:temp_index.'.'.a:bufnr
else
let cmd .= g:gitgutter_git_executable.' ls-files --error-unmatch '.gitgutter#utility#shellescape(gitgutter#utility#filename()).' && ('
endif
endif
if a:realtime " Without the buffer number, buff_file would have a race between the
let blob_name = g:gitgutter_diff_base.':'.gitgutter#utility#shellescape(gitgutter#utility#file_relative_to_repo_root()) " second gitgutter#process_buffer() writing the file (synchronously, below)
let blob_file = s:temp_index " and the first gitgutter#process_buffer()'s async job reading it (with
let buff_file = s:temp_buffer " git-diff).
let extension = gitgutter#utility#extension() let buff_file = s:temp_buffer.'.'.a:bufnr
let extension = gitgutter#utility#extension(a:bufnr)
if !empty(extension) if !empty(extension)
let blob_file .= '.'.extension let index_file .= '.'.extension
let buff_file .= '.'.extension let buff_file .= '.'.extension
endif endif
let cmd .= g:gitgutter_git_executable.' show '.blob_name.' > '.blob_file.' && '
" Writing the whole buffer resets the '[ and '] marks and also the " Write file from index to temporary file.
" 'modified' flag (if &cpoptions includes '+'). These are unwanted let index_name = g:gitgutter_diff_base.':'.gitgutter#utility#repo_path(a:bufnr, 1)
" side-effects so we save and restore the values ourselves. let cmd .= g:gitgutter_git_executable.' --no-pager show '.index_name.' > '.index_file.' && '
let modified = getbufvar(bufnr, "&mod")
let op_mark_start = getpos("'[")
let op_mark_end = getpos("']")
let current_buffer = bufnr('') " Write buffer to temporary file.
execute 'buffer '.bufnr " Note: this is synchronous.
execute 'keepalt noautocmd silent write!' buff_file call s:write_buffer(a:bufnr, buff_file)
execute 'buffer '.current_buffer
call setbufvar(bufnr, "&mod", modified) " Call git-diff with the temporary files.
call setpos("'[", op_mark_start) let cmd .= g:gitgutter_git_executable.' --no-pager'
call setpos("']", op_mark_end)
endif
let cmd .= g:gitgutter_git_executable
if s:c_flag if s:c_flag
let cmd .= ' -c "diff.autorefreshindex=0"' let cmd .= ' -c "diff.autorefreshindex=0"'
let cmd .= ' -c "diff.noprefix=false"' let cmd .= ' -c "diff.noprefix=false"'
let cmd .= ' -c "core.safecrlf=false"'
endif 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 " Pipe git-diff output into grep.
let cmd .= ' -- '.blob_file.' '.buff_file if !a:preserve_full_diff && !empty(g:gitgutter_grep)
else let cmd .= ' | '.g:gitgutter_grep.' '.gitgutter#utility#shellescape('^@@ ')
let cmd .= g:gitgutter_diff_base.' -- '.gitgutter#utility#shellescape(gitgutter#utility#filename())
endif endif
if !a:preserve_full_diff && s:grep_available " grep exits with 1 when no matches are found; git-diff exits with 1 when
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
" differences are found. However we want to treat non-matches and " differences are found. However we want to treat non-matches and
" differences as non-erroneous behaviour; so we OR the command with one " differences as non-erroneous behaviour; so we OR the command with one
" which always exits with success (0). " which always exits with success (0).
let cmd .= ' || exit 0' let cmd .= ' || exit 0'
endif
let cmd .= ')' let cmd .= ')'
if !tracked let cmd = gitgutter#utility#cd_cmd(a:bufnr, cmd)
let cmd .= ')'
endif
let cmd = gitgutter#utility#command_in_directory_of_file(cmd) if g:gitgutter_async && gitgutter#async#available()
call gitgutter#async#execute(cmd, a:bufnr, {
if g:gitgutter_async && gitgutter#async#available() && !a:preserve_full_diff \ 'out': function('gitgutter#diff#handler'),
call gitgutter#async#execute(cmd) \ 'err': function('gitgutter#hunk#reset'),
\ })
return 'async' return 'async'
else else
let diff = gitgutter#utility#system(cmd) let diff = gitgutter#utility#system(cmd)
if gitgutter#utility#shell_error() if v:shell_error
" A shell error indicates the file is not tracked by git (unless something bizarre is going on). call gitgutter#debug#log(diff)
throw 'diff failed' throw 'gitgutter diff failed'
endif endif
return diff return diff
endif endif
endfunction 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 function! gitgutter#diff#parse_diff(diff) abort
let hunks = [] let hunks = []
for line in split(a:diff, '\n') for line in split(a:diff, '\n')
@ -171,69 +195,71 @@ function! gitgutter#diff#parse_hunk(line) abort
end end
endfunction 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 = [] let modified_lines = []
for hunk in a:hunks 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 endfor
return modified_lines return modified_lines
endfunction endfunction
" Returns [ [<line_number (number)>, <name (string)>], ...] " Returns [ [<line_number (number)>, <name (string)>], ...]
function! gitgutter#diff#process_hunk(hunk) abort function! s:process_hunk(bufnr, hunk) abort
let modifications = [] let modifications = []
let from_line = a:hunk[0] let from_line = a:hunk[0]
let from_count = a:hunk[1] let from_count = a:hunk[1]
let to_line = a:hunk[2] let to_line = a:hunk[2]
let to_count = a:hunk[3] let to_count = a:hunk[3]
if gitgutter#diff#is_added(from_count, to_count) if s:is_added(from_count, to_count)
call gitgutter#diff#process_added(modifications, from_count, to_count, to_line) call s:process_added(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_added(to_count) call gitgutter#hunk#increment_lines_added(a:bufnr, to_count)
elseif gitgutter#diff#is_removed(from_count, to_count) elseif s:is_removed(from_count, to_count)
call gitgutter#diff#process_removed(modifications, from_count, to_count, to_line) call s:process_removed(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_removed(from_count) call gitgutter#hunk#increment_lines_removed(a:bufnr, from_count)
elseif gitgutter#diff#is_modified(from_count, to_count) elseif s:is_modified(from_count, to_count)
call gitgutter#diff#process_modified(modifications, from_count, to_count, to_line) call s:process_modified(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_modified(to_count) call gitgutter#hunk#increment_lines_modified(a:bufnr, to_count)
elseif gitgutter#diff#is_modified_and_added(from_count, to_count) elseif s:is_modified_and_added(from_count, to_count)
call gitgutter#diff#process_modified_and_added(modifications, from_count, to_count, to_line) call s: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_added(a:bufnr, to_count - from_count)
call gitgutter#hunk#increment_lines_modified(from_count) call gitgutter#hunk#increment_lines_modified(a:bufnr, from_count)
elseif gitgutter#diff#is_modified_and_removed(from_count, to_count) elseif s:is_modified_and_removed(from_count, to_count)
call gitgutter#diff#process_modified_and_removed(modifications, from_count, to_count, to_line) call s:process_modified_and_removed(modifications, from_count, to_count, to_line)
call gitgutter#hunk#increment_lines_modified(to_count) call gitgutter#hunk#increment_lines_modified(a:bufnr, to_count)
call gitgutter#hunk#increment_lines_removed(from_count - to_count) call gitgutter#hunk#increment_lines_removed(a:bufnr, from_count - to_count)
endif endif
return modifications return modifications
endfunction 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 return a:from_count == 0 && a:to_count > 0
endfunction 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 return a:from_count > 0 && a:to_count == 0
endfunction 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 return a:from_count > 0 && a:to_count > 0 && a:from_count == a:to_count
endfunction 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 return a:from_count > 0 && a:to_count > 0 && a:from_count < a:to_count
endfunction 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 return a:from_count > 0 && a:to_count > 0 && a:from_count > a:to_count
endfunction 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 let offset = 0
while offset < a:to_count while offset < a:to_count
let line_number = a:to_line + offset let line_number = a:to_line + offset
@ -242,7 +268,7 @@ function! gitgutter#diff#process_added(modifications, from_count, to_count, to_l
endwhile endwhile
endfunction 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 if a:to_line == 0
call add(a:modifications, [1, 'removed_first_line']) call add(a:modifications, [1, 'removed_first_line'])
else else
@ -250,7 +276,7 @@ function! gitgutter#diff#process_removed(modifications, from_count, to_count, to
endif endif
endfunction 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 let offset = 0
while offset < a:to_count while offset < a:to_count
let line_number = a:to_line + offset let line_number = a:to_line + offset
@ -259,7 +285,7 @@ function! gitgutter#diff#process_modified(modifications, from_count, to_count, t
endwhile endwhile
endfunction 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 let offset = 0
while offset < a:from_count while offset < a:from_count
let line_number = a:to_line + offset let line_number = a:to_line + offset
@ -273,7 +299,7 @@ function! gitgutter#diff#process_modified_and_added(modifications, from_count, t
endwhile endwhile
endfunction 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 let offset = 0
while offset < a:to_count while offset < a:to_count
let line_number = a:to_line + offset 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'] let a:modifications[-1] = [a:to_line + offset - 1, 'modified_removed']
endfunction 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' " Returns a diff for the current hunk.
let diff_for_hunk = gitgutter#diff#adjust_hunk_summary(diff_for_hunk, a:type == 'stage') function! gitgutter#diff#hunk_diff(bufnr, full_diff)
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
let modified_diff = [] let modified_diff = []
let keep_line = a:keep_header let keep_line = 1
for line in split(a:diff, '\n') " 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) let hunk_info = gitgutter#diff#parse_hunk(line)
if len(hunk_info) == 4 " start of new hunk if len(hunk_info) == 4 " start of new hunk
let keep_line = gitgutter#hunk#cursor_in_hunk(hunk_info) 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) call add(modified_diff, line)
endif endif
endfor endfor
return join(modified_diff, "\n")."\n"
if a:keep_header
return gitgutter#utility#stringify(modified_diff)
else
" Discard hunk summary too.
return gitgutter#utility#stringify(modified_diff[1:])
endif
endfunction endfunction
" Adjust hunk summary (from's / to's line number) to ignore changes above/before this one.
" function! s:write_buffer(bufnr, file)
" diff_for_hunk - a diff containing only the hunk of interest let bufcontents = getbufline(a:bufnr, 1, '$')
" staging - truthy if the hunk is to be staged, falsy if it is to be undone
" if getbufvar(a:bufnr, '&fileformat') ==# 'dos'
" TODO: push this down to #discard_hunks? call map(bufcontents, 'v:val."\r"')
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', '')
endif endif
let fenc = getbufvar(a:bufnr, '&fileencoding')
if fenc !=# &encoding
call map(bufcontents, 'iconv(v:val, &encoding, "'.fenc.'")')
endif endif
call add(adj_diff, line)
endfor if getbufvar(a:bufnr, '&bomb')
return gitgutter#utility#stringify(adj_diff) let bufcontents[0]=''.bufcontents[0]
endif
call writefile(bufcontents, a:file)
endfunction 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 function! gitgutter#highlight#define_sign_column_highlight() abort
if g:gitgutter_override_sign_column_highlight if g:gitgutter_override_sign_column_highlight
highlight! link SignColumn LineNr highlight! link SignColumn LineNr
@ -7,7 +41,7 @@ function! gitgutter#highlight#define_sign_column_highlight() abort
endfunction endfunction
function! gitgutter#highlight#define_highlights() abort 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. " Highlights used by the signs.
@ -42,12 +76,12 @@ function! gitgutter#highlight#define_signs() abort
sign define GitGutterLineModifiedRemoved sign define GitGutterLineModifiedRemoved
sign define GitGutterDummy 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_text_highlights()
call gitgutter#highlight#define_sign_line_highlights() call s:define_sign_line_highlights()
endfunction 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 GitGutterLineAdded text=" . g:gitgutter_sign_added
execute "sign define GitGutterLineModified text=" . g:gitgutter_sign_modified execute "sign define GitGutterLineModified text=" . g:gitgutter_sign_modified
execute "sign define GitGutterLineRemoved text=" . g:gitgutter_sign_removed execute "sign define GitGutterLineRemoved text=" . g:gitgutter_sign_removed
@ -75,7 +109,7 @@ function! gitgutter#highlight#define_sign_text_highlights() abort
endif endif
endfunction endfunction
function! gitgutter#highlight#define_sign_line_highlights() abort function! s:define_sign_line_highlights() abort
if g:gitgutter_highlight_lines if g:gitgutter_highlight_lines
sign define GitGutterLineAdded linehl=GitGutterAddLine sign define GitGutterLineAdded linehl=GitGutterAddLine
sign define GitGutterLineModified linehl=GitGutterChangeLine sign define GitGutterLineModified linehl=GitGutterChangeLine
@ -91,22 +125,22 @@ function! gitgutter#highlight#define_sign_line_highlights() abort
endif endif
endfunction endfunction
function! gitgutter#highlight#get_background_colors(group) abort function! s:get_background_colors(group) abort
redir => highlight redir => highlight
silent execute 'silent highlight ' . a:group silent execute 'silent highlight ' . a:group
redir END redir END
let link_matches = matchlist(highlight, 'links to \(\S\+\)') let link_matches = matchlist(highlight, 'links to \(\S\+\)')
if len(link_matches) > 0 " follow the link 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 endif
let ctermbg = gitgutter#highlight#match_highlight(highlight, 'ctermbg=\([0-9A-Za-z]\+\)') let ctermbg = s:match_highlight(highlight, 'ctermbg=\([0-9A-Za-z]\+\)')
let guibg = gitgutter#highlight#match_highlight(highlight, 'guibg=\([#0-9A-Za-z]\+\)') let guibg = s:match_highlight(highlight, 'guibg=\([#0-9A-Za-z]\+\)')
return [guibg, ctermbg] return [guibg, ctermbg]
endfunction endfunction
function! gitgutter#highlight#match_highlight(highlight, pattern) abort function! s:match_highlight(highlight, pattern) abort
let matches = matchlist(a:highlight, a:pattern) let matches = matchlist(a:highlight, a:pattern)
if len(matches) == 0 if len(matches) == 0
return 'NONE' return 'NONE'

View file

@ -1,15 +1,15 @@
function! gitgutter#hunk#set_hunks(hunks) abort function! gitgutter#hunk#set_hunks(bufnr, hunks) abort
call gitgutter#utility#setbufvar(gitgutter#utility#bufnr(), 'hunks', a:hunks) call gitgutter#utility#setbufvar(a:bufnr, 'hunks', a:hunks)
call s:reset_summary() call s:reset_summary(a:bufnr)
endfunction endfunction
function! gitgutter#hunk#hunks() abort function! gitgutter#hunk#hunks(bufnr) abort
return gitgutter#utility#getbufvar(gitgutter#utility#bufnr(), 'hunks', []) return gitgutter#utility#getbufvar(a:bufnr, 'hunks', [])
endfunction endfunction
function! gitgutter#hunk#reset() abort function! gitgutter#hunk#reset(bufnr) abort
call gitgutter#utility#setbufvar(gitgutter#utility#bufnr(), 'hunks', []) call gitgutter#utility#setbufvar(a:bufnr, 'hunks', [])
call s:reset_summary() call s:reset_summary(a:bufnr)
endfunction endfunction
@ -17,37 +17,35 @@ function! gitgutter#hunk#summary(bufnr) abort
return gitgutter#utility#getbufvar(a:bufnr, 'summary', [0,0,0]) return gitgutter#utility#getbufvar(a:bufnr, 'summary', [0,0,0])
endfunction endfunction
function! s:reset_summary() abort function! s:reset_summary(bufnr) abort
call gitgutter#utility#setbufvar(gitgutter#utility#bufnr(), 'summary', [0,0,0]) call gitgutter#utility#setbufvar(a:bufnr, 'summary', [0,0,0])
endfunction endfunction
function! gitgutter#hunk#increment_lines_added(count) abort function! gitgutter#hunk#increment_lines_added(bufnr, count) abort
let bufnr = gitgutter#utility#bufnr() let summary = gitgutter#hunk#summary(a:bufnr)
let summary = gitgutter#hunk#summary(bufnr)
let summary[0] += a:count let summary[0] += a:count
call gitgutter#utility#setbufvar(bufnr, 'summary', summary) call gitgutter#utility#setbufvar(a:bufnr, 'summary', summary)
endfunction endfunction
function! gitgutter#hunk#increment_lines_modified(count) abort function! gitgutter#hunk#increment_lines_modified(bufnr, count) abort
let bufnr = gitgutter#utility#bufnr() let summary = gitgutter#hunk#summary(a:bufnr)
let summary = gitgutter#hunk#summary(bufnr)
let summary[1] += a:count let summary[1] += a:count
call gitgutter#utility#setbufvar(bufnr, 'summary', summary) call gitgutter#utility#setbufvar(a:bufnr, 'summary', summary)
endfunction endfunction
function! gitgutter#hunk#increment_lines_removed(count) abort function! gitgutter#hunk#increment_lines_removed(bufnr, count) abort
let bufnr = gitgutter#utility#bufnr() let summary = gitgutter#hunk#summary(a:bufnr)
let summary = gitgutter#hunk#summary(bufnr)
let summary[2] += a:count let summary[2] += a:count
call gitgutter#utility#setbufvar(bufnr, 'summary', summary) call gitgutter#utility#setbufvar(a:bufnr, 'summary', summary)
endfunction endfunction
function! gitgutter#hunk#next_hunk(count) abort 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 current_line = line('.')
let hunk_count = 0 let hunk_count = 0
for hunk in gitgutter#hunk#hunks() for hunk in gitgutter#hunk#hunks(bufnr)
if hunk[2] > current_line if hunk[2] > current_line
let hunk_count += 1 let hunk_count += 1
if hunk_count == a:count if hunk_count == a:count
@ -61,10 +59,11 @@ function! gitgutter#hunk#next_hunk(count) abort
endfunction endfunction
function! gitgutter#hunk#prev_hunk(count) abort 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 current_line = line('.')
let hunk_count = 0 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 if hunk[2] < current_line
let hunk_count += 1 let hunk_count += 1
if hunk_count == a:count 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 " Returns the hunk the cursor is currently in or an empty list if the cursor
" isn't in a hunk. " isn't in a hunk.
function! gitgutter#hunk#current_hunk() abort function! s:current_hunk() abort
let bufnr = bufnr('')
let current_hunk = [] let current_hunk = []
for hunk in gitgutter#hunk#hunks() for hunk in gitgutter#hunk#hunks(bufnr)
if gitgutter#hunk#cursor_in_hunk(hunk) if gitgutter#hunk#cursor_in_hunk(hunk)
let current_hunk = hunk let current_hunk = hunk
break break
@ -107,22 +107,8 @@ function! gitgutter#hunk#cursor_in_hunk(hunk) abort
return 0 return 0
endfunction 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 function! gitgutter#hunk#text_object(inner) abort
let hunk = gitgutter#hunk#current_hunk() let hunk = s:current_hunk()
if empty(hunk) if empty(hunk)
return return
@ -141,3 +127,155 @@ function! gitgutter#hunk#text_object(inner) abort
execute 'normal! 'first_line.'GV'.last_line.'G' execute 'normal! 'first_line.'GV'.last_line.'G'
endfunction 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")) 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#enable() abort
function! gitgutter#sign#clear_signs() abort let old_signs = g:gitgutter_signs
let bufnr = gitgutter#utility#bufnr()
call gitgutter#sign#find_current_signs()
let sign_ids = map(values(gitgutter#utility#getbufvar(bufnr, 'gitgutter_signs')), 'v:val.id') let g:gitgutter_signs = 1
call gitgutter#sign#remove_signs(sign_ids, 1) call gitgutter#highlight#define_sign_text_highlights()
call gitgutter#utility#setbufvar(bufnr, 'gitgutter_signs', {})
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 endfunction
@ -25,39 +54,37 @@ endfunction
" "
" modified_lines: list of [<line_number (number)>, <name (string)>] " modified_lines: list of [<line_number (number)>, <name (string)>]
" where name = 'added|removed|modified|modified_removed' " where name = 'added|removed|modified|modified_removed'
function! gitgutter#sign#update_signs(modified_lines) abort function! gitgutter#sign#update_signs(bufnr, modified_lines) abort
call gitgutter#sign#find_current_signs() call s:find_current_signs(a:bufnr)
let new_gitgutter_signs_line_numbers = map(copy(a:modified_lines), 'v:val[0]') 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) let flicker_possible = s:remove_all_old_signs && !empty(a:modified_lines)
if flicker_possible if flicker_possible
call gitgutter#sign#add_dummy_sign() call s:add_dummy_sign(a:bufnr)
endif endif
call gitgutter#sign#remove_signs(obsolete_signs, s:remove_all_old_signs) call s:remove_signs(a:bufnr, obsolete_signs, s:remove_all_old_signs)
call gitgutter#sign#upsert_new_gitgutter_signs(a:modified_lines) call s:upsert_new_gitgutter_signs(a:bufnr, a:modified_lines)
if flicker_possible if flicker_possible
call gitgutter#sign#remove_dummy_sign(0) call gitgutter#sign#remove_dummy_sign(a:bufnr, 0)
endif endif
endfunction endfunction
function! gitgutter#sign#add_dummy_sign() abort function! s:add_dummy_sign(bufnr) abort
let bufnr = gitgutter#utility#bufnr() if !gitgutter#utility#getbufvar(a:bufnr, 'dummy_sign')
if !gitgutter#utility#getbufvar(bufnr, 'dummy_sign') execute "sign place" s:dummy_sign_id "line=" . 9999 "name=GitGutterDummy buffer=" . a:bufnr
execute "sign place" s:dummy_sign_id "line=" . 9999 "name=GitGutterDummy buffer=" . bufnr call gitgutter#utility#setbufvar(a:bufnr, 'dummy_sign', 1)
call gitgutter#utility#setbufvar(bufnr, 'dummy_sign', 1)
endif endif
endfunction endfunction
function! gitgutter#sign#remove_dummy_sign(force) abort function! gitgutter#sign#remove_dummy_sign(bufnr, force) abort
let bufnr = gitgutter#utility#bufnr() if gitgutter#utility#getbufvar(a:bufnr, 'dummy_sign') && (a:force || !g:gitgutter_sign_column_always)
if gitgutter#utility#getbufvar(bufnr, 'dummy_sign') && (a:force || !g:gitgutter_sign_column_always) execute "sign unplace" s:dummy_sign_id "buffer=" . a:bufnr
execute "sign unplace" s:dummy_sign_id "buffer=" . bufnr call gitgutter#utility#setbufvar(a:bufnr, 'dummy_sign', 0)
call gitgutter#utility#setbufvar(bufnr, 'dummy_sign', 0)
endif endif
endfunction endfunction
@ -67,14 +94,13 @@ endfunction
" "
function! gitgutter#sign#find_current_signs() abort function! s:find_current_signs(bufnr) abort
let bufnr = gitgutter#utility#bufnr()
let gitgutter_signs = {} " <line_number (string)>: {'id': <id (number)>, 'name': <name (string)>} let gitgutter_signs = {} " <line_number (string)>: {'id': <id (number)>, 'name': <name (string)>}
let other_signs = [] " [<line_number (number),...] let other_signs = [] " [<line_number (number),...]
let dummy_sign_placed = 0 let dummy_sign_placed = 0
redir => signs redir => signs
silent execute "sign place buffer=" . bufnr silent execute "sign place buffer=" . a:bufnr
redir END redir END
for sign_line in filter(split(signs, '\n')[2:], 'v:val =~# "="') for sign_line in filter(split(signs, '\n')[2:], 'v:val =~# "="')
@ -101,19 +127,18 @@ function! gitgutter#sign#find_current_signs() abort
end end
endfor endfor
call gitgutter#utility#setbufvar(bufnr, 'dummy_sign', dummy_sign_placed) call gitgutter#utility#setbufvar(a:bufnr, 'dummy_sign', dummy_sign_placed)
call gitgutter#utility#setbufvar(bufnr, 'gitgutter_signs', gitgutter_signs) call gitgutter#utility#setbufvar(a:bufnr, 'gitgutter_signs', gitgutter_signs)
call gitgutter#utility#setbufvar(bufnr, 'other_signs', other_signs) call gitgutter#utility#setbufvar(a:bufnr, 'other_signs', other_signs)
endfunction endfunction
" Returns a list of [<id (number)>, ...] " Returns a list of [<id (number)>, ...]
" Sets `s:remove_all_old_signs` as a side-effect. " Sets `s:remove_all_old_signs` as a side-effect.
function! gitgutter#sign#obsolete_gitgutter_signs_to_remove(new_gitgutter_signs_line_numbers) abort function! s:obsolete_gitgutter_signs_to_remove(bufnr, new_gitgutter_signs_line_numbers) abort
let bufnr = gitgutter#utility#bufnr()
let signs_to_remove = [] " list of [<id (number)>, ...] let signs_to_remove = [] " list of [<id (number)>, ...]
let remove_all_signs = 1 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) for line_number in keys(old_gitgutter_signs)
if index(a:new_gitgutter_signs_line_numbers, str2nr(line_number)) == -1 if index(a:new_gitgutter_signs_line_numbers, str2nr(line_number)) == -1
call add(signs_to_remove, old_gitgutter_signs[line_number].id) 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 endfunction
function! gitgutter#sign#remove_signs(sign_ids, all_signs) abort function! s:remove_signs(bufnr, sign_ids, all_signs) abort
let bufnr = gitgutter#utility#bufnr() if a:all_signs && s:supports_star && empty(gitgutter#utility#getbufvar(a:bufnr, 'other_signs'))
if a:all_signs && s:supports_star && empty(gitgutter#utility#getbufvar(bufnr, 'other_signs')) let dummy_sign_present = gitgutter#utility#getbufvar(a:bufnr, 'dummy_sign')
let dummy_sign_present = gitgutter#utility#getbufvar(bufnr, 'dummy_sign') execute "sign unplace * buffer=" . a:bufnr
execute "sign unplace * buffer=" . bufnr
if dummy_sign_present 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 endif
else else
for id in a:sign_ids for id in a:sign_ids
@ -142,22 +166,21 @@ function! gitgutter#sign#remove_signs(sign_ids, all_signs) abort
endfunction endfunction
function! gitgutter#sign#upsert_new_gitgutter_signs(modified_lines) abort function! s:upsert_new_gitgutter_signs(bufnr, modified_lines) abort
let bufnr = gitgutter#utility#bufnr() let other_signs = gitgutter#utility#getbufvar(a:bufnr, 'other_signs')
let other_signs = gitgutter#utility#getbufvar(bufnr, 'other_signs') let old_gitgutter_signs = gitgutter#utility#getbufvar(a:bufnr, 'gitgutter_signs')
let old_gitgutter_signs = gitgutter#utility#getbufvar(bufnr, 'gitgutter_signs')
for line in a:modified_lines for line in a:modified_lines
let line_number = line[0] " <number> let line_number = line[0] " <number>
if index(other_signs, line_number) == -1 " don't clobber others' signs 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 if !has_key(old_gitgutter_signs, line_number) " insert
let id = gitgutter#sign#next_sign_id() let id = s:next_sign_id()
execute "sign place" id "line=" . line_number "name=" . name "buffer=" . bufnr execute "sign place" id "line=" . line_number "name=" . name "buffer=" . a:bufnr
else " update if sign has changed else " update if sign has changed
let old_sign = old_gitgutter_signs[line_number] let old_sign = old_gitgutter_signs[line_number]
if old_sign.name !=# name 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 end
endif endif
endif endif
@ -166,7 +189,7 @@ function! gitgutter#sign#upsert_new_gitgutter_signs(modified_lines) abort
endfunction endfunction
function! gitgutter#sign#next_sign_id() abort function! s:next_sign_id() abort
let next_id = s:next_sign_id let next_id = s:next_sign_id
let s:next_sign_id += 1 let s:next_sign_id += 1
return next_id return next_id
@ -177,3 +200,20 @@ endfunction
function! gitgutter#sign#reset() function! gitgutter#sign#reset()
let s:next_sign_id = s:first_sign_id let s:next_sign_id = s:first_sign_id
endfunction 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 = '' function! gitgutter#utility#supports_overscore_sign()
let s:using_xolox_shell = -1 if s:windows()
let s:exit_code = 0 return &encoding ==? 'utf-8'
else
return &termencoding ==? &encoding || &termencoding == ''
endif
endfunction
function! gitgutter#utility#setbufvar(buffer, varname, val) function! gitgutter#utility#setbufvar(buffer, varname, val)
let dict = get(getbufvar(a:buffer, ''), 'gitgutter', {}) let dict = get(getbufvar(a:buffer, ''), 'gitgutter', {})
let needs_setting = empty(dict)
let dict[a:varname] = a:val let dict[a:varname] = a:val
if needs_setting
call setbufvar(a:buffer, 'gitgutter', dict) call setbufvar(a:buffer, 'gitgutter', dict)
endif
endfunction endfunction
function! gitgutter#utility#getbufvar(buffer, varname, ...) function! gitgutter#utility#getbufvar(buffer, varname, ...)
@ -26,11 +33,11 @@ function! gitgutter#utility#warn(message) abort
let v:warningmsg = a:message let v:warningmsg = a:message
endfunction endfunction
function! gitgutter#utility#warn_once(message, key) abort function! gitgutter#utility#warn_once(bufnr, message, key) abort
if empty(gitgutter#utility#getbufvar(s:bufnr, a:key)) if empty(gitgutter#utility#getbufvar(a:bufnr, a:key))
call gitgutter#utility#setbufvar(s:bufnr, a:key, '1') call gitgutter#utility#setbufvar(a:bufnr, a:key, '1')
echohl WarningMsg echohl WarningMsg
redraw | echo 'vim-gitgutter: ' . a:message redraw | echom 'vim-gitgutter: ' . a:message
echohl None echohl None
let v:warningmsg = a:message let v:warningmsg = a:message
endif endif
@ -38,188 +45,166 @@ endfunction
" Returns truthy when the buffer's file should be processed; and falsey when it shouldn't. " 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. " 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 && return g:gitgutter_enabled &&
\ !pumvisible() && \ !pumvisible() &&
\ gitgutter#utility#is_file_buffer() && \ s:is_file_buffer(a:bufnr) &&
\ gitgutter#utility#exists_file() && \ s:exists_file(a:bufnr) &&
\ gitgutter#utility#not_git_dir() \ s:not_git_dir(a:bufnr)
endfunction endfunction
function! gitgutter#utility#not_git_dir() abort function! s:not_git_dir(bufnr) abort
return gitgutter#utility#full_path_to_directory_of_file() !~ '[/\\]\.git\($\|[/\\]\)' return s:dir(a:bufnr) !~ '[/\\]\.git\($\|[/\\]\)'
endfunction endfunction
function! gitgutter#utility#is_file_buffer() abort function! s:is_file_buffer(bufnr) abort
return empty(getbufvar(s:bufnr, '&buftype')) return empty(getbufvar(a:bufnr, '&buftype'))
endfunction endfunction
" A replacement for the built-in `shellescape(arg)`. " From tpope/vim-fugitive
" function! s:winshell()
" Recent versions of Vim handle shell escaping pretty well. However older return &shell =~? 'cmd' || exists('+shellslash') && !&shellslash
" versions aren't as good. This attempts to do the right thing. endfunction
"
" See: " From tpope/vim-fugitive
" https://github.com/tpope/vim-fugitive/blob/8f0b8edfbd246c0026b7a2388e1d883d579ac7f6/plugin/fugitive.vim#L29-L37
function! gitgutter#utility#shellescape(arg) abort function! gitgutter#utility#shellescape(arg) abort
if a:arg =~ '^[A-Za-z0-9_/.-]\+$' if a:arg =~ '^[A-Za-z0-9_/.-]\+$'
return a:arg return a:arg
elseif &shell =~# 'cmd' || gitgutter#utility#using_xolox_shell() elseif s:winshell()
return '"' . substitute(substitute(a:arg, '"', '""', 'g'), '%', '"%"', 'g') . '"' return '"' . substitute(substitute(a:arg, '"', '""', 'g'), '%', '"%"', 'g') . '"'
else else
return shellescape(a:arg) return shellescape(a:arg)
endif endif
endfunction endfunction
function! gitgutter#utility#set_buffer(bufnr) abort function! gitgutter#utility#file(bufnr)
let s:bufnr = a:bufnr return s:abs_path(a:bufnr, 1)
let s:file = resolve(bufname(a:bufnr))
endfunction endfunction
function! gitgutter#utility#bufnr() " Not shellescaped
return s:bufnr function! gitgutter#utility#extension(bufnr) abort
endfunction return fnamemodify(s:abs_path(a:bufnr, 0), ':e')
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
endfunction endfunction
function! gitgutter#utility#system(cmd, ...) abort function! gitgutter#utility#system(cmd, ...) abort
call gitgutter#debug#log(a:cmd, a:000) call gitgutter#debug#log(a:cmd, a:000)
if gitgutter#utility#using_xolox_shell() call s:use_known_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
silent let output = (a:0 == 0) ? system(a:cmd) : system(a:cmd, a:1) silent let output = (a:0 == 0) ? system(a:cmd) : system(a:cmd, a:1)
endif call s:restore_shell()
return output return output
endfunction endfunction
function! gitgutter#utility#file_relative_to_repo_root() abort " Path of file relative to repo root.
let file_path_relative_to_repo_root = gitgutter#utility#getbufvar(s:bufnr, 'repo_relative_path') "
if empty(file_path_relative_to_repo_root) " * empty string - not set
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')) " * non-empty string - path
let dir_path_relative_to_repo_root = gitgutter#utility#strip_trailing_new_line(dir_path_relative_to_repo_root) " * -1 - pending
let file_path_relative_to_repo_root = dir_path_relative_to_repo_root . gitgutter#utility#filename() " * -2 - not tracked by git
call gitgutter#utility#setbufvar(s:bufnr, 'repo_relative_path', file_path_relative_to_repo_root) 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 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 endif
endfunction endfunction
" Dedups list in-place. if has('nvim') && !has('nvim-0.2.0')
" Assumes list has no empty entries. function! s:not_tracked_by_git(bufnr)
function! gitgutter#utility#dedup(list) call s:set_path(a:bufnr, -2)
return filter(sort(a:list), 'index(a:list, v:val, v:key + 1) == -1') 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 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$', '', '') return substitute(a:line, '\n$', '', '')
endfunction endfunction
" True for git v1.7.2+. function! s:windows()
function! gitgutter#utility#git_supports_command_line_config_override() abort return has('win64') || has('win32') || has('win16')
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
endfunction endfunction

View file

@ -4,45 +4,50 @@
Vim Git Gutter Vim Git Gutter
Author: Andy Stewart <http://airbladesoftware.com/> Author: Andy Stewart <https://airbladesoftware.com/>
Plugin Homepage: <https://github.com/airblade/vim-gitgutter> 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* CONTENTS *gitgutter*
*GitGutter*
Vim Git Gutter is a Vim plugin which shows a git diff in the 'gutter' (sign Introduction ................. |gitgutter-introduction|
column). It shows whether each line has been added, modified, and where lines Installation ................. |gitgutter-installation|
have been removed. 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 cd ~/.vim/bundle
git clone git://github.com/airblade/vim-gitgutter.git git clone git://github.com/airblade/vim-gitgutter.git
< <
* Voom: Voom:~
Edit your plugin manifest (`voom edit`) and add: Edit your plugin manifest (`voom edit`) and add:
> >
airblade/vim-gitgutter airblade/vim-gitgutter
< <
* VimPlug: VimPlug:~
Place this in your .vimrc: Place this in your .vimrc:
> >
@ -53,7 +58,7 @@ Then run the following in Vim:
:source % :source %
:PlugInstall :PlugInstall
< <
* NeoBundle: NeoBundle:~
Place this in your .vimrc: Place this in your .vimrc:
> >
@ -64,7 +69,7 @@ Then run the following in Vim:
:source % :source %
:NeoBundleInstall :NeoBundleInstall
< <
* No plugin manager: No plugin manager:~
Copy vim-gitgutter's subdirectories into your vim configuration directory: 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|. 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* *gitgutter-:GitGutterDisable*
Explicitly turn Git Gutter off. :GitGutterDisable Turn vim-gitgutter off for all buffers.
:GitGutterEnable *:GitGutterEnable* *gitgutter-:GitGutterEnable*
Explicitly turn Git Gutter on. :GitGutterEnable Turn vim-gitgutter on for all buffers.
:GitGutterToggle *:GitGutterToggle* *gitgutter-:GitGutterToggle*
Explicitly turn Git Gutter on if it was off and vice versa. :GitGutterToggle Toggle vim-gitgutter on or off for all buffers.
:GitGutter *:GitGutter* *gitgutter-:GitGutter*
Update signs for the current buffer. :GitGutter Update signs for the current buffer. You shouldn't
need to run this.
:GitGutterAll *:GitGutterAll* *gitgutter-:GitGutterAll*
Update signs across all buffers. :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* Commands for turning signs on and off (defaults to on):~
Explicitly turn line signs on.
:GitGutterSignsDisable *:GitGutterSignsDisable* *gitgutter-:GitGutterSignsEnable*
Explicitly turn line signs off. :GitGutterSignsEnable Show signs for the diff.
:GitGutterSignsToggle *:GitGutterSignsToggle* *gitgutter-:GitGutterSignsDisable*
Explicitly turn line signs on if it was off and vice versa. :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* Commands for turning line highlighting on and off (defaults to off):~
Explicitly turn line highlighting off.
:GitGutterLineHighlightsToggle *:GitGutterLineHighlightsToggle* *gitgutter-:GitGutterLineHighlightsEnable*
Explicitly turn line highlighting on if it was off and vice versa. :GitGutterLineHighlightsEnable Turn on line highlighting.
Commands for jumping between marked hunks: *gitgutter-:GitGutterLineHighlightsDisable*
:GitGutterLineHighlightsDisable Turn off line highlighting.
:GitGutterNextHunk *:GitGutterNextHunk* *gitgutter-:GitGutterLineHighlightsToggle*
Jump to the next marked hunk. Takes a count. :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* *gitgutter-:GitGutterNextHunk*
Stage the hunk the cursor is in. :GitGutterNextHunk Jump to the next [count] hunk.
:GitGutterUndoHunk *:GitGutterUndoHunk* *gitgutter-:GitGutterPrevHunk*
Undo the hunk the cursor is in. :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 After updating a buffer's signs vim-gitgutter fires a |User| |autocmd| with the
event GitGutter. You can listen for this event, for example: event GitGutter. You can listen for this event, for example:
> >
autocmd User GitGutter call updateMyStatusLine() 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 Hunk operations:~
- 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)
Please note that vim-gitgutter won't override any colours or highlights you've These can be repeated with `.` if you have vim-repeat installed.
set in your colorscheme.
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 *gitgutter-<Leader>hs*
column (i.e. the |hl-LineNr| highlight group). <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 To customise your sign column's background color, first tell vim-gitgutter to
leave it alone: leave it alone:
@ -186,15 +365,54 @@ And then either update your colorscheme's |hlSignColumn| highlight group or set
it in your |vimrc|: it in your |vimrc|:
Desired appearance Command ~ 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 (terminal Vim) highlight SignColumn ctermbg={whatever}
User-defined (graphical Vim) highlight SignColumn guibg={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|: colorscheme or |vimrc|:
> >
GitGutterAdd " an added line GitGutterAdd " an added line
GitGutterChange " a changed line GitGutterChange " a changed line
@ -208,17 +426,7 @@ them to existing highlight groups with, say:
highlight link GitGutterAdd DiffAdd highlight link GitGutterAdd DiffAdd
< <
To customise the symbols, add the following to your |vimrc|: To change the line highlights, set up the following highlight groups in your
>
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
colorscheme or |vimrc|: colorscheme or |vimrc|:
> >
GitGutterAddLine " default: links to DiffAdd GitGutterAddLine " default: links to DiffAdd
@ -227,115 +435,85 @@ colorscheme or |vimrc|:
GitGutterChangeDeleteLine " default: links to GitGutterChangeLineDefault 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. 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 {{{ " Initialisation {{{
" Realtime sign updates require Vim 7.3.105+.
if v:version < 703 || (v:version == 703 && !has("patch105")) if v:version < 703 || (v:version == 703 && !has("patch105"))
let g:gitgutter_realtime = 0 call gitgutter#utility#warn('requires Vim 7.3.105')
endif finish
" Eager updates require gettabvar()/settabvar().
if !exists("*gettabvar")
let g:gitgutter_eager = 0
endif endif
function! s:set(var, default) abort 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_highlight_lines', 0)
call s:set('g:gitgutter_sign_column_always', 0) call s:set('g:gitgutter_sign_column_always', 0)
if g:gitgutter_sign_column_always && exists('&signcolumn') if g:gitgutter_sign_column_always && exists('&signcolumn')
" Vim 7.4.2201.
set signcolumn=yes set signcolumn=yes
let g:gitgutter_sign_column_always = 0 let g:gitgutter_sign_column_always = 0
call gitgutter#utility#warn('please replace "let g:gitgutter_sign_column_always=1" with "set signcolumn=yes"') call gitgutter#utility#warn('please replace "let g:gitgutter_sign_column_always=1" with "set signcolumn=yes"')
endif endif
call s:set('g:gitgutter_override_sign_column_highlight', 1) 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_added', '+')
call s:set('g:gitgutter_sign_modified', '~') call s:set('g:gitgutter_sign_modified', '~')
call s:set('g:gitgutter_sign_removed', '_') call s:set('g:gitgutter_sign_removed', '_')
try
if gitgutter#utility#supports_overscore_sign()
call s:set('g:gitgutter_sign_removed_first_line', '‾') call s:set('g:gitgutter_sign_removed_first_line', '‾')
catch /E239/ else
let g:gitgutter_sign_removed_first_line = '_^' call s:set('g:gitgutter_sign_removed_first_line', '_^')
endtry endif
call s:set('g:gitgutter_sign_modified_removed', '~_') call s:set('g:gitgutter_sign_modified_removed', '~_')
call s:set('g:gitgutter_diff_args', '') call s:set('g:gitgutter_diff_args', '')
call s:set('g:gitgutter_diff_base', '') call s:set('g:gitgutter_diff_base', '')
call s:set('g:gitgutter_map_keys', 1) 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_async', 1)
call s:set('g:gitgutter_log', 0) 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) if !executable(g:gitgutter_git_executable)
call gitgutter#utility#warn('cannot find git. Please set g:gitgutter_git_executable.') call gitgutter#utility#warn('cannot find git. Please set g:gitgutter_git_executable.')
endif 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_sign_column_highlight()
call gitgutter#highlight#define_highlights() call gitgutter#highlight#define_highlights()
call gitgutter#highlight#define_signs() call gitgutter#highlight#define_signs()
@ -70,40 +80,39 @@ call gitgutter#highlight#define_signs()
" Primary functions {{{ " Primary functions {{{
command -bar GitGutterAll call gitgutter#all() command! -bar GitGutterAll call gitgutter#all(1)
command -bar GitGutter call gitgutter#process_buffer(bufnr(''), 0) command! -bar GitGutter call gitgutter#process_buffer(bufnr(''), 1)
command -bar GitGutterDisable call gitgutter#disable() command! -bar GitGutterDisable call gitgutter#disable()
command -bar GitGutterEnable call gitgutter#enable() command! -bar GitGutterEnable call gitgutter#enable()
command -bar GitGutterToggle call gitgutter#toggle() command! -bar GitGutterToggle call gitgutter#toggle()
" }}} " }}}
" Line highlights {{{ " Line highlights {{{
command -bar GitGutterLineHighlightsDisable call gitgutter#line_highlights_disable() command! -bar GitGutterLineHighlightsDisable call gitgutter#highlight#line_disable()
command -bar GitGutterLineHighlightsEnable call gitgutter#line_highlights_enable() command! -bar GitGutterLineHighlightsEnable call gitgutter#highlight#line_enable()
command -bar GitGutterLineHighlightsToggle call gitgutter#line_highlights_toggle() command! -bar GitGutterLineHighlightsToggle call gitgutter#highlight#line_toggle()
" }}} " }}}
" Signs {{{ " Signs {{{
command -bar GitGutterSignsEnable call gitgutter#signs_enable() command! -bar GitGutterSignsEnable call gitgutter#sign#enable()
command -bar GitGutterSignsDisable call gitgutter#signs_disable() command! -bar GitGutterSignsDisable call gitgutter#sign#disable()
command -bar GitGutterSignsToggle call gitgutter#signs_toggle() command! -bar GitGutterSignsToggle call gitgutter#sign#toggle()
" }}} " }}}
" Hunks {{{ " Hunks {{{
command -bar -count=1 GitGutterNextHunk call gitgutter#hunk#next_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 -count=1 GitGutterPrevHunk call gitgutter#hunk#prev_hunk(<count>)
command -bar GitGutterStageHunk call gitgutter#stage_hunk() command! -bar GitGutterStageHunk call gitgutter#hunk#stage()
command -bar GitGutterUndoHunk call gitgutter#undo_hunk() command! -bar GitGutterUndoHunk call gitgutter#hunk#undo()
command -bar GitGutterRevertHunk echomsg 'GitGutterRevertHunk is deprecated. Use GitGutterUndoHunk'<Bar>call gitgutter#undo_hunk() command! -bar GitGutterPreviewHunk call gitgutter#hunk#preview()
command -bar GitGutterPreviewHunk call gitgutter#preview_hunk()
" Hunk text object " Hunk text object
onoremap <silent> <Plug>GitGutterTextObjectInnerPending :<C-U>call gitgutter#hunk#text_object(1)<CR> 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 " `line` - refers to the line number where the change starts
" `count` - refers to the number of lines the change covers " `count` - refers to the number of lines the change covers
function! GitGutterGetHunks() function! GitGutterGetHunks()
return gitgutter#utility#is_active() ? gitgutter#hunk#hunks() : [] let bufnr = bufnr('')
return gitgutter#utility#is_active(bufnr) ? gitgutter#hunk#hunks(bufnr) : []
endfunction endfunction
" Returns an array that contains a summary of the hunk status for the current " 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 {{{ " Maps {{{
@ -169,7 +179,6 @@ if g:gitgutter_map_keys
endif endif
if !hasmapto('<Plug>GitGutterUndoHunk') && maparg('<Leader>hu', 'n') ==# '' if !hasmapto('<Plug>GitGutterUndoHunk') && maparg('<Leader>hu', 'n') ==# ''
nmap <Leader>hu <Plug>GitGutterUndoHunk nmap <Leader>hu <Plug>GitGutterUndoHunk
nmap <Leader>hr <Plug>GitGutterUndoHunk:echomsg '<Leader>hr is deprecated. Use <Leader>hu'<CR>
endif endif
if !hasmapto('<Plug>GitGutterPreviewHunk') && maparg('<Leader>hp', 'n') ==# '' if !hasmapto('<Plug>GitGutterPreviewHunk') && maparg('<Leader>hp', 'n') ==# ''
nmap <Leader>hp <Plug>GitGutterPreviewHunk nmap <Leader>hp <Plug>GitGutterPreviewHunk
@ -196,35 +205,26 @@ endif
augroup gitgutter augroup gitgutter
autocmd! autocmd!
if g:gitgutter_realtime autocmd TabEnter * let t:gitgutter_didtabenter = 1
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 BufEnter * autocmd BufEnter *
\ if gettabvar(tabpagenr(), 'gitgutter_didtabenter') | \ if exists('t:gitgutter_didtabenter') && t:gitgutter_didtabenter |
\ call settabvar(tabpagenr(), 'gitgutter_didtabenter', 0) | \ let t:gitgutter_didtabenter = 0 |
\ call gitgutter#all() | \ call gitgutter#all(!g:gitgutter_terminal_reports_focus) |
\ else | \ else |
\ call gitgutter#process_buffer(bufnr(''), 0) | \ call gitgutter#init_buffer(bufnr('')) |
\ call gitgutter#process_buffer(bufnr(''), !g:gitgutter_terminal_reports_focus) |
\ endif \ 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.: " Ensure that all buffers are processed when opening vim with multiple files, e.g.:
" "
" vim -o file1 file2 " 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(1)
autocmd FocusGained * call gitgutter#all()
endif
else
autocmd BufRead,BufWritePost,FileChangedShellPost * call gitgutter#process_buffer(bufnr(''), 0)
endif
autocmd ColorScheme * call gitgutter#highlight#define_sign_column_highlight() | call gitgutter#highlight#define_highlights() 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" VIM="/Applications/MacVim.app/Contents/MacOS/Vim -v"
export VIM_GITGUTTER_TEST=1
$VIM -u NONE -U NONE -N \ $VIM -u NONE -U NONE -N \
--cmd 'set rtp+=../' \ --cmd 'set rtp+=../' \
--cmd 'let g:gitgutter_async=0' \ --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') return split(system('git diff -U0 --staged fixture.txt'), '\n')
endfunction endfunction
function s:trigger_gitgutter()
doautocmd CursorHold
endfunction
" "
" SetUp / TearDown " SetUp / TearDown
@ -40,7 +44,8 @@ function SetUp()
call system("git init ".s:test_repo. call system("git init ".s:test_repo.
\ " && cd ".s:test_repo. \ " && cd ".s:test_repo.
\ " && cp ../fixture.txt .". \ " && 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 execute ':cd' s:test_repo
edit! fixture.txt edit! fixture.txt
call gitgutter#sign#reset() call gitgutter#sign#reset()
@ -64,7 +69,7 @@ endfunction
function Test_add_lines() function Test_add_lines()
normal ggo* normal ggo*
write call s:trigger_gitgutter()
let expected = ["line=2 id=3000 name=GitGutterLineAdded"] let expected = ["line=2 id=3000 name=GitGutterLineAdded"]
call assert_equal(expected, s:signs('fixture.txt')) call assert_equal(expected, s:signs('fixture.txt'))
@ -76,7 +81,7 @@ function Test_add_lines_fish()
set shell=/usr/local/bin/fish set shell=/usr/local/bin/fish
normal ggo* normal ggo*
write call s:trigger_gitgutter()
let expected = ["line=2 id=3000 name=GitGutterLineAdded"] let expected = ["line=2 id=3000 name=GitGutterLineAdded"]
call assert_equal(expected, s:signs('fixture.txt')) call assert_equal(expected, s:signs('fixture.txt'))
@ -87,7 +92,7 @@ endfunction
function Test_modify_lines() function Test_modify_lines()
normal ggi* normal ggi*
write call s:trigger_gitgutter()
let expected = ["line=1 id=3000 name=GitGutterLineModified"] let expected = ["line=1 id=3000 name=GitGutterLineModified"]
call assert_equal(expected, s:signs('fixture.txt')) call assert_equal(expected, s:signs('fixture.txt'))
@ -96,7 +101,7 @@ endfunction
function Test_remove_lines() function Test_remove_lines()
execute '5d' execute '5d'
write call s:trigger_gitgutter()
let expected = ["line=4 id=3000 name=GitGutterLineRemoved"] let expected = ["line=4 id=3000 name=GitGutterLineRemoved"]
call assert_equal(expected, s:signs('fixture.txt')) call assert_equal(expected, s:signs('fixture.txt'))
@ -105,7 +110,7 @@ endfunction
function Test_remove_first_lines() function Test_remove_first_lines()
execute '1d' execute '1d'
write call s:trigger_gitgutter()
let expected = ["line=1 id=3000 name=GitGutterLineRemovedFirstLine"] let expected = ["line=1 id=3000 name=GitGutterLineRemovedFirstLine"]
call assert_equal(expected, s:signs('fixture.txt')) call assert_equal(expected, s:signs('fixture.txt'))
@ -115,7 +120,7 @@ endfunction
function Test_edit_file_with_same_name_as_a_branch() function Test_edit_file_with_same_name_as_a_branch()
normal 5Gi* normal 5Gi*
call system('git checkout -b fixture.txt') call system('git checkout -b fixture.txt')
write call s:trigger_gitgutter()
let expected = ["line=5 id=3000 name=GitGutterLineModified"] let expected = ["line=5 id=3000 name=GitGutterLineModified"]
call assert_equal(expected, s:signs('fixture.txt')) 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) call system('touch '.tmpfile.' && git add '.tmpfile)
execute 'edit '.tmpfile execute 'edit '.tmpfile
normal ihello normal ihello
write call s:trigger_gitgutter()
let expected = ["line=1 id=3000 name=GitGutterLineAdded"] let expected = ["line=1 id=3000 name=GitGutterLineAdded"]
call assert_equal(expected, s:signs('fileAddedToGit.tmp')) 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') call system('touch =fixture=.txt && git add =fixture=.txt')
edit =fixture=.txt edit =fixture=.txt
normal ggo* normal ggo*
write call s:trigger_gitgutter()
let expected = [ let expected = [
\ 'line=1 id=3000 name=GitGutterLineAdded', \ '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') call system('touch fix[tu]re.txt && git add fix[tu]re.txt')
edit fix[tu]re.txt edit fix[tu]re.txt
normal ggo* normal ggo*
write call s:trigger_gitgutter()
let expected = [ let expected = [
\ 'line=1 id=3000 name=GitGutterLineAdded', \ 'line=1 id=3000 name=GitGutterLineAdded',
@ -168,7 +173,7 @@ function Test_follow_symlink()
call system('ln -nfs fixture.txt '.tmp) call system('ln -nfs fixture.txt '.tmp)
execute 'edit '.tmp execute 'edit '.tmp
6d 6d
write call s:trigger_gitgutter()
let expected = ['line=5 id=3000 name=GitGutterLineRemoved'] let expected = ['line=5 id=3000 name=GitGutterLineRemoved']
call assert_equal(expected, s:signs('symlink')) call assert_equal(expected, s:signs('symlink'))
@ -183,7 +188,7 @@ function Test_keep_alt()
call assert_equal('', bufname('#')) call assert_equal('', bufname('#'))
normal ggx normal ggx
doautocmd CursorHold call s:trigger_gitgutter()
call assert_equal('', bufname('#')) call assert_equal('', bufname('#'))
endfunction endfunction
@ -193,7 +198,7 @@ function Test_keep_modified()
normal 5Go* normal 5Go*
call assert_equal(1, getbufvar('', '&modified')) call assert_equal(1, getbufvar('', '&modified'))
doautocmd CursorHold call s:trigger_gitgutter()
call assert_equal(1, getbufvar('', '&modified')) call assert_equal(1, getbufvar('', '&modified'))
endfunction endfunction
@ -204,7 +209,7 @@ function Test_keep_op_marks()
call assert_equal([0,6,1,0], getpos("'[")) call assert_equal([0,6,1,0], getpos("'["))
call assert_equal([0,6,2,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,1,0], getpos("'["))
call assert_equal([0,6,2,0], getpos("']")) call assert_equal([0,6,2,0], getpos("']"))
@ -218,26 +223,15 @@ endfunction
function Test_orphaned_signs() function Test_orphaned_signs()
execute "normal 5GoX\<CR>Y" execute "normal 5GoX\<CR>Y"
write call s:trigger_gitgutter()
6d 6d
write call s:trigger_gitgutter()
let expected = ['line=6 id=3001 name=GitGutterLineAdded'] let expected = ['line=6 id=3001 name=GitGutterLineAdded']
call assert_equal(expected, s:signs('fixture.txt')) call assert_equal(expected, s:signs('fixture.txt'))
endfunction 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() function Test_untracked_file_outside_repo()
let tmp = tempname() let tmp = tempname()
call system('touch '.tmp) call system('touch '.tmp)
@ -252,7 +246,7 @@ function Test_untracked_file_within_repo()
call system('touch '.tmp) call system('touch '.tmp)
execute 'edit '.tmp execute 'edit '.tmp
normal ggo* normal ggo*
doautocmd CursorHold call s:trigger_gitgutter()
call assert_equal([], s:signs(tmp)) call assert_equal([], s:signs(tmp))
@ -265,7 +259,7 @@ function Test_untracked_file_square_brackets_within_repo()
call system('touch '.tmp) call system('touch '.tmp)
execute 'edit '.tmp execute 'edit '.tmp
normal ggo* normal ggo*
doautocmd CursorHold call s:trigger_gitgutter()
call assert_equal([], s:signs(tmp)) 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: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 = [ let expected = [
\ 'diff --git a/fixture.txt b/fixture.txt', \ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index f5c6aff..ae8e546 100644', \ 'index f5c6aff..ae8e546 100644',
@ -313,6 +318,11 @@ function Test_hunk_stage()
\ '+*e' \ '+*e'
\ ] \ ]
call assert_equal(expected, s:git_diff_staged()) call assert_equal(expected, s:git_diff_staged())
" Save the buffer
write
call assert_equal([], s:git_diff())
endfunction endfunction
@ -329,6 +339,31 @@ function Test_hunk_stage_nearby_hunk()
\ ] \ ]
call assert_equal(expected, s:signs('fixture.txt')) 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 = [ let expected = [
\ 'diff --git a/fixture.txt b/fixture.txt', \ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index 53b13df..8fdfda7 100644', \ 'index 53b13df..8fdfda7 100644',
@ -340,16 +375,6 @@ function Test_hunk_stage_nearby_hunk()
\ '+z', \ '+z',
\ ] \ ]
call assert_equal(expected, s:git_diff()) 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 endfunction
@ -359,7 +384,6 @@ function Test_hunk_undo()
normal 5Gi* normal 5Gi*
GitGutterUndoHunk GitGutterUndoHunk
write " write file so we can verify git diff (--staged)
call assert_equal('foo', &shell) call assert_equal('foo', &shell)
let &shell = _shell let &shell = _shell
@ -374,8 +398,9 @@ function Test_undo_nearby_hunk()
execute "normal! 2Gox\<CR>y\<CR>z" execute "normal! 2Gox\<CR>y\<CR>z"
normal 2jdd normal 2jdd
normal k normal k
call s:trigger_gitgutter()
GitGutterUndoHunk GitGutterUndoHunk
write " write file so we can verify git diff (--staged) call s:trigger_gitgutter()
let expected = [ let expected = [
\ 'line=3 id=3000 name=GitGutterLineAdded', \ '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(expected, s:signs('fixture.txt'))
call assert_equal([], s:git_diff())
call assert_equal([], s:git_diff_staged())
" Save the buffer
write
let expected = [ let expected = [
\ 'diff --git a/fixture.txt b/fixture.txt', \ 'diff --git a/fixture.txt b/fixture.txt',
\ 'index f5c6aff..3fbde56 100644', \ 'index f5c6aff..3fbde56 100644',
@ -396,5 +428,148 @@ function Test_undo_nearby_hunk()
\ ] \ ]
call assert_equal(expected, s:git_diff()) 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 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 ## 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: IMPROVEMENTS:
* Add descriptions to neosnippet abbrevations. * Add descriptions to neosnippet abbrevations.
@ -8,6 +18,16 @@ IMPROVEMENTS:
`gometalinter` is run automatically when saving a buffer. Whether the `gometalinter` is run automatically when saving a buffer. Whether the
location list or quickfix list is used can be customized in the usual ways. location list or quickfix list is used can be customized in the usual ways.
[[GH-1652]](https://github.com/fatih/vim-go/pull/1652) [[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: BUG FIXES:
@ -47,7 +67,17 @@ BUG FIXES:
* Show the file location of test errors when the message is empty or begins * Show the file location of test errors when the message is empty or begins
with a newline. with a newline.
[[GH-1664]](https://github.com/fatih/vim-go/pull/1664) [[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: BACKWARDS INCOMPATIBILITIES:
@ -67,6 +97,9 @@ BACKWARDS INCOMPATIBILITIES:
[[GH-1557]](https://github.com/fatih/vim-go/pull/1557) [[GH-1557]](https://github.com/fatih/vim-go/pull/1557)
* Rename g`g:go_metalinter_excludes` to `g:go_metalinter_disabled`. * Rename g`g:go_metalinter_excludes` to `g:go_metalinter_disabled`.
[[GH-1648]](https://github.com/fatih/vim-go/pull/1648) [[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) ## 1.16 - (December 29, 2017)

View file

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

View file

@ -17,7 +17,7 @@ function! go#cmd#Build(bang, ...) abort
let args = let args =
\ ["build"] + \ ["build"] +
\ map(copy(a:000), "expand(v:val)") + \ map(copy(a:000), "expand(v:val)") +
\ ["-i", ".", "errors"] \ [".", "errors"]
" Vim async. " Vim async.
if go#util#has_job() 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' let s:sock_type = (has('win32') || has('win64')) ? 'tcp' : 'unix'
function! s:gocodeCurrentBuffer() abort function! s:gocodeCommand(cmd, args) 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
let bin_path = go#path#CheckBinPath("gocode") let bin_path = go#path#CheckBinPath("gocode")
if empty(bin_path) if empty(bin_path)
return return []
endif 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 " We might hit cache problems, as gocode doesn't handle different GOPATHs
" well. See: https://github.com/nsf/gocode/issues/239 " well. See: https://github.com/nsf/gocode/issues/239
let old_goroot = $GOROOT let old_goroot = $GOROOT
let $GOROOT = go#util#env("goroot") let $GOROOT = go#util#env("goroot")
try try
let socket_type = get(g:, 'go_gocode_socket_type', s:sock_type) let cmd = s:gocodeCommand(a:cmd, a:args)
let cmd = printf('%s -sock %s %s %s %s', " gocode can sometimes be slow, so redraw now to avoid waiting for gocode
\ go#util#Shellescape(bin_path), " to return before redrawing automatically.
\ socket_type, redraw
\ join(a:preargs),
\ go#util#Shellescape(a:cmd),
\ join(a:args)
\ )
let result = go#util#System(cmd) let [l:result, l:err] = go#util#Exec(cmd, a:input)
finally finally
let $GOROOT = old_goroot let $GOROOT = old_goroot
endtry endtry
if go#util#ShellError() != 0 if l:err != 0
return "[\"0\", []]" return "[0, []]"
else endif
if &encoding != 'utf-8' if &encoding != 'utf-8'
let result = iconv(result, 'utf-8', &encoding) let l:result = iconv(l:result, 'utf-8', &encoding)
endif
return result
endif endif
return l:result
endfunction endfunction
function! s:gocodeCurrentBufferOpt(filename) abort " TODO(bc): reset when gocode isn't running
return '-in=' . a:filename
endfunction
let s:optionsEnabled = 0 let s:optionsEnabled = 0
function! s:gocodeEnableOptions() abort function! s:gocodeEnableOptions() abort
if s:optionsEnabled if s:optionsEnabled
@ -78,62 +71,161 @@ endfunction
function! s:gocodeAutocomplete() abort function! s:gocodeAutocomplete() abort
call s:gocodeEnableOptions() call s:gocodeEnableOptions()
let filename = s:gocodeCurrentBuffer() " use the offset as is, because the cursor position is the position for
let result = s:gocodeCommand('autocomplete', " which autocomplete candidates are needed.
\ [s:gocodeCurrentBufferOpt(filename), '-f=vim'], return s:sync_gocode('autocomplete',
\ [expand('%:p'), go#util#OffsetCursor()]) \ [expand('%:p'), go#util#OffsetCursor()],
call delete(filename) \ go#util#GetLines())
return result
endfunction endfunction
" go#complete#GoInfo returns the description of the identifier under the
" cursor.
function! go#complete#GetInfo() abort function! go#complete#GetInfo() abort
let offset = go#util#OffsetCursor()+1 return s:sync_info(0)
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 ""
endfunction endfunction
function! go#complete#Info(auto) abort 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 " auto is true if we were called by g:go_auto_type_info's autocmd
let result = go#complete#GetInfo()
if !empty(result) " add 1 to the offset, so that the position at the cursor will be included
" if auto, and the result is a PANIC by gocode, hide it " in gocode's search
if a:auto && result ==# 'PANIC PANIC PANIC' | return | endif let offset = go#util#OffsetCursor()+1
echo "vim-go: " | echohl Function | echon result | echohl None
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 endif
endfunction endfunction
@ -142,20 +234,22 @@ function! s:trim_bracket(val) abort
return a:val return a:val
endfunction endfunction
let s:completions = ""
function! go#complete#Complete(findstart, base) abort function! go#complete#Complete(findstart, base) abort
"findstart = 1 when we need to get the text length "findstart = 1 when we need to get the text length
if a:findstart == 1 if a:findstart == 1
execute "silent let g:gocomplete_completions = " . s:gocodeAutocomplete() execute "silent let s:completions = " . s:gocodeAutocomplete()
return col('.') - g:gocomplete_completions[0] - 1 return col('.') - s:completions[0] - 1
"findstart = 0 when we need to return the list of completions "findstart = 0 when we need to return the list of completions
else else
let s = getline(".")[col('.') - 1] let s = getline(".")[col('.') - 1]
if s =~ '[(){}\{\}]' 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 endif
return g:gocomplete_completions[1]
return s:completions[1]
endif endif
endf endfunction
function! go#complete#ToggleAutoTypeInfo() abort function! go#complete#ToggleAutoTypeInfo() abort
if get(g:, "go_auto_type_info", 0) 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") call go#util#EchoProgress("auto type info enabled")
endfunction endfunction
" vim: sw=2 ts=2 et " vim: sw=2 ts=2 et

View file

@ -45,7 +45,7 @@ function! go#coverage#Buffer(bang, ...) abort
let l:tmpname = tempname() let l:tmpname = tempname()
if get(g:, 'go_echo_command_info', 1) if get(g:, 'go_echo_command_info', 1)
echon "vim-go: " | echohl Identifier | echon "testing ..." | echohl None call go#util#EchoProgress("testing...")
endif endif
if go#util#has_job() 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" if has_key(l:list_title, "title") && l:list_title['title'] == "Format"
call go#list#Clean(l:listtype) call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
endif endif
endfunction endfunction

View file

@ -78,7 +78,7 @@ function! s:guru_cmd(args) range abort
let scopes = go#util#StripTrailingSlash(scopes) let scopes = go#util#StripTrailingSlash(scopes)
" create shell-safe entries of the list " 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 " guru expect a comma-separated list of patterns, construct it
let l:scope = join(scopes, ",") let l:scope = join(scopes, ",")
@ -129,7 +129,7 @@ function! s:sync_guru(args) abort
endif endif
if has_key(a:args, 'custom_parse') 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 else
call s:parse_guru_output(go#util#ShellError(), out, a:args.mode) call s:parse_guru_output(go#util#ShellError(), out, a:args.mode)
endif endif
@ -137,6 +137,33 @@ function! s:sync_guru(args) abort
return out return out
endfunc 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 " async_guru runs guru in async mode with the given arguments
function! s:async_guru(args) abort function! s:async_guru(args) abort
let result = s:guru_cmd(a:args) let result = s:guru_cmd(a:args)
@ -145,8 +172,6 @@ function! s:async_guru(args) abort
return return
endif endif
let status_dir = expand('%:p:h')
let statusline_type = printf("%s", a:args.mode)
if !has_key(a:args, 'disable_progress') if !has_key(a:args, 'disable_progress')
if a:args.needs_scope if a:args.needs_scope
@ -155,59 +180,63 @@ function! s:async_guru(args) abort
endif endif
endif endif
let messages = [] let state = {
function! s:callback(chan, msg) closure \ 'status_dir': expand('%:p:h'),
call add(messages, a:msg) \ '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 endfunction
let status = {} function! s:exit_cb(job, exitval) dict
let exitval = 0 let self.exited = 1
let closed = 0
let exited = 0
function! s:exit_cb(job, exitval) closure
let exited = 1
let status = { let status = {
\ 'desc': 'last status', \ 'desc': 'last status',
\ 'type': statusline_type, \ 'type': self.statusline_type,
\ 'state': "finished", \ 'state': "finished",
\ } \ }
if a:exitval if a:exitval
let exitval = a:exitval let self.exitval = a:exitval
let status.state = "failed" let status.state = "failed"
endif endif
call go#statusline#Update(status_dir, status) call go#statusline#Update(self.status_dir, status)
if closed if self.closed
call s:complete() call self.complete()
endif endif
endfunction endfunction
function! s:close_cb(ch) closure function! s:close_cb(ch) dict
let closed = 1 let self.closed = 1
if exited if self.exited
call s:complete() call self.complete()
endif endif
endfunction endfunction
function! s:complete() closure function state.complete() dict
let out = join(messages, "\n") let out = join(self.messages, "\n")
if has_key(a:args, 'custom_parse') call self.parse(self.exitval, out, self.mode)
call a:args.custom_parse(exitval, out)
else
call s:parse_guru_output(exitval, out, a:args.mode)
endif
endfunction 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 = { let start_options = {
\ 'callback': funcref("s:callback"), \ 'callback': function('s:callback', [], state),
\ 'exit_cb': funcref("s:exit_cb"), \ 'exit_cb': function('s:exit_cb', [], state),
\ 'close_cb': funcref("s:close_cb"), \ 'close_cb': function('s:close_cb', [], state)
\ } \ }
if has_key(result, 'stdin_content') if has_key(result, 'stdin_content')
@ -217,18 +246,18 @@ function! s:async_guru(args) abort
let l:start_options.in_name = l:tmpname let l:start_options.in_name = l:tmpname
endif endif
call go#statusline#Update(status_dir, { call go#statusline#Update(state.status_dir, {
\ 'desc': "current status", \ 'desc': "current status",
\ 'type': statusline_type, \ 'type': state.statusline_type,
\ 'state': "analysing", \ 'state': "analysing",
\}) \})
return job_start(result.cmd, start_options) return s:job_start(result.cmd, start_options)
endfunc endfunc
" run_guru runs the given guru argument " run_guru runs the given guru argument
function! s:run_guru(args) abort 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) let res = s:async_guru(a:args)
else else
let res = s:sync_guru(a:args) let res = s:sync_guru(a:args)
@ -289,7 +318,7 @@ function! go#guru#DescribeInfo() abort
return return
endif endif
function! s:info(exit_val, output) function! s:info(exit_val, output, mode)
if a:exit_val != 0 if a:exit_val != 0
return return
endif endif
@ -464,10 +493,6 @@ function! go#guru#Referrers(selected) abort
call s:run_guru(args) call s:run_guru(args)
endfunction endfunction
function! go#guru#SameIdsTimer() abort
call timer_start(200, function('go#guru#SameIds'), {'repeat': -1})
endfunction
function! go#guru#SameIds() abort function! go#guru#SameIds() abort
" we use matchaddpos() which was introduce with 7.4.330, be sure we have " 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 " 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) call s:run_guru(args)
endfunction 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. call go#guru#ClearSameIds() " run after calling guru to reduce flicker.
if a:output[0] !=# '{' if a:output[0] !=# '{'

View file

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

View file

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

View file

@ -105,7 +105,6 @@ function! go#lint#Gometa(autosave, ...) abort
if l:err == 0 if l:err == 0
call go#list#Clean(l:listtype) call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
echon "vim-go: " | echohl Function | echon "[metalinter] PASS" | echohl None echon "vim-go: " | echohl Function | echon "[metalinter] PASS" | echohl None
else else
" GoMetaLinter can output one of the two, so we look for both: " GoMetaLinter can output one of the two, so we look for both:
@ -147,7 +146,7 @@ function! go#lint#Golint(...) abort
endif endif
let l:listtype = go#list#Type("GoLint") 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) let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors)) call go#list#Window(l:listtype, len(errors))
call go#list#JumpToFirst(l:listtype) call go#list#JumpToFirst(l:listtype)
@ -166,8 +165,9 @@ function! go#lint#Vet(bang, ...) abort
let l:listtype = go#list#Type("GoVet") let l:listtype = go#list#Type("GoVet")
if go#util#ShellError() != 0 if go#util#ShellError() != 0
let errors = go#tool#ParseErrors(split(out, '\n')) let errorformat="%-Gexit status %\\d%\\+," . &errorformat
call go#list#Populate(l:listtype, errors, 'Vet') 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)) call go#list#Window(l:listtype, len(errors))
if !empty(errors) && !a:bang if !empty(errors) && !a:bang
call go#list#JumpToFirst(l:listtype) 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 echon "vim-go: " | echohl ErrorMsg | echon "[vet] FAIL" | echohl None
else else
call go#list#Clean(l:listtype) call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
redraw | echon "vim-go: " | echohl Function | echon "[vet] PASS" | echohl None redraw | echon "vim-go: " | echohl Function | echon "[vet] PASS" | echohl None
endif endif
endfunction endfunction
@ -228,7 +227,6 @@ function! go#lint#Errcheck(...) abort
endif endif
else else
call go#list#Clean(l:listtype) call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
echon "vim-go: " | echohl Function | echon "[errcheck] PASS" | echohl None echon "vim-go: " | echohl Function | echon "[errcheck] PASS" | echohl None
endif endif
@ -246,10 +244,18 @@ function! go#lint#ToggleMetaLinterAutoSave() abort
endfunction endfunction
function! s:lint_job(args, autosave) function! s:lint_job(args, autosave)
let status_dir = expand('%:p:h') let state = {
let started_at = reltime() \ '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", \ 'desc': "current status",
\ 'type': "gometalinter", \ 'type': "gometalinter",
\ 'state': "analysing", \ 'state': "analysing",
@ -259,26 +265,18 @@ function! s:lint_job(args, autosave)
call go#cmd#autowrite() call go#cmd#autowrite()
if a:autosave if a:autosave
let l:listtype = go#list#Type("GoMetaLinterAutoSave") let state.listtype = go#list#Type("GoMetaLinterAutoSave")
else else
let l:listtype = go#list#Type("GoMetaLinter") let state.listtype = go#list#Type("GoMetaLinter")
endif endif
let l:errformat = '%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m' function! s:callback(chan, msg) dict closure
call add(self.messages, a:msg)
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)
endfunction endfunction
function! s:exit_cb(job, exitval) closure function! s:exit_cb(job, exitval) dict
let exited = 1 let self.exited = 1
let exit_status = a:exitval let self.exit_status = a:exitval
let status = { let status = {
\ 'desc': 'last status', \ 'desc': 'last status',
@ -290,50 +288,63 @@ function! s:lint_job(args, autosave)
let status.state = "failed" let status.state = "failed"
endif endif
let elapsed_time = reltimestr(reltime(started_at)) let elapsed_time = reltimestr(reltime(self.started_at))
" strip whitespace " strip whitespace
let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '') let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '')
let status.state .= printf(" (%ss)", elapsed_time) let status.state .= printf(" (%ss)", elapsed_time)
call go#statusline#Update(status_dir, status) call go#statusline#Update(self.status_dir, status)
if closed if self.closed
call s:show_errors() call self.show_errors()
endif endif
endfunction endfunction
function! s:close_cb(ch) closure function! s:close_cb(ch) dict
let closed = 1 let self.closed = 1
if exited if self.exited
call s:show_errors() call self.show_errors()
endif endif
endfunction 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 " 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 " run when the listtype is locationlist so that the location list for the
" correct window will be populated. " correct window will be populated.
if l:listtype == 'locationlist' if self.listtype == 'locationlist'
exe l:winnr . "wincmd w" exe self.winnr . "wincmd w"
endif endif
let l:errorformat = '%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m' 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) let errors = go#list#Get(self.listtype)
call go#list#Window(l:listtype, len(errors)) 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) if get(g:, 'go_echo_command_info', 1)
call go#util#EchoSuccess("linting finished") call go#util#EchoSuccess("linting finished")
endif endif
endfunction 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 = { let start_options = {
\ 'callback': funcref("s:callback"), \ 'callback': funcref("s:callback", [], state),
\ 'exit_cb': funcref("s:exit_cb"), \ 'exit_cb': funcref("s:exit_cb", [], state),
\ 'close_cb': funcref("s:close_cb"), \ 'close_cb': funcref("s:close_cb", [], state),
\ } \ }
call job_start(a:args.cmd, start_options) 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' silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
let expected = [ 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 " clear the quickfix lists
@ -37,7 +37,7 @@ func! Test_GometaWithDisabled() abort
silent exe 'e ' . $GOPATH . '/src/lint/lint.go' silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
let expected = [ 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 " clear the quickfix lists
@ -71,7 +71,7 @@ func! Test_GometaAutoSave() abort
silent exe 'e ' . $GOPATH . '/src/lint/lint.go' silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
let expected = [ 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() let winnr = winnr()
@ -102,4 +102,30 @@ func! Test_GometaAutoSave() abort
let g:go_metalinter_autosave_enabled = orig_go_metalinter_autosave_enabled let g:go_metalinter_autosave_enabled = orig_go_metalinter_autosave_enabled
endfunc 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 " 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 " location list increases/decreases, cwindow will not resize when a new
" updated height is passed. lopen in the other hand resizes the screen. " updated height is passed. lopen in the other hand resizes the screen.
if !a:0 || a:1 == 0 if !a:0 || a:1 == 0
let autoclose_window = get(g:, 'go_list_autoclose', 1) call go#list#Close(a:listtype)
if autoclose_window
if a:listtype == "locationlist"
lclose
else
cclose
endif
endif
return return
endif endif
@ -79,13 +72,7 @@ function! go#list#ParseFormat(listtype, errformat, items, title) abort
" parse and populate the location list " parse and populate the location list
let &errorformat = a:errformat let &errorformat = a:errformat
try try
if a:listtype == "locationlist" call go#list#Parse(a:listtype, a:items, a:title)
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
finally finally
"restore back "restore back
let &errorformat = old_errorformat let &errorformat = old_errorformat
@ -94,11 +81,13 @@ endfunction
" Parse parses the given items based on the global errorformat and " Parse parses the given items based on the global errorformat and
" populates the list. " populates the list.
function! go#list#Parse(listtype, items) abort function! go#list#Parse(listtype, items, title) abort
if a:listtype == "locationlist" if a:listtype == "locationlist"
lgetexpr a:items lgetexpr a:items
if has("patch-7.4.2200") | call setloclist(0, [], 'a', {'title': a:title}) | endif
else else
cgetexpr a:items cgetexpr a:items
if has("patch-7.4.2200") | call setqflist([], 'a', {'title': a:title}) | endif
endif endif
endfunction endfunction
@ -111,13 +100,29 @@ function! go#list#JumpToFirst(listtype) abort
endif endif
endfunction endfunction
" Clean cleans the location list " Clean cleans and closes the location list
function! go#list#Clean(listtype) abort function! go#list#Clean(listtype) abort
if a:listtype == "locationlist" if a:listtype == "locationlist"
lex [] lex []
else else
cex [] cex []
endif 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 endfunction
function! s:listtype(listtype) abort function! s:listtype(listtype) abort

View file

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

View file

@ -72,20 +72,22 @@ function! go#rename#Rename(bang, ...) abort
endfunction endfunction
function s:rename_job(args) function s:rename_job(args)
let exited = 0 let state = {
let closed = 0 \ 'exited': 0,
let exitval = 0 \ 'closed': 0,
let messages = [] \ 'exitval': 0,
\ 'messages': [],
\ 'status_dir': expand('%:p:h'),
\ 'bang': a:args.bang
\ }
function! s:callback(chan, msg) closure function! s:callback(chan, msg) dict
call add(messages, a:msg) call add(self.messages, a:msg)
endfunction endfunction
let status_dir = expand('%:p:h') function! s:exit_cb(job, exitval) dict
let self.exited = 1
function! s:exit_cb(job, exitval) closure let self.exitval = a:exitval
let exited = 1
let exitval = a:exitval
let status = { let status = {
\ 'desc': 'last status', \ 'desc': 'last status',
@ -97,28 +99,30 @@ function s:rename_job(args)
let status.state = "failed" let status.state = "failed"
endif endif
call go#statusline#Update(status_dir, status) call go#statusline#Update(self.status_dir, status)
if closed if self.closed
call s:parse_errors(a:exitval, a:args.bang, messages) call s:parse_errors(self.exitval, self.bang, self.messages)
endif endif
endfunction endfunction
function! s:close_cb(ch) closure function! s:close_cb(ch) dict
let closed = 1 let self.closed = 1
if exited if self.exited
call s:parse_errors(exitval, a:args.bang, messages) call s:parse_errors(self.exitval, self.bang, self.messages)
endif endif
endfunction 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 = { let start_options = {
\ 'callback': funcref("s:callback"), \ 'callback': funcref("s:callback", [], state),
\ 'exit_cb': funcref("s:exit_cb"), \ 'exit_cb': funcref("s:exit_cb", [], state),
\ 'close_cb': funcref("s:close_cb"), \ 'close_cb': funcref("s:close_cb", [], state),
\ } \ }
call go#statusline#Update(status_dir, { call go#statusline#Update(state.status_dir, {
\ 'desc': "current status", \ 'desc': "current status",
\ 'type': "gorename", \ 'type': "gorename",
\ 'state': "started", \ '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 " strip out newline on the end that gorename puts. If we don't remove, it
" will trigger the 'Hit ENTER to continue' prompt " will trigger the 'Hit ENTER to continue' prompt
call go#list#Clean(l:listtype) call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
call go#util#EchoSuccess(a:out[0]) call go#util#EchoSuccess(a:out[0])
" refresh the buffer so we can see the new content " 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") let l:template_file = get(g:, 'go_template_file', "hello_world.go")
endif endif
let l:template_path = go#util#Join(l:root_dir, "templates", l:template_file) 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 elseif l:package_name == -1 && l:go_template_use_pkg == 1
" cwd is now the dir of the package " cwd is now the dir of the package
let l:path = fnamemodify(getcwd(), ':t') 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' let g:go_term_mode = 'vsplit'
endif 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 " 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 " global variable g:go_term_mode, which is by default set to :vsplit
function! go#term#new(bang, cmd) abort 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 let mode = g:go_term_mode
endif endif
let state = {
\ 'cmd': a:cmd,
\ 'bang' : a:bang,
\ 'winid': win_getid(winnr()),
\ 'stdout': []
\ }
" execute go build in the files directory " execute go build in the files directory
let l:winnr = winnr()
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd() let dir = getcwd()
@ -33,30 +36,27 @@ function! go#term#newmode(bang, cmd, mode) abort
setlocal noswapfile setlocal noswapfile
setlocal nobuflisted 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 = { let job = {
\ 'stderr' : [], \ 'on_stdout': function('s:on_stdout', [], state),
\ 'stdout' : [], \ 'on_exit' : function('s:on_exit', [], state),
\ 'bang' : a:bang,
\ 'on_stdout': function('s:on_stdout'),
\ 'on_stderr': function('s:on_stderr'),
\ 'on_exit' : function('s:on_exit'),
\ } \ }
let id = termopen(a:cmd, job) let state.id = termopen(a:cmd, job)
let state.termwinid = win_getid(winnr())
execute cd . fnameescape(dir) execute cd . fnameescape(dir)
let job.id = id
let job.cmd = a:cmd
startinsert
" resize new term if needed. " resize new term if needed.
let height = get(g:, 'go_term_height', winheight(0)) let height = get(g:, 'go_term_height', winheight(0))
let width = get(g:, 'go_term_width', winwidth(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 " Adjust the window width or height depending on whether it's a vertical or
" the height. The below command resizes the buffer " horizontal split.
if mode =~ "vertical" || mode =~ "vsplit" || mode =~ "vnew" if mode =~ "vertical" || mode =~ "vsplit" || mode =~ "vnew"
exe 'vertical resize ' . width exe 'vertical resize ' . width
elseif mode =~ "split" || mode =~ "new" elseif mode =~ "split" || mode =~ "new"
@ -64,77 +64,56 @@ function! go#term#newmode(bang, cmd, mode) abort
endif endif
" we also need to resize the pty, so there you go... " 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 call win_gotoid(state.winid)
stopinsert
if l:winnr !=# winnr() return state.id
exe l:winnr . "wincmd w"
endif
return id
endfunction endfunction
function! s:on_stdout(job_id, data, event) dict abort function! s:on_stdout(job_id, data, event) dict abort
if !has_key(s:jobs, a:job_id) call extend(self.stdout, a:data)
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)
endfunction endfunction
function! s:on_exit(job_id, exit_status, event) dict abort 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") let l:listtype = go#list#Type("_term")
" usually there is always output so never branch into this clause " usually there is always output so never branch into this clause
if empty(job.stdout) if empty(self.stdout)
call go#list#Clean(l:listtype) call s:cleanlist(self.winid, l:listtype)
call go#list#Window(l:listtype)
unlet s:jobs[a:job_id]
return return
endif endif
let errors = go#tool#ParseErrors(job.stdout) let errors = go#tool#ParseErrors(self.stdout)
let errors = go#tool#FilterValids(errors) let errors = go#tool#FilterValids(errors)
if !empty(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 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)) call go#list#Window(l:listtype, len(errors))
if !self.bang if !self.bang
call go#list#JumpToFirst(l:listtype) call go#list#JumpToFirst(l:listtype)
endif endif
unlet s:jobs[a:job_id]
return return
endif endif
" tests are passing clean the list and close the list. But we only can call s:cleanlist(self.winid, l:listtype)
" close them from a normal view, so jump back, close the list and then endfunction
" again jump back to the terminal
wincmd p
call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
wincmd p
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 endfunction
" vim: sw=2 ts=2 et " 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") call go#util#EchoError("[test] FAIL")
else else
call go#list#Clean(l:listtype) call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
if a:compile if a:compile
call go#util#EchoSuccess("[test] SUCCESS") call go#util#EchoSuccess("[test] SUCCESS")
@ -139,9 +138,6 @@ function! go#test#Func(bang, ...) abort
endfunction endfunction
function! s:test_job(args) abort function! s:test_job(args) abort
let status_dir = expand('%:p:h')
let started_at = reltime()
let status = { let status = {
\ 'desc': 'current status', \ 'desc': 'current status',
\ 'type': "test", \ 'type': "test",
@ -152,23 +148,29 @@ function! s:test_job(args) abort
let status.state = "compiling" let status.state = "compiling"
endif endif
call go#statusline#Update(status_dir, status)
" autowrite is not enabled for jobs " autowrite is not enabled for jobs
call go#cmd#autowrite() call go#cmd#autowrite()
let l:exited = 0 let state = {
let l:closed = 0 \ 'exited': 0,
let l:exitval = 0 \ 'closed': 0,
let messages = [] \ '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 go#statusline#Update(state.status_dir, status)
call add(messages, a:msg)
function! s:callback(chan, msg) dict
call add(self.messages, a:msg)
endfunction endfunction
function! s:exit_cb(job, exitval) closure function! s:exit_cb(job, exitval) dict
let exited = 1 let self.exited = 1
let exitval = a:exitval let self.exitval = a:exitval
let status = { let status = {
\ 'desc': 'last status', \ 'desc': 'last status',
@ -176,7 +178,7 @@ function! s:test_job(args) abort
\ 'state': "pass", \ 'state': "pass",
\ } \ }
if a:args.compile_test if self.compile_test
let status.state = "success" let status.state = "success"
endif endif
@ -186,7 +188,7 @@ function! s:test_job(args) abort
if get(g:, 'go_echo_command_info', 1) if get(g:, 'go_echo_command_info', 1)
if a:exitval == 0 if a:exitval == 0
if a:args.compile_test if self.compile_test
call go#util#EchoSuccess("[test] SUCCESS") call go#util#EchoSuccess("[test] SUCCESS")
else else
call go#util#EchoSuccess("[test] PASS") call go#util#EchoSuccess("[test] PASS")
@ -196,30 +198,32 @@ function! s:test_job(args) abort
endif endif
endif endif
let elapsed_time = reltimestr(reltime(started_at)) let elapsed_time = reltimestr(reltime(self.started_at))
" strip whitespace " strip whitespace
let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '') let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '')
let status.state .= printf(" (%ss)", elapsed_time) let status.state .= printf(" (%ss)", elapsed_time)
call go#statusline#Update(status_dir, status) call go#statusline#Update(self.status_dir, status)
if closed if self.closed
call s:show_errors(a:args, l:exitval, messages) call s:show_errors(self.args, self.exitval, self.messages)
endif endif
endfunction endfunction
function! s:close_cb(ch) closure function! s:close_cb(ch) dict
let closed = 1 let self.closed = 1
if exited if self.exited
call s:show_errors(a:args, l:exitval, messages) call s:show_errors(self.args, self.exitval, self.messages)
endif endif
endfunction 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 = { let start_options = {
\ 'callback': funcref("s:callback"), \ 'callback': funcref("s:callback", [], state),
\ 'exit_cb': funcref("s:exit_cb"), \ 'exit_cb': funcref("s:exit_cb", [], state),
\ 'close_cb': funcref("s:close_cb"), \ 'close_cb': funcref("s:close_cb", [], state)
\ } \ }
" pre start " pre start
@ -241,7 +245,6 @@ function! s:show_errors(args, exit_val, messages) abort
let l:listtype = go#list#Type("GoTest") let l:listtype = go#list#Type("GoTest")
if a:exit_val == 0 if a:exit_val == 0
call go#list#Clean(l:listtype) call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
return return
endif endif

View file

@ -17,6 +17,7 @@ endif
" < > " < >
" t for tag " t for tag
" Select a function in visual mode.
function! go#textobj#Function(mode) abort function! go#textobj#Function(mode) abort
let offset = go#util#OffsetCursor() let offset = go#util#OffsetCursor()
@ -98,23 +99,8 @@ function! go#textobj#Function(mode) abort
call cursor(info.rbrace.line-1, 1) call cursor(info.rbrace.line-1, 1)
endfunction endfunction
function! go#textobj#FunctionJump(mode, direction) abort " Get the location of the previous or next function.
" get count of the motion. This should be done before all the normal function! go#textobj#FunctionLocation(direction, cnt) abort
" 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 offset = go#util#OffsetCursor() let offset = go#util#OffsetCursor()
let fname = shellescape(expand("%:p")) let fname = shellescape(expand("%:p"))
@ -131,7 +117,7 @@ function! go#textobj#FunctionJump(mode, direction) abort
endif endif
let command = printf("%s -format vim -file %s -offset %s", bin_path, fname, offset) 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' if a:direction == 'next'
let command .= ' -mode next' let command .= ' -mode next'
@ -154,9 +140,33 @@ function! go#textobj#FunctionJump(mode, direction) abort
call delete(l:tmpname) call delete(l:tmpname)
endif endif
" convert our string dict representation into native Vim dictionary type let l:result = json_decode(out)
let result = eval(out) if type(l:result) != 4 || !has_key(l:result, 'fn')
if type(result) != 4 || !has_key(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 return
endif endif

View file

@ -330,6 +330,7 @@ function! go#util#EchoWarning(msg)
call s:echo(a:msg, 'WarningMsg') call s:echo(a:msg, 'WarningMsg')
endfunction endfunction
function! go#util#EchoProgress(msg) function! go#util#EchoProgress(msg)
redraw
call s:echo(a:msg, 'Identifier') call s:echo(a:msg, 'Identifier')
endfunction endfunction
function! go#util#EchoInfo(msg) function! go#util#EchoInfo(msg)
@ -362,7 +363,6 @@ function! go#util#archive()
return expand("%:p:gs!\\!/!") . "\n" . strlen(l:buffer) . "\n" . l:buffer return expand("%:p:gs!\\!/!") . "\n" . strlen(l:buffer) . "\n" . l:buffer
endfunction endfunction
" Make a named temporary directory which starts with "prefix". " Make a named temporary directory which starts with "prefix".
" "
" Unfortunately Vim's tempname() is not portable enough across various systems; " Unfortunately Vim's tempname() is not portable enough across various systems;
@ -384,7 +384,7 @@ function! go#util#tempdir(prefix) abort
endfor endfor
if l:dir == '' 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 return
endif endif
@ -395,4 +395,9 @@ function! go#util#tempdir(prefix) abort
return l:tmp return l:tmp
endfunction 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 " vim: sw=2 ts=2 et

View file

@ -22,10 +22,11 @@ CONTENTS *go-contents*
6. Functions....................................|go-functions| 6. Functions....................................|go-functions|
7. Settings.....................................|go-settings| 7. Settings.....................................|go-settings|
8. Syntax highlighting..........................|go-syntax| 8. Syntax highlighting..........................|go-syntax|
9. FAQ/Troubleshooting..........................|go-troubleshooting| 9. Debugger.....................................|go-debug|
10. Development..................................|go-development| 10. FAQ/Troubleshooting..........................|go-troubleshooting|
11. Donation.....................................|go-donation| 11. Development..................................|go-development|
12. Credits......................................|go-credits| 12. Donation.....................................|go-donation|
13. Credits......................................|go-credits|
============================================================================== ==============================================================================
INTRO *go-intro* 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. tools developed by the Go community to provide a seamless Vim experience.
* Compile your package with |:GoBuild|, install it with |:GoInstall| or * 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|. * Quickly execute your current file(s) with |:GoRun|.
* Improved syntax highlighting and folding. * Improved syntax highlighting and folding.
* Debug programs with integrated `delve` support with |:GoDebugStart|.
* Completion support via `gocode`. * Completion support via `gocode`.
* `gofmt` or `goimports` on save keeps the cursor position and undo history. * `gofmt` or `goimports` on save keeps the cursor position and undo history.
* Go to symbol/declaration with |:GoDef|. * Go to symbol/declaration with |:GoDef|.
* Look up documentation with |:GoDoc| or |:GoDocBrowser|. * Look up documentation with |:GoDoc| or |:GoDocBrowser|.
* Easily import packages via |:GoImport|, remove them via |:GoDrop|. * Easily import packages via |:GoImport|, remove them via |:GoDrop|.
* Automatic `GOPATH` detection which works with `gb` and `godep`. Change or * Precise type-safe renaming of identifiers with |:GoRename|.
display `GOPATH` with |:GoPath|.
* See which code is covered by tests with |:GoCoverage|. * See which code is covered by tests with |:GoCoverage|.
* Add or remove tags on struct fields with |:GoAddTags| and |:GoRemoveTags|. * Add or remove tags on struct fields with |:GoAddTags| and |:GoRemoveTags|.
* Call `gometalinter` with |:GoMetaLinter| to invoke all possible linters * 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|. static errors, or make sure errors are checked with |:GoErrCheck|.
* Advanced source analysis tools utilizing `guru`, such as |:GoImplements|, * Advanced source analysis tools utilizing `guru`, such as |:GoImplements|,
|:GoCallees|, and |:GoReferrers|. |: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`, * Integrated and improved snippets, supporting `ultisnips`, `neosnippet`,
and `vim-minisnip`. and `vim-minisnip`.
* Share your current code to play.golang.org with |:GoPlay|. * 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()* *go#complete#GetInfo()*
Returns the description of the identifer under the cursor. Can be used to plug 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* SETTINGS *go-settings*
@ -1658,6 +1660,18 @@ By default "snakecase" is used. Current values are: ["snakecase",
> >
let g:go_addtags_transform = '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* 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. `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* FAQ TROUBLESHOOTING *go-troubleshooting*

View file

@ -98,4 +98,11 @@ command! -nargs=0 GoKeyify call go#keyify#Keyify()
" -- fillstruct " -- fillstruct
command! -nargs=0 GoFillStruct call go#fillstruct#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 " vim: sw=2 ts=2 et

View file

@ -40,7 +40,7 @@ function! s:GoMinisnip() abort
endif endif
if exists('g:minisnip_dir') 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 else
let g:minisnip_dir = globpath(&rtp, 'gosnippets/minisnip') let g:minisnip_dir = globpath(&rtp, 'gosnippets/minisnip')
endif endif

View file

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

View file

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

View file

@ -31,6 +31,7 @@ endif
" needed by the user with GoInstallBinaries " needed by the user with GoInstallBinaries
let s:packages = { let s:packages = {
\ 'asmfmt': ['github.com/klauspost/asmfmt/cmd/asmfmt'], \ 'asmfmt': ['github.com/klauspost/asmfmt/cmd/asmfmt'],
\ 'dlv': ['github.com/derekparker/delve/cmd/dlv'],
\ 'errcheck': ['github.com/kisielk/errcheck'], \ 'errcheck': ['github.com/kisielk/errcheck'],
\ 'fillstruct': ['github.com/davidrjenni/reftools/cmd/fillstruct'], \ 'fillstruct': ['github.com/davidrjenni/reftools/cmd/fillstruct'],
\ 'gocode': ['github.com/nsf/gocode', {'windows': '-ldflags -H=windowsgui'}], \ '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' \ . ' contains=@goCommentGroup,@Spell'
\ . (s:fold_package_comment ? ' fold' : '') \ . (s:fold_package_comment ? ' fold' : '')
exe 'syn region goPackageComment start=/\v\/\*.*\n(.*\n)*\s*\*\/\npackage/' 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' \ . ' contains=@goCommentGroup,@Spell'
\ . (s:fold_package_comment ? ' fold' : '') \ . (s:fold_package_comment ? ' fold' : '')
hi def link goPackageComment Comment 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 ### 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 ```vim
let g:vim_markdown_emphasis_multiline = 0 let g:vim_markdown_emphasis_multiline = 0
@ -176,7 +176,7 @@ Default is `['c++=cpp', 'viml=vim', 'bash=sh', 'ini=dosini']`.
### Follow named anchors ### 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 `file#anchor` or just `#anchor`, where file may omit the `.md` extension as
usual. Two variables control its operation: usual. Two variables control its operation:

View file

@ -764,10 +764,10 @@ function! s:MarkdownClearSyntaxVariables()
endfunction endfunction
augroup Mkd augroup Mkd
autocmd! autocmd! * <buffer>
au BufWinEnter * call s:MarkdownRefreshSyntax(1) autocmd BufWinEnter <buffer> call s:MarkdownRefreshSyntax(1)
au BufUnload * call s:MarkdownClearSyntaxVariables() autocmd BufUnload <buffer> call s:MarkdownClearSyntaxVariables()
au BufWritePost * call s:MarkdownRefreshSyntax(0) autocmd BufWritePost <buffer> call s:MarkdownRefreshSyntax(0)
au InsertEnter,InsertLeave * call s:MarkdownRefreshSyntax(0) autocmd InsertEnter,InsertLeave <buffer> call s:MarkdownRefreshSyntax(0)
au CursorHold,CursorHoldI * call s:MarkdownRefreshSyntax(0) autocmd CursorHold,CursorHoldI <buffer> call s:MarkdownRefreshSyntax(0)
augroup END 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 ### Add a cursor to each line of your visual selection
![Example2](assets/example2.gif?raw=true) ![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 :) ### Do it backwards too! This is not just a replay of the above gif :)
![Example3](assets/example3.gif?raw=true) ![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 - Live update in Insert mode
- One key to rule it all! See [Quick Start](#quick-start) on what the key does in different scenarios - 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 - Works in Normal, Insert, and Visual mode for any commands (including
multi-key commands, assuming you set `g:multicursor_insert_maps` and multi-key commands, assuming you set `g:multicursor_normal_maps`;
`g:multicursor_normal_maps`; see Settings below for details) see Settings below for details)
## Installation ## Installation
Install using [Pathogen], [Vundle], [Neobundle], or your favorite Vim package manager. 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) ### ```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. 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) ### ```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}` 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() function! s:fire_pre_triggers()
if !s:before_function_called if !s:before_function_called
doautocmd User MultipleCursorsPre silent doautocmd User MultipleCursorsPre
if exists('*Multiple_cursors_before') if exists('*Multiple_cursors_before')
exe "call Multiple_cursors_before()" exe "call Multiple_cursors_before()"
endif endif
@ -445,7 +445,7 @@ function! s:CursorManager.reset(restore_view, restore_setting, ...) dict
if exists('*Multiple_cursors_after') if exists('*Multiple_cursors_after')
exe "call Multiple_cursors_after()" exe "call Multiple_cursors_after()"
endif endif
doautocmd User MultipleCursorsPost silent doautocmd User MultipleCursorsPost
let s:before_function_called = 0 let s:before_function_called = 0
endif endif
endfunction endfunction
@ -516,7 +516,6 @@ function! s:CursorManager.update_current() dict
if s:to_mode ==# 'V' if s:to_mode ==# 'V'
exec "normal! gvv\<Esc>" exec "normal! gvv\<Esc>"
endif endif
" Sets the cursor at the right place " Sets the cursor at the right place
exec "normal! gv\<Esc>" exec "normal! gv\<Esc>"
call cur.update_visual_selection(s:get_visual_region(s:pos('.'))) 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 " This should be executed after user input is processed, when unnamed
" register already contains the text. " register already contains the text.
call cur.save_unnamed_register() call cur.save_unnamed_register()
call cur.remove_visual_selection() 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 normal! h
elseif s:from_mode ==# 'n' elseif s:from_mode ==# 'n'
" Save contents of unnamed register after each operation in Normal mode. " Save contents of unnamed register after each operation in Normal mode.
call cur.save_unnamed_register() call cur.save_unnamed_register()
endif 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 " If the total number of lines changed in the buffer, we need to potentially
" adjust other cursor locations " adjust other cursor locations
let vdelta = line('$') - s:saved_linecount
if vdelta != 0 if vdelta != 0
if self.current_index != self.size() - 1 if self.current_index != self.size() - 1
let cur_column_offset = (cur.column() - col('.')) * -1 let cur_column_offset = (cur.column() - col('.')) * -1
@ -547,7 +547,7 @@ function! s:CursorManager.update_current() dict
let c = self.get(i) let c = self.get(i)
" If there're other cursors on the same line, we need to adjust their " If there're other cursors on the same line, we need to adjust their
" columns. This needs to happen before we adjust their line! " 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 if vdelta > 0
" Added a line " Added a line
let hdelta = cur_column_offset let hdelta = cur_column_offset
@ -583,7 +583,6 @@ function! s:CursorManager.update_current() dict
endif endif
endif endif
let pos = s:pos('.')
if cur.position == pos if cur.position == pos
return 0 return 0
endif endif
@ -598,7 +597,8 @@ endfunction
" Start tracking cursor updates " Start tracking cursor updates
function! s:CursorManager.start_loop() dict function! s:CursorManager.start_loop() dict
let self.starting_index = self.current_index let self.current_index = 0
let self.starting_index = 0
endfunction endfunction
" Returns true if we're cycled through all the cursors " 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 = "" let s:saved_keys = ""
endif endif
if s:from_mode ==# 'i' && has_key(g:multi_cursor_insert_maps, s:last_char()) " ambiguous mappings are note supported; e.g.:
let c = getchar(0) " imap jj JJ
let char_type = type(c) " imap jjj JJJ
" will always trigger the 'jj' mapping
if s:from_mode ==# 'i' && mapcheck(s:char, "i") != ""
let poll_count = 0 let poll_count = 0
while char_type == 0 && c == 0 && poll_count < &timeoutlen let map_dict = {}
sleep 1m while poll_count < &timeoutlen
let c = getchar(0) let c = getchar(0)
let char_type = type(c) let char_type = type(c)
let poll_count += 1 let poll_count += 1.5
endwhile
if char_type == 0 && c != 0 if char_type == 0 && c != 0
let s:char .= nr2char(c) let s:char .= nr2char(c)
elseif char_type == 1 " char with more than 8 bits (as string) elseif char_type == 1 " char with more than 8 bits (as string)
let s:char .= c let s:char .= c
endif 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] ==# ":" elseif s:from_mode !=# 'i' && s:char[0] ==# ":"
call feedkeys(s:char) call feedkeys(s:char)
call s:cm.reset(1, 1) call s:cm.reset(1, 1)
return return
elseif s:from_mode ==# 'n' elseif s:from_mode ==# 'n' || s:from_mode =~# 'v\|V'
while match(s:last_char(), "\\d") == 0 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() let s:char .= s:get_char()
endwhile endwhile
endif 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 Escape and go back to Normal mode, and still be able to operate on all the
cursors. 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) *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}` 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>', \ '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_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 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 = let g:multi_cursor_normal_maps =
\ get(g:, 'multi_cursor_normal_maps', s:default_normal_maps) \ get(g:, 'multi_cursor_normal_maps', s:default_normal_maps)
let g:multi_cursor_visual_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 EOF
end 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 end
describe "Multiple Cursors when normal_maps is empty" do describe "Multiple Cursors when normal_maps is empty" do
@ -190,7 +263,53 @@ describe "Multiple Cursors when visual_maps is empty" do
end 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(:filename) { 'test.txt' }
let(:options) { ['set autoindent'] } 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]` `!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 endsnippet
##########
# Misc #
##########
snippet uuid "Random UUID" w
`!p if not snip.c: import uuid; snip.rv = uuid.uuid4()`
endsnippet
# vim:ft=snippets: # 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 endsnippet
snippet rfki "Return From Keyword If" 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 endsnippet
snippet rk "Run Keyword" snippet rk "Run Keyword"
@ -54,7 +54,7 @@ Run Keyword And Return ${1:${kw}} ${2:${args}}
endsnippet endsnippet
snippet rkari "Run Keyword And Return If" 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 endsnippet
snippet rkars "Run Keyword And Return Status" snippet rkars "Run Keyword And Return Status"
@ -62,9 +62,12 @@ snippet rkars "Run Keyword And Return Status"
endsnippet endsnippet
snippet rki "Run Keyword If" snippet rki "Run Keyword If"
Run Keyword If ${1:${rc} < 0} ${2:${VISUAL:Some keyword returning a value}} Run Keyword If '\${${1:rc}}' != '${2:abc}'
... ELSE IF ${3:'${str}' == 'abc'} ${4:Another keyword} ... ${3:${VISUAL:Some keyword returning a value}}
... ELSE ${5:Final keyword} ... ELSE IF '\${${4:str}}' != '${5:def}'
... ${6:Another keyword}
... ELSE
... ${7:Final keyword}
endsnippet endsnippet
snippet rkiactf "Run Keyword If Any Critical Tests Failed" snippet rkiactf "Run Keyword If Any Critical Tests Failed"
@ -102,7 +105,7 @@ Run Keywords
endsnippet endsnippet
snippet rku "Run Keyword Unless" 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 endsnippet
snippet sgv "Set Global Variable" snippet sgv "Set Global Variable"
@ -130,7 +133,9 @@ snippet sv "Set Variable"
endsnippet endsnippet
snippet svi "Set Variable If" 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 endsnippet
snippet wuks "Wait Until Keyword Succeeds" snippet wuks "Wait Until Keyword Succeeds"

View file

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

View file

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

View file

@ -4,16 +4,16 @@
# PowerShell Class # PowerShell Class
snippet class snippet class
class { class {
[string] ${0:FirstName} [string] ${1:FirstName}
} }
# PowerShell Advanced Function # PowerShell Advanced Function
snippet function snippet function
function {0:name} { function ${1:name} {
[CmdletBinding()] [CmdletBinding()]
param ( param (
[Parameter(Mandatory = $true)] [Parameter(Mandatory = $true)]
[string] $Param1 [string] ${2:Param}
) )
begin { begin {
@ -29,30 +29,74 @@ snippet function
# PowerShell Splatting # PowerShell Splatting
snippet splatting snippet splatting
$Params = @{ $Params = @{
${0:Param1} = 'Value1' ${1:Param1} = '{2:Value1}'
${1:Param2} = 'Value2' ${3:Param2} = '{4:Value2}'
} }
${3:CommandName} ${5:CommandName} @Params
# PowerShell Enumeration # PowerShell Enumeration
snippet enum snippet enum
enum ${0:name} { enum ${1:name} {
${1:item1} ${2:item1}
${2:item2} ${3:item2}
} }
# PowerShell if..then # PowerShell if..then
snippet if snippet if
if (${0:condition}) { if (${1:condition}) {
${1:statement} ${2:statement}
}
# PowerShell if..else
snippet ife
if ( ${1:condition} ) {
${2}
}
else {
${3}
} }
# PowerShell While Loop # PowerShell While Loop
snippet while snippet while
while (${0:condition}) { while (${1:condition}) {
${1:statement} ${2:statement}
} }
# PowerShell Filter..Sort # PowerShell Filter..Sort
snippet filtersort 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 # 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 # 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 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. # @example Simple use
# # class { '$1': }
# [*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 Use with params
# class { '$1': # 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 class $1(
# $$4 = undef,
# Copyright `strftime("%Y")` `g:snips_author` ) {
# ${0}
class $1 (${3}){
${4}
} }
snippet defheader 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 # @param ${4:parameter1} [${5:String}]
# # ${6:Explanation of what this parameter affects.}
# 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:
# #
# @example Simple use
# $1 { 'namevar': # $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 define $1(
# $$4 = undef,
# Copyright `strftime("%Y")` `g:snips_author` ) {
# ${0}
define $1(${3}) {
${4}
} }
# Language Constructs # Language Constructs

View file

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

View file

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

View file

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

View file

@ -146,24 +146,13 @@ let g:go_fmt_command = "goimports"
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" => Syntastic (syntax checker) " => Syntastic (syntax checker)
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Python let g:ale_linters = {
let g:syntastic_python_checkers=['pyflakes'] \ 'javascript': ['jshint'],
\ 'python': ['flake8'],
\ 'go': ['go', 'golint', 'errcheck']
\}
" Javascript nmap <silent> <leader>a <Plug>(ale_next_wrap)
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>
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""