├── .gitignore
├── Makefile
├── README.md
├── autoload
├── changes.vim
└── changes_icons
│ ├── add.bmp
│ ├── add1.bmp
│ ├── delete.bmp
│ ├── delete1.bmp
│ ├── license_designkode.txt
│ ├── warning.bmp
│ └── warning1.bmp
├── changes.zip
├── doc
└── ChangesPlugin.txt
├── plugin
└── changesPlugin.vim
├── screencast.gif
└── todo.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | post.pl
2 | vim_passfile
3 | .*.un~
4 | .*.sw*
5 | # ignore vimballs
6 | *.vba
7 | *.vmb
8 | # ignore *.orig files
9 | *.orig
10 | # ignore some zip files
11 | *-*.zip
12 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | SCRIPT=$(wildcard plugin/*.vim)
2 | AUTOL =$(wildcard autoload/*.vim)
3 | DOC=$(wildcard doc/*.txt)
4 | PLUGIN=$(shell basename "$$PWD")
5 | VERSION=$(shell sed -n '/Version:/{s/^.*\(\S\.\S\+\)$$/\1/;p}' $(SCRIPT))
6 |
7 | .PHONY: $(PLUGIN).vmb
8 |
9 | all: uninstall vimball install
10 |
11 | vimball: $(PLUGIN).vmb
12 |
13 | zip: $(PLUGIN).zip
14 |
15 | release: uninstall version zip
16 |
17 | clean:
18 | rm -rf *.vmb *.vba */*.orig *.~* .VimballRecord doc/tags
19 | find . -type f \( -name "*.vba" -o -name "*.orig" -o -name "*.~*" \
20 | -o -name ".VimballRecord" -o -name ".*.un~" -o -name "*.sw*" -o \
21 | -name tags -o -name "*.vmb" \) -delete
22 |
23 | dist-clean: clean
24 |
25 | install:
26 | vim -N -i NONE -u NONE -c 'ru! plugin/vimballPlugin.vim' -c':so %' -c':q!' $(PLUGIN)-$(VERSION).vmb
27 |
28 | uninstall:
29 | vim -N -i NONE -u NONE -c 'ru! plugin/vimballPlugin.vim' -c':RmVimball' -c':q!' $(PLUGIN)-$(VERSION).vmb
30 |
31 | undo:
32 | for i in */*.orig; do mv -f "$$i" "$${i%.*}"; done
33 |
34 | archive: $(PLUGIN).zip
35 |
36 | $(PLUGIN).zip:
37 | -@/bin/sh -c "if [ -f $(PLUGIN).zip ]; then \
38 | zip -u --verbose $(PLUGIN).zip ${SCRIPT} ${AUTOL} ${DOC} autoload/*/* ; \
39 | else \
40 | zip $(PLUGIN).zip ${SCRIPT} ${AUTOL} ${DOC} autoload/*/* ; \
41 | fi "
42 | ln -f $(PLUGIN).zip $(PLUGIN)-$(VERSION).zip
43 |
44 | $(PLUGIN).vmb:
45 | rm -f $(PLUGIN)-$(VERSION).vmb
46 | vim -N -i NONE -u NONE -c 'ru! plugin/vimballPlugin.vim' -c ':call append("0", [ "$(SCRIPT)", "$(AUTOL)", "$(DOC)"])' -c '$$d' -c ":%MkVimball $(PLUGIN)-$(VERSION) ." -c':q!'
47 | ln -f $(PLUGIN)-$(VERSION).vmb $(PLUGIN).vmb
48 |
49 | release: version all
50 |
51 | version:
52 | perl -i.orig -pne 'if (/Version:/) {s/\.(\d*)/sprintf(".%d", 1+$$1)/e}' ${SCRIPT} ${AUTOL}
53 | perl -i -pne 'if (/GetLatestVimScripts:/) {s/(\d+)\s+:AutoInstall:/sprintf("%d :AutoInstall:", 1+$$1)/e}' ${SCRIPT} ${AUTOL}
54 | #perl -i -pne 'if (/Last Change:/) {s/\d+\.\d+\.\d\+$$/sprintf("%s", `date -R`)/e}' ${SCRIPT}
55 | perl -i -pne 'if (/Last Change:/) {s/(:\s+).*\n/sprintf(": %s", `date -R`)/e}' ${SCRIPT} ${AUTOL}
56 | perl -i.orig -pne 'if (/Version:/) {s/\.(\d+).*\n/sprintf(".%d %s", 1+$$1, `date -R`)/e}' ${DOC}
57 | VERSION=$(shell sed -n '/Version:/{s/^.*\(\S\.\S\+\)$$/\1/;p}' $(SCRIPT))
58 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Changes plugin [](https://saythanks.io/to/cb%40256bit.org)
2 | > A Vim plugin for displaying changes in a buffer
3 |
4 | This plugin was written to help visualize which lines have been changed since
5 | editing started for a file. The plugin was inspired by so called changed-bars,
6 | available in other editors, such as Embarcadero C++ Builder (there it is
7 | called [Change Bars](http://edn.embarcadero.com/article/33453#6PersonalDeveloperProductivity)
8 | or Visual Studio where it is called [indicator margin](http://blog.eveningcreek.com/?p=151).
9 |
10 | ChangesPlugin.vim uses the diff feature of vim and compares the actual
11 | buffer with its saved state (or possibly against a state in a VCS repository).
12 | It displays signs in the signcolumn to indicate any changes, with optional line highlighting.
13 |
14 | See also the following screencast showing several of the features available:
15 | 
16 |
17 | Note, that a '-' indicates that at least one line was deleted between that
18 | particular line and the following line.
19 |
20 | Features:
21 | * Shows signs for added, modified, and deleted lines
22 | * Quick jumping between changed blocks ("hunks")
23 | * Tries to update signs as fast as possible
24 | * Optional line highlighting
25 | * Customisable
26 | * Many different Commands (fold all non changed lines, show changed lines in Quickfix window, opens a diff split...)
27 | * Preserves signs from other plugins
28 | * Built-in integration with [vim-airline](https://github.com/vim-airline/vim-airline/)
29 | * Good documentation
30 | * Quick response
31 | * Nice icons for gvim
32 | * Uses Vim 8's async feature for parsing the diff
33 |
34 | #### Why another git-gutter/vim-signify clone?
35 | To be fair, there were a lot of other vim plugins that offered the same functionality as those two. They just didn't get the same attention as those two. ChangesPlugin was one of those plugins.
36 |
37 | ### Installation
38 | Vim 8 comes with package support. Simply clone it into your packpath like this:
39 | ```
40 | cd ~/.vim/pack/custom/start/
41 | git clone https://github.com/chrisbra/changesPlugin.git
42 | ```
43 | (Remember to run `:helptags ALL` after restarting Vim to regenerate the help files)
44 |
45 | Other installation methods:
46 |
47 | | Plugin Manager | Install with... |
48 | | ------------- | ------------- |
49 | | [Pathogen][1] | `git clone https://github.com/chrisbra/changesPlugin ~/.vim/bundle/changesPlugin`
Remember to run `:Helptags` to generate help tags |
50 | | [NeoBundle][2] | `NeoBundle 'chrisbra/changesPlugin'` |
51 | | [Vundle][3] | `Plugin 'chrisbra/changesPlugin'` |
52 | | [Plug][4] | `Plug 'chrisbra/changesPlugin'` |
53 | | [VAM][5] | `call vam#ActivateAddons([ 'changesPlugin' ])` |
54 | | [Dein][6] | `call dein#add('chrisbra/changesPlugin')` |
55 | | [minpac][7] | `call minpac#add('chrisbra/changesPlugin')` |
56 | | manual | copy all of the directories into your `~/.vim` directory (preserve existing directories)|
57 |
58 | Other package managers work similarly, please refer to their documentation.
59 |
60 | ### Usage
61 | Once installed, take a look at the help at `:h ChangesPlugin`
62 |
63 | Here is a short overview of the functionality provided by the plugin:
64 | #### Ex commands:
65 | :EC - Activate the plugin (display indicators of changes for the current buffer)
66 | :DC - Disable the plugin
67 | :TCV - Toggle the plugin
68 | :CC - Show a small help window
69 | :CL - Open the Quickfix window with all changes for the current buffer
70 | :CD - Open a diff view for the current buffer
71 | :CF - Fold away all non-changed lines
72 | :CT - Toggle how the highlighting is displayed
73 | #### Mappings
74 | ]h - Moves forward to the next changed line
75 | [h - Moves backwards to the previous changed line
76 | ah - Selects the current hunk (TextObject)
77 | h - Stage the hunk that the cursor is on (works only for git)
78 |
79 | ### Configuration
80 |
81 | `g:changes_autocmd` (default: 1) - update the signs automatically using InsertLeave and TextChanged autocommands.
82 |
83 | `g:changes_vcs_check` (default: 0)
84 |
85 | `g:changes_vcs_system` (default: '') - Check against a version in a repository (e.g. git/mercurial) and specify VCS to use (if not specified, will try to autodetect).
86 |
87 | `g:changes_diff_preview` (default: 0) - Display diff in the preview window
88 |
89 | `g:changes_respect_SignColumn` (default 0) - If set, will use the SignColumn Highlighting group, else uses the Normal Highlighting group
90 |
91 | `g:changes_sign_text_utf8` (default 1) - If set, will display nice little utf-8 signs.
92 |
93 | `g:changes_linehi_diff` (default: 0) - If set, will overlay the text with highlighting for the difference in the line.
94 |
95 | `g:changes_use_icons` (default: 1) - If set, will display graphical icons if support is available.
96 |
97 | `g:changes_add_sign` (default: '+') - If set, will display custom sign
98 |
99 | `g:changes_delete_sign` (default: '-') - If set, will display custom sign
100 |
101 | `g:changes_modified_sign` (default: '*') - If set, will display custom sign
102 |
103 | `g:changes_utf8_add_sign` (default '➕') - If set, will display nice little utf-8 plus signs.
104 |
105 | `g:changes_utf8_delete_sign` (default '➖') - If set, will display nice little utf-8 minus signs.
106 |
107 | `g:changes_utf8_modifed_sign` (default '★') - If set, will display nice little utf-8 star signs.
108 |
109 | #### Similar Work
110 | [vim-gitgutter](https://github.com/airblade/vim-gitgutter)
111 | Only works for git.
112 |
113 | [vim-signify](https://github.com/mhinz/vim-signify/)
114 | Supports several VCS, only updates the sign on write.
115 |
116 | ### License & Copyright
117 |
118 | © 2009-2014 by Christian Brabandt. The Vim License applies. See `:h license`
119 |
120 | __NO WARRANTY, EXPRESS OR IMPLIED. USE AT-YOUR-OWN-RISK__
121 |
122 | [1]: https://github.com/tpope/vim-pathogen
123 | [2]: https://github.com/Shougo/neobundle.vim
124 | [3]: https://github.com/VundleVim/Vundle.vim
125 | [4]: https://github.com/junegunn/vim-plug
126 | [5]: https://github.com/MarcWeber/vim-addon-manager
127 | [6]: https://github.com/Shougo/dein.vim
128 | [7]: https://github.com/k-takata/minpac/
129 |
--------------------------------------------------------------------------------
/autoload/changes.vim:
--------------------------------------------------------------------------------
1 | " Changes.vim - Using Signs for indicating changed lines
2 | " ------------------------------------------------------
3 | " Version: 0.16
4 | " Author: Christian Brabandt
5 | " Last Change: Thu, 15 Jan 2015 21:16:40 +0100
6 | " License: Vim License
7 | " Documentation: see :help changesPlugin.txt
8 | " GetLatestVimScripts: 3052 15 :AutoInstall: ChangesPlugin.vim
9 | " Documentation: "{{{1
10 | " TODO: Remove old in-efficient sign handling, once 8.2 or so is stable
11 | " See :h ChangesPlugin.txt
12 |
13 | "{{{1script-level variables
14 | scriptencoding utf-8
15 | let s:i_path = fnamemodify(expand(""), ':p:h'). '/changes_icons/'
16 | let s:sign_api = v:version > 801 || (v:version == 801 && has("patch614"))
17 | let s:sign_api_list = exists("*sign_placelist")
18 | let s:sign_api_group = 'ChangesSignGroup'
19 |
20 | fu! GetSID() "{{{1
21 | return matchstr(expand(''), '\zs\d\+\ze_GetSID$')
22 | endfu
23 |
24 | let s:sid = GetSID()
25 | delf GetSID "not needed anymore
26 |
27 | " Check preconditions
28 | fu! s:Check() "{{{1
29 | if !exists("s:msg")
30 | let s:msg=[]
31 | endif
32 | if !has("diff")
33 | call s:StoreMessage("Diff support not available in your Vim version.")
34 | throw 'changes:abort'
35 | endif
36 |
37 | if !has("signs")
38 | call s:StoreMessage("Sign Support support not available in your Vim.")
39 | throw 'changes:abort'
40 | endif
41 |
42 | if !executable("diff") || executable("diff") == -1
43 | call s:StoreMessage("No diff executable found")
44 | throw 'changes:abort'
45 | endif
46 |
47 | let s:ids={}
48 | let s:ids["add"] = hlID("DiffAdd")
49 | let s:ids["del"] = hlID("DiffDelete")
50 | let s:ids["cha"] = hlID("DiffChange")
51 | let s:ids["ch2"] = hlID("DiffText")
52 |
53 | call s:SetupSignTextHl()
54 | call s:DefineSigns(0)
55 | endfu
56 | fu! s:GetSignDef(def) "{{{1
57 | " Returns sign definition as string for use as `:sign command`
58 | " not used when s:sign_api is defined
59 | return (has_key(a:def, 'text') ? ' text='.get(a:def, 'text', '') : '').
60 | \ (has_key(a:def, 'texthl') ? ' texthl='.get(a:def, 'texthl', '') : '').
61 | \ (has_key(a:def, 'icon') ? ' icon='.get(a:def, 'icon', '') : '').
62 | \ (has_key(a:def, 'linehl') ? ' linehl='.get(a:def, 'linehl', '') : '')
63 | endfu
64 | fu! s:DefineSigns(undef) "{{{1
65 | if s:sign_api_list
66 | if a:undef
67 | call sign_undefine(keys(s:signs))
68 | endif
69 | call sign_define(values(s:signs))
70 | return
71 | endif
72 | for key in keys(s:signs)
73 | if a:undef
74 | try
75 | " Try undefining first, so that refining will actually work!
76 | if s:sign_api
77 | call sign_undefine(s:signs[key].name)
78 | else
79 | exe "sil! sign undefine " key
80 | endif
81 | catch /^Vim\%((\a\+)\)\=:E155/ " sign does not exist
82 | endtry
83 | endif
84 | try
85 | if s:sign_api
86 | call sign_define(key, s:signs[key])
87 | else
88 | exe "sil! sign define" key. s:GetSignDef(s:signs[key])
89 | endif
90 | catch /^Vim\%((\a\+)\)\=:E255/ " Can't read icons
91 | if s:sign_api
92 | call sign_undefine(key)
93 | unlet! s:signs[key]['icon']
94 | call sign_define(key, s:signs[key])
95 | else
96 | exe "sil! sign undefine " key
97 | unlet! s:signs[key]['icon']
98 | exe "sign define" key s:GetSignDef(s:signs[key])
99 | endif
100 | endtry
101 | endfor
102 | endfu
103 | fu! s:CheckLines(arg) "{{{1
104 | " OLD: not needed any more.
105 | " a:arg 1: check original buffer
106 | " 0: check diffed scratch buffer
107 | let line=1
108 | while line <= line('$')
109 | let id=diff_hlID(line,1)
110 | if (id == 0)
111 | let line+=1
112 | continue
113 | " in the original buffer, there won't be any lines accessible
114 | " that have been 'marked' deleted, so we need to check scratch
115 | " buffer for added lines
116 | elseif (id == s:ids['add']) && !a:arg
117 | let s:temp['del'] = s:temp['del'] + [ line ]
118 | elseif (id == s:ids['add']) && a:arg
119 | let b:diffhl['add'] = b:diffhl['add'] + [ line ]
120 | elseif ((id == s:ids['cha']) || (id == s:ids['ch2'])) && a:arg
121 | let b:diffhl['cha'] = b:diffhl['cha'] + [ line ]
122 | endif
123 | let line+=1
124 | endw
125 | endfu
126 | fu! s:UpdateView(...) "{{{1
127 | " if a:1 is given, force update!
128 | let force = a:0
129 | let did_source_init = 0
130 | let b:changes_chg_tick = get(b:, 'changes_chg_tick', 0)
131 | try
132 | call changes#Init()
133 | catch
134 | call changes#CleanUp()
135 | return
136 | endtry
137 | let did_source_init = 1
138 | if !s:IsUpdateAllowed(1)
139 | return
140 | endif
141 | let b:changes_last_line = get(b:, 'changes_last_line', line('$'))
142 | if get(g:, 'gitgutter_enabled', 0) &&
143 | \ exists('b:gitgutter_gitgutter_signs')
144 | " Gitgutter plugin is available, stop here
145 | call changes#IgnoreCurrentBuffer()
146 | let force = 0
147 | endif
148 | " Only update, if there have been changes to the buffer
149 | if exists("b:diffhl") &&
150 | \ get(g:, 'changes_fast', 1) &&
151 | \ line("'[") == line("']") &&
152 | \ !empty(b:diffhl) &&
153 | \ index(b:diffhl['add'] + b:diffhl['cha'] + b:diffhl['del'], line("'[")) > -1 &&
154 | \ b:changes_last_line == line('$')
155 | " there already is a sign on the current line, so
156 | " skip an expensive call to create diff (might happen with many
157 | " rx commands on the same line and triggered TextChanged autocomands)
158 | " and should make Vim more responsive (at the cost of being a little
159 | " bit more unprecise.)
160 | " If you don't like this, set the g:changes_fast variable to zero
161 | let b:changes_chg_tick = b:changedtick
162 | endif
163 |
164 | if b:changes_chg_tick != b:changedtick || force
165 | try
166 | if !did_source_init
167 | call changes#Init()
168 | endif
169 | call s:GetDiff(1, '')
170 | call s:HighlightTextChanges()
171 | let b:changes_chg_tick = b:changedtick
172 | let b:changes_last_line = line('$')
173 | catch
174 | call s:StoreMessage(v:exception)
175 | " Make sure, the message is actually displayed!
176 | verbose call changes#WarningMsg()
177 | call changes#CleanUp()
178 | endtry
179 | else
180 | " if nothing has been added, remove the sign, that has been added
181 | " using the InsertEnter autocommand
182 | if exists("s:changes_last_inserted_sign")
183 | let name=s:PrevDictHasKey(line('.'))
184 | if get(name, 'name', '') ==# s:changes_last_inserted_sign.name
185 | call s:UnPlaceSpecificSigns([{'id': get(name, 'id', s:SignIdRemove())}])
186 | endif
187 | endif
188 | endif
189 | endfu
190 | fu! s:SetupSignTextHl() "{{{1
191 | if &bg == 'dark'
192 | hi default ChangesSignTextAdd ctermbg=NONE ctermfg=darkgreen guibg=NONE guifg=darkgreen
193 | hi default ChangesSignTextDel ctermbg=NONE ctermfg=darkred guibg=NONE guifg=darkred
194 | hi default ChangesSignTextCh ctermbg=NONE ctermfg=lightblue guibg=NONE guifg=lightblue
195 | else
196 | hi default ChangesSignTextAdd ctermbg=NONE ctermfg=green guibg=NONE guifg=green
197 | hi default ChangesSignTextDel ctermbg=NONE ctermfg=red guibg=NONE guifg=red
198 | hi default ChangesSignTextCh ctermbg=NONE ctermfg=darkblue guibg=NONE guifg=darkblue
199 | endif
200 | hi default link ChangesSignTextDummyCh ChangesSignTextCh
201 | hi default link ChangesSignTextDummyAdd ChangesSignTextAdd
202 | endfu
203 | fu! s:ChangesSignsLines() "{{{1
204 | " returns a list of all changes placed signs
205 | return sort(map(copy(s:placed_signs[0]), 'get(v:val, "line")+0'), 'N')
206 | endfu
207 | fu! s:PrevDictHasKey(line) "{{{1
208 | " Return existing sign on the current line
209 | if s:sign_api
210 | let result=sign_getplaced(bufnr(''), {'group': s:sign_api_group, 'lnum': a:line})[0]['signs']
211 | return len(result) ? result[0] : {}
212 | endif
213 | let result=filter(copy(s:placed_signs[0]), 'v:val.line==?a:line')
214 | return result == [] ? {} : result[0]
215 | endfu
216 | fu! s:PrevDictHasKeyType(line, id) "{{{1
217 | " Return existing sign on the current line
218 | " If a:1, return complete dict item
219 | let result=filter(copy(s:placed_signs[0]), 'v:val.line ==? a:line && v:val.name[0:2] ==? a:id')
220 | return result == [] ? '' : result[0].name
221 | endfu
222 | fu! s:Write(name) "{{{1
223 | " turn off fsync, so writing is faster
224 | let _fsync=&fsync
225 | set nofsync
226 | exe ":sil keepalt noa :w! ++ff=unix" a:name
227 | let &fsync=_fsync
228 | endfu
229 | fu! s:PlaceSigns(dict) "{{{1
230 | " signs by other plugins
231 | let b = copy(s:placed_signs[1])
232 | let changes_signs_lines=s:ChangesSignsLines()
233 | for id in ['add', 'cha', 'del']
234 | let prev_line = -1
235 | if !has_key(a:dict, id)
236 | " safety check
237 | break
238 | endif
239 | for item in a:dict[id]
240 | " One special case could occur:
241 | " You could delete the last lines. In that case, we couldn't place
242 | " here the deletion marks. If this happens, place the deletion
243 | " marks on the last line
244 | if item > line('$')
245 | let item=line('$')
246 | endif
247 | " There already exists a sign in this line, skip now
248 | if index(b, item) > -1
249 | continue
250 | endif
251 | let name=id
252 | " check if there is already a changes sign on the current line
253 | let sign_exists = index(changes_signs_lines, item) > -1
254 | let sign_exists_prev = index(changes_signs_lines, (item-1)) > -1
255 | " Make sure, 'dummych' ==? 'ch'
256 | " or 'dummydel' ==? 'del'
257 | if prev_line+1 == item || (sign_exists_prev && s:SignType(s:PrevDictHasKeyType(item-1, id)) ==? id)
258 | if id=='del'
259 | " don't need to place more deleted signs on those lines,
260 | " skip
261 | let prev_line = item
262 | continue
263 | else
264 | let name=id.'_dummy'
265 | endif
266 | endif
267 | if sign_exists && get(s:PrevDictHasKey(item), 'name', '') ==? name
268 | " There is already a Changes sign placed
269 | continue
270 | endif
271 | let sid=b:sign_prefix.s:SignId()
272 | call s:PlaceSpecificSign(sid, item, name, bufnr(''))
273 | " remember line number, so that we don't place a second sign
274 | " there!
275 | call add(s:placed_signs[0], {'id': sid, 'line':item, 'type': name})
276 | let prev_line = item
277 | endfor
278 | endfor
279 | endfu
280 | fu! s:UnPlaceSigns(force) "{{{1
281 | if !exists("b:sign_prefix")
282 | return
283 | endif
284 | if a:force
285 | " only changes sign present, can remove all of them now
286 | if s:sign_api
287 | call sign_unplace(s:sign_api_group, {'buffer': bufnr('')})
288 | else
289 | exe "sign unplace * buffer=".bufnr('')
290 | endif
291 | return
292 | endif
293 | if !exists("b:diffhl")
294 | return
295 | endif
296 | let s:placed_signs = s:PlacedSigns()
297 | call s:UnPlaceSpecificSigns(s:placed_signs[0])
298 | endfu
299 | fu! s:StoreMessage(msg) "{{{1
300 | call add(s:msg, a:msg)
301 | endfu
302 | fu! s:PreviewDiff(file) "{{{1
303 | try
304 | if !get(g:, 'changes_diff_preview', 0) || &diff
305 | return
306 | endif
307 | if getfsize(a:file) <= 0
308 | " getfsize() returns -1 on error (e.g. file does not exists!)
309 | if bufwinnr(a:file) > 0
310 | sil! pclose
311 | endif
312 | return
313 | endif
314 | let cur = exists("b:current_line") ? b:current_line : 0
315 | let _ar=&g:ar
316 | " Avoid W11 message (:h W11)
317 | set ar
318 | try
319 | exe printf(':noa keepp noswap sil pedit +/@@\ -%d.*\\n\\zs %s', cur, a:file)
320 | let &g:ar=_ar
321 | noa wincmd P
322 | " execute commands in preview window
323 | setl nofoldenable syntax=diff bt=nofile ft=diff
324 | sil keepp noa g/^[+-]\{3\}/d_
325 | catch
326 | finally
327 | " Be sure, to stay in the original window
328 | noa wincmd p
329 | endtry
330 | if get(g:, 'neocomplcache_enable_auto_close_preview', 0)
331 | " Neocomplache closes preview window, GRR!
332 | " don't close preview window
333 | let g:neocomplcache_enable_auto_close_preview = 0
334 | endif
335 | catch
336 | endtry
337 | endfu
338 | fu! s:ChangeDir() "{{{1
339 | let _pwd = fnameescape(getcwd())
340 | exe "lcd " fnameescape(fnamemodify(expand("%"), ':h'))
341 | return _pwd
342 | endfu
343 | fu! s:Output(list) "{{{1
344 | let eol="\n"
345 | if &ff ==? 'dos'
346 | let eol = "\r\n"
347 | elseif &ff ==? 'mac'
348 | let eol = "\n\r"
349 | endif
350 | return join(a:list, eol).eol
351 | endfu
352 | fu! s:DeleteTempFiles() "{{{1
353 | for file in glob(s:diff_out.'*', 1, 1)
354 | call delete(file)
355 | endfor
356 | endfu
357 | fu! s:MakeDiff_new(file, type) "{{{1
358 | " Parse Diff output and place signs
359 | " Needs unified diff output
360 | let outfile = printf("%s.%d", s:diff_out, s:jobid)
361 | try
362 | let _pwd = s:ChangeDir()
363 | unlet! b:current_line
364 | call s:Write(s:diff_in_cur) " writes in Unix format
365 | if !s:vcs || !empty(a:file)
366 | let file = !empty(a:file) ? a:file : bufname('')
367 | if empty(file)
368 | throw "changes:abort"
369 | endif
370 | let cp = (!s:Is('unix') ? 'copy ' : 'cp ')
371 | let output = system(cp. shellescape(file). " ". s:diff_in_old)
372 | if v:shell_error
373 | call s:StoreMessage(output[:-2])
374 | throw "changes:abort"
375 | endif
376 | else
377 | if b:vcs_type == 'git'
378 | let git_rep_p = s:ReturnGitRepPath()
379 | if !empty(git_rep_p)
380 | exe 'lcd' git_rep_p
381 | endif
382 | elseif b:vcs_type == 'cvs'
383 | " I am not sure, if this is the best way
384 | " to query CVS. But just to make sure,
385 | " we are in the right path and we don't have
386 | " to consider CVSROOT
387 | exe 'lcd' fnamemodify(expand('%'), ':p:h')
388 | endif
389 | let cmd = printf("%s %s%s > %s", (b:vcs_type==?'rcs'?'':
390 | \ b:vcs_type), s:vcs_cat[b:vcs_type],
391 | \ shellescape(fnamemodify(resolve(expand('%')), ':.')),
392 | \ s:diff_in_old)
393 | let output = system(cmd)
394 | if v:shell_error
395 | call s:StoreMessage(output[:-2])
396 | throw "changes:abort"
397 | endif
398 | " if &ff ==? 'dos'
399 | " " need to postprocess the resulting file
400 | " call system('vim --clean -c ":w! ++ff=dos" -c ":q!" ' . s:diff_in_old)
401 | " endif
402 | endif
403 | let cmd = printf("diff -a -U0 -N %s %s %s > %s",
404 | \ s:diff_in_old, s:diff_in_cur,
405 | \ (get(g:, 'changes_grep_diff', 0) ? '|fgrep "@@"' : ''),
406 | \ outfile)
407 | if s:Is('win') && &shell =~? 'cmd.exe$'
408 | let cmd = '( '. cmd. ' )'
409 | endif
410 | if has('job')
411 | call s:ChangesDoAsync(cmd, fnamemodify(bufname(''), ':p'), a:type, outfile)
412 | else
413 | let output = system(cmd)
414 | if v:shell_error >= 2 || v:shell_error < 0
415 | " diff returns 2 on errors
416 | call s:StoreMessage(output[:-2])
417 | throw "changes:abort"
418 | endif
419 | if getfsize(outfile) <= 0
420 | call s:StoreMessage("File not found or no differences found!")
421 | return
422 | endif
423 | call s:ParseDiffOutput(outfile)
424 | endif
425 | finally
426 | if filereadable(outfile)
427 | call s:PreviewDiff(outfile)
428 | endif
429 | exe 'lcd' _pwd
430 | endtry
431 | endfu
432 | fu! s:MakeDiff(...) "{{{1
433 | " Old version, only needed, when GetDiff(3) is called (or argument 1 is non-empty)
434 | " Get diff for current buffer with original
435 | let o_pwd = s:ChangeDir()
436 | let bnr = bufnr('%')
437 | let ft = &l:ft
438 | noa vert new
439 | set bt=nofile
440 | let scratchbuf = bufnr('')
441 | if !s:vcs
442 | exe ":silent :noa :r " (exists("a:1") && !empty(a:1) ? a:1 : '#')
443 | let &l:ft=ft
444 | else
445 | let vcs=getbufvar(bnr, 'vcs_type')
446 | try
447 | if vcs == 'git'
448 | let git_rep_p = s:ReturnGitRepPath()
449 | if !empty(git_rep_p)
450 | exe 'lcd' git_rep_p
451 | endif
452 | elseif vcs == 'cvs'
453 | " I am not sure, if this is the best way
454 | " to query CVS. But just to make sure,
455 | " we are in the right path and we don't have
456 | " to consider CVSROOT
457 | exe 'lcd' fnamemodify(expand('#'), ':p:h')
458 | endif
459 | let output = system((vcs==?'rcs'?'':vcs) . ' '. s:vcs_cat[vcs] . expand("#") . '>'. s:diff_out)
460 | if v:shell_error
461 | call s:StoreMessage(output[:-2])
462 | throw "changes:abort"
463 | endif
464 | let fsize=getfsize(s:diff_out)
465 | if fsize == 0
466 | call s:StoreMessage("Couldn't get VCS output, aborting")
467 | "call s:MoveToPrevWindow()
468 | exe "noa" bufwinnr(bnr) "wincmd w"
469 | throw "changes:abort"
470 | endif
471 | exe ':silent :noa :r' s:diff_out
472 | catch /^changes: No git Repository found/
473 | call s:StoreMessage("Unable to find git Top level repository.")
474 | echo v:errmsg
475 | exe "noa" bufwinnr(bnr) "wincmd w"
476 | throw "changes:abort"
477 | catch
478 | if bufnr('%') != bnr
479 | "call s:MoveToPrevWindow()
480 | exe "noa" bufwinnr(bnr) "wincmd w"
481 | endif
482 | throw "changes:abort"
483 | finally
484 | endtry
485 | endif
486 | 0d_
487 | diffthis
488 | exe "noa" bufwinnr(bnr) "wincmd w"
489 | diffthis
490 | exe "lcd " o_pwd
491 | return scratchbuf
492 | endfu
493 | fu! s:ParseDiffOutput(file) "{{{1
494 | let b:current_line = 1000000
495 | let file=readfile(a:file)
496 | if !get(g:, 'changes_grep_diff', 0)
497 | let file=filter(file, 'v:val=~''^@@''')
498 | endif
499 | for line in file
500 | " TODO: matchlist seems slow, can this be made faster by manually parsing?
501 | let submatch = matchlist(line, '@@ -\(\d\+\),\?\(\d*\) +\(\d\+\),\?\(\d*\) @@')
502 | if empty(submatch)
503 | " There was probably an error, skip parsing now
504 | return
505 | else
506 | let old_line = submatch[1] + 0
507 | let old_count = (empty(submatch[2]) ? 1 : submatch[2] + 0)
508 | let new_line = submatch[3] + 0
509 | let new_count = (empty(submatch[4]) ? 1 : submatch[4] + 0)
510 | if b:current_line > (old_line - line('.'))
511 | let b:current_line = old_line
512 | endif
513 | endif
514 |
515 | " 2 Lines added
516 | " @@ -4,0 +4,2 @@
517 | if old_count == 0 && new_count > 0
518 | let b:diffhl.add += range(new_line, new_line + new_count - 1)
519 |
520 | " 2 lines deleted:
521 | " @@ -4,2 +3,0 @@
522 | elseif old_count > 0 && new_count == 0
523 | if new_line == 0
524 | let new_line = 1
525 | endif
526 | let b:diffhl.del += range(new_line, new_line + old_count - 1)
527 |
528 | " Lines changed
529 | " 2 deleted, 2 changed
530 | " @@ -3,4 +3,2 @@
531 | "
532 | " But this is just a Change:
533 | " @@ -1010,5 +1020,5 @@
534 | elseif old_count >= new_count
535 | let b:diffhl.cha += range(new_line, new_line + new_count - 1)
536 | " if old and new count is not given, can't be deletion
537 | " I think this is wrong:
538 | " if new_line + new_count < old_line+old_count && !(empty(submatch[2]) && empty(submatch[4]))
539 | " should probably rather be:
540 | if new_line + new_count < old_line + old_count && new_count != old_count
541 | let b:diffhl.del+= range(new_count + new_line, old_line + old_count - 1)
542 | endif
543 |
544 | " Lines changed
545 | " 3 added, 2 changed
546 | " @@ -4,2 +4,5 @@
547 | else " old_count < new_count
548 | let b:diffhl.cha += range(new_line, new_line + old_count - 1)
549 | if new_line + old_count <= new_line+new_count-1
550 | let b:diffhl.add += range(new_line + old_count, new_line + new_count - 1)
551 | endif
552 | endif
553 | endfor
554 | endfu
555 | fu! s:ReturnGitRepPath() "{{{1
556 | " return the top level of the repository path. This is needed, so
557 | " git show will correctly return the file
558 | exe 'lcd' fnamemodify(resolve(expand('%')), ':h')
559 | let git_path = system('git rev-parse --git-dir')
560 | if !v:shell_error
561 | " we need the directory right above the .git metatdata directory
562 | return escape(git_path[:-2].'/..', ' ')
563 | else
564 | return ''
565 | endif
566 | endfu
567 | fu! s:ShowDifferentLines() "{{{1
568 | if !exists("b:diffhl")
569 | return
570 | else
571 | let list=[]
572 | let placed={}
573 | let tline = -1
574 | let types={'cha':'*', 'add': '+', 'del': '-'}
575 | for type in ['cha', 'del', 'add']
576 | for line in b:diffhl[type]
577 | if has_key(placed, line)
578 | continue
579 | endif
580 | if line == tline+1
581 | let tline=line
582 | continue
583 | endif
584 | call add(list, {'bufnr': bufnr(''),
585 | \ 'lnum': line, 'text': getline(line),
586 | \ 'type': types[type]})
587 | let placed.line=1
588 | let tline=line
589 | endfor
590 | endfor
591 | endif
592 | if !empty(list)
593 | call setloclist(winnr(), list)
594 | lopen
595 | endif
596 | endfun
597 | let g:Changes_funcref=funcref("ShowDifferentLines")
598 | fu! s:GetSignId() "{{{1
599 | " Not called for s:sign_api
600 | let signs = s:GetPlacedSigns()
601 | if empty(signs)
602 | " No signs placed yet...
603 | return 10
604 | endif
605 | let list=[]
606 | for val in signs[1:]
607 | " get 'id' value of signs
608 | let id = split(val, '=\d\+\zs')[1]
609 | call add(list, (split(id, '=')[1] + 0))
610 | endfor
611 | return max(list)
612 | endfu
613 | fu! s:GetPlacedSigns() "{{{1
614 | if s:sign_api
615 | return sign_getplaced(bufnr(''), {'group': s:sign_api_group})
616 | elseif exists("s:all_signs")
617 | return s:all_signs
618 | else
619 | " set local to english
620 | let lang=v:lang
621 | if lang isnot# 'C'
622 | sil lang mess C
623 | endif
624 | redir => a| exe "silent sign place buffer=".bufnr('')|redir end
625 | let s:all_signs = split(a,"\n")[1:]
626 | if lang != 'C'
627 | exe "sil lang mess" lang
628 | endif
629 | endif
630 | return s:all_signs
631 | endfu
632 | fu! s:PlacedSigns() "{{{1
633 | if empty(bufname(''))
634 | " empty(bufname): unnamed buffer, can't get diff of it,
635 | " anyhow, so stop expansive call here
636 | return [[],[]]
637 | endif
638 | let b = s:GetPlacedSigns()
639 | if s:sign_api
640 | return [[], b]
641 | endif
642 | if empty(b)
643 | return [[],[]]
644 | endif
645 | let dict = {}
646 | let own = []
647 | let other = []
648 | " Filter from the second item. The first one contains the buffer name:
649 | " Signs for [NULL]: or Signs for :
650 | let b=b[1:]
651 | let c=filter(copy(b), 'v:val =~ "id=".b:sign_prefix')
652 | for item in b
653 | if item =~ "id=".b:sign_prefix
654 | " Signs placed by this plugin
655 | let t = split(item)
656 | let dict.line = split(t[0], '=')[1]
657 | let dict.id = split(t[1], '=')[1]
658 | let dict.name = split(t[2], '=')[1]
659 | call add(own, copy(dict))
660 | else
661 | call add(other, matchstr(item, '^\s*\w\+=\zs\d\+\ze')+0)
662 | endif
663 | endfor
664 |
665 | " Make sure s:GetPlacedSigns() reruns correctly
666 | unlet! s:all_signs
667 | return [own, other]
668 | endfu
669 | fu! s:GuessVCSSystem() "{{{1
670 | " Check global config variable
671 | for var in [ b:, g:]
672 | let pat='\c\vgit|hg|bzr\|cvs|svn|subversion|mercurial|rcs|fossil|darcs'
673 | let vcs=matchstr(get(var, 'changes_vcs_system', ''), pat)
674 | if vcs
675 | return vcs
676 | endif
677 | endfor
678 | let file = fnamemodify(resolve(expand("%")), ':p')
679 | let path = escape(fnamemodify(file, ':h'), ' ')
680 | " First try git and hg, they seem to be the most popular ones these days
681 | " also check that the file actually exists in the repository
682 | if !empty(finddir('.git',path.';')) &&
683 | \ !empty(system('git ls-tree -r HEAD --name-only '. file))
684 | return 'git'
685 | elseif !empty(finddir('.hg',path.';')) &&
686 | \ empty(system('hg status -ui '. file))
687 | return 'hg'
688 | elseif isdirectory(path . '/CVS')
689 | return 'cvs'
690 | elseif isdirectory(path . '/.svn')
691 | return 'svn'
692 | elseif !empty(finddir('.bzr',path.';'))
693 | return 'bzr'
694 | elseif !empty(findfile('_FOSSIL_', path.';'))
695 | return 'fossil'
696 | elseif !empty(finddir('_darcs', path.';'))
697 | return 'darcs'
698 | elseif !empty(finddir('RCS', path.';'))
699 | return 'rcs'
700 | else
701 | return ''
702 | endif
703 | endfu
704 | fu! s:Is(os) "{{{1
705 | if (a:os == "win")
706 | return has("win32") || has("win16") || has("win64")
707 | elseif (a:os == "mac")
708 | return has("mac") || has("macunix")
709 | elseif (a:os == "unix")
710 | return has("unix") || has("macunix")
711 | endif
712 | endfu
713 | fu! s:RemoveConsecutiveLines(fwd, list) "{{{1
714 | " only keep the start/end of a bunch of successive lines
715 | let temp = -1
716 | let lines = a:fwd ? a:list : reverse(a:list)
717 | for item in lines
718 | if (a:fwd && temp == item - 1) || (!a:fwd && temp == item + 1)
719 | call remove(lines, index(lines, item))
720 | endif
721 | let temp = item
722 | endfor
723 | return lines
724 | endfu
725 | fu! s:GetDiff(arg, file) "{{{1
726 | " a:arg == 1 Create signs
727 | " a:arg == 2 Show changed lines in locationlist
728 | " a:arg == 3 Stay in diff mode
729 | " a:file -> which file to diff against
730 |
731 | " If error happened, don't try to get a diff list
732 | try
733 | if changes#CurrentBufferIsIgnored() ||
734 | \ !empty(&l:bt) || line2byte(line('$')) == -1
735 | call s:StoreMessage('Buffer is ignored')
736 | return
737 | endif
738 |
739 | " Save some settings
740 | let _wsv = winsaveview()
741 | " Lazy redraw
742 | setl lz
743 | let scratchbuf = 0
744 |
745 | try
746 | if !filereadable(bufname(''))
747 | call s:StoreMessage("You've opened a new file so viewing changes ".
748 | \ "is disabled until the file is saved ")
749 | return
750 | endif
751 |
752 | " Does not make sense to check an empty buffer
753 | if empty(bufname(''))
754 | call s:StoreMessage("The buffer does not contain a name. Aborted!")
755 | " don't ignore buffer, it could get a name later...
756 | return
757 | endif
758 |
759 | " do not generate signs for special buffers
760 | if !empty(&buftype)
761 | call s:StoreMessage("Not generating diff for special buffer!")
762 | call changes#IgnoreCurrentBuffer()
763 | return
764 | endif
765 |
766 | let b:diffhl={'add': [], 'del': [], 'cha': []}
767 | if a:arg == 3
768 | let s:temp = {'del': []}
769 | let curbuf = bufnr('%')
770 | let _ft = &ft
771 | let scratchbuf = s:MakeDiff(a:file)
772 | call s:CheckLines(1)
773 | exe "noa" bufwinnr(scratchbuf) "wincmd w"
774 | exe "setl ft=". _ft
775 | call s:CheckLines(0)
776 | " Switch to other buffer and check for deleted lines
777 | exe "noa" bufwinnr(curbuf) "wincmd w"
778 | let b:diffhl['del'] = s:temp['del']
779 | else
780 | " parse diff output
781 | call s:MakeDiff_new(a:file, a:arg)
782 | endif
783 | if !has("job")
784 | call s:AfterDiff(bufnr(''))
785 | if a:arg != 3 || s:nodiff
786 | let b:changes_view_enabled=1
787 | endif
788 | if a:arg ==# 2
789 | call s:ShowDifferentLines()
790 | endif
791 | endif
792 | catch /^Vim\%((\a\+)\)\=:E139/ " catch error E139
793 | return
794 | catch /^changes/
795 | let b:changes_view_enabled=0
796 | call changes#IgnoreCurrentBuffer()
797 | catch
798 | call s:StoreMessage("Error occured: ".v:exception)
799 | call s:StoreMessage("Trace: ". v:throwpoint)
800 | finally
801 | if s:vcs && get(b:, "b:changes_view_enabled", 0)
802 | " only add info here, when 'verbose' > 1
803 | call s:StoreMessage("Check against ".
804 | \ fnamemodify(expand("%"),':t') . " from " . b:vcs_type)
805 | endif
806 | " redraw (there seems to be some junk left)
807 | " disabled, redraw is expensive
808 | "redr!
809 | endtry
810 | finally
811 | if exists("_wsv")
812 | call winrestview(_wsv)
813 | endif
814 | " Make sure, the message is actually displayed!
815 | call changes#WarningMsg()
816 | " restore change marks
817 | call s:SaveRestoreChangeMarks(0)
818 | endtry
819 | endfu
820 | fu! s:AfterDiffAPI(bufnr) "{{{1
821 | " Get all currently placed signs in the buffer
822 | let placed_signs = sign_getplaced(a:bufnr, {'group': s:sign_api_group})
823 | if len(placed_signs[0]['signs']) > 0
824 | " remove signs, that are no longer needed anymore
825 | let newly_signs = b:diffhl['add'] + b:diffhl['cha'] + b:diffhl['del']
826 | let to_remove = filter(placed_signs[0]['signs'], { idx, val -> index(newly_signs, val.lnum) == -1})
827 | call map(to_remove, {idx, val -> sign_unplace(s:sign_api_group, {'id': val.id})})
828 | endif
829 | for name in ['del', 'add', 'cha']
830 | " Del delete first, since those refer to the old line numbers
831 | let previous=-1
832 | let id = 0
833 | for line in b:diffhl[name]
834 | let actual_name = name
835 | let cur_sign = sign_getplaced(a:bufnr, {'group': s:sign_api_group, 'lnum':line})
836 | let length=len(cur_sign[0]['signs'])
837 | let cur_id = 0
838 | let cur_name = ''
839 | while length > 0
840 | " In case there are several signs on the current line,
841 | " remove all except for the last one
842 | let cur_id = cur_sign[0]['signs'][length-1]['id']
843 | let cur_name = cur_sign[0]['signs'][length-1]['name']
844 | if length > 1
845 | call sign_unplace(s:sign_api_group, {'id':cur_id})
846 | endif
847 | let length -= 1
848 | endwhile
849 | if line==previous+1
850 | " There was a sign on the previous line
851 | if index(['add', 'cha'], name) > -1
852 | let actual_name = name.'_dummy'
853 | " Only place sign, if the old existing sign is not of the
854 | " same type (name)
855 | if actual_name isnot# cur_name
856 | call s:PlaceSpecificSign(cur_id, line, actual_name, a:bufnr)
857 | endif
858 | else
859 | " nothing to do for deleted lines,
860 | " but remove previously placed lines
861 | if cur_id > 0
862 | call sign_unplace(s:sign_api_group, {'id': cur_id})
863 | endif
864 | let previous=line
865 | continue
866 | endif
867 | else
868 | " Only place sign, if the old existing sign is not of the same type (name)
869 | if actual_name isnot# cur_name
870 | call s:PlaceSpecificSign(cur_id, line, actual_name, a:bufnr)
871 | endif
872 | endif
873 | let previous=line
874 | endfor
875 | endfor
876 | endfu
877 | fu! s:AfterDiff(bufnr) "{{{1
878 | call s:SortDiffHl()
879 | if s:sign_api
880 | call s:AfterDiffAPI(a:bufnr)
881 | return
882 | endif
883 | " Check for empty dict of signs
884 | if !exists("b:diffhl") ||
885 | \ ((b:diffhl ==? {'add': [], 'del': [], 'cha': []})
886 | \ && empty(s:placed_signs[0]))
887 | " Make sure, diff and previous diff are different,
888 | " otherwise, we might forget to update the signs
889 | call s:StoreMessage('No differences found!')
890 | let s:nodiff=1
891 | else
892 | let s:diffhl = s:CheckInvalidSigns()
893 | " remove invalid signs
894 | " s:diffhl[0] - invalid signs, that need to be removed
895 | " s:diffhl[1] - valid signs, that need to be added
896 | " safety check
897 | if type(s:diffhl) == type([]) && len(s:diffhl) == 2 && type(s:diffhl[0]) == type([])
898 | call s:UnPlaceSpecificSigns(s:diffhl[0])
899 | call s:PlaceSigns(s:diffhl[1])
900 | endif
901 | endif
902 | endfu
903 | fu! s:SortDiffHl() "{{{1
904 | for i in ['add', 'cha', 'del']
905 | call sort(b:diffhl[i], 'N')
906 | call uniq(b:diffhl[i])
907 | endfor
908 | endfu
909 | fu! s:SignType(string) "{{{1
910 | " returns type but skips dummy type
911 | return a:string[0:2]
912 | "return matchstr(a:string, '\(dummy\)\?\zs.*$')
913 | endfu
914 | fu! s:CheckInvalidSigns() "{{{1
915 | " list[0]: signs to remove
916 | " list[1]: signs to add
917 | let list=[[],{'add': [], 'del': [], 'cha': []}]
918 | let ind=0
919 | let last={}
920 | try
921 | " 1) check, if there are signs to delete
922 | for item in s:placed_signs[0]
923 | if (item.name ==? '[Deleted]')
924 | call add(list[0], item)
925 | continue
926 | elseif (item.line == get(last, 'line', 0))
927 | " remove duplicate signs
928 | call add(list[0], item)
929 | continue
930 | endif
931 | let type=s:SignType(item.name)
932 | if match(keys(b:diffhl), type) < 0
933 | continue
934 | endif
935 | if !empty(type) && index(b:diffhl[type], item.line+0) == -1
936 | call add(list[0], item)
937 | " remove item from the placed sign list, so that we
938 | " don't erroneously place a dummy sign later on
939 | let next = get(s:placed_signs[0], ind+1, {})
940 | if item.name !~? 'dummy' && !empty(next) && next.name =~? 'dummy'
941 | " The next line should not be of type dummy, so add it to the
942 | " delete list and to the add list
943 | call add(list[0], next)
944 | if index(b:diffhl[type], next.line+0) > -1
945 | call add(list[1][type], next.line+0)
946 | endif
947 | call remove(s:placed_signs[0], ind+1)
948 | endif
949 | call remove(s:placed_signs[0], ind)
950 | else
951 | if item.name =~? 'dummy' && s:SignType(get(last, 'name', '')) != type
952 | call add(list[0], item)
953 | if index(b:diffhl[type], item.line+0) > -1
954 | call add(list[1][type], item.line+0)
955 | endif
956 | endif
957 | if s:SignType(item.name) == s:SignType(get(last, 'name', ''))
958 | \ && item.name !~ 'dummy'
959 | \ && item.line+0 == get(last, 'line', 0)+1
960 | " Current type needs to be of type dummy, but isn't,
961 | " remove and re-add the sign
962 | call add(list[0], item)
963 | call add(list[1][type], item.line+0)
964 | endif
965 | let ind+=1
966 | let last = item
967 | endif
968 | endfor
969 | " Check, which signs are to be placed
970 | let changes_signs_lines=s:ChangesSignsLines()
971 | for id in ['add', 'cha', 'del']
972 | for line in b:diffhl[id]
973 | let has_sign = index(changes_signs_lines, line) > -1
974 | let cur = index(list[1][id], line)
975 | let prev = index(b:diffhl[id], (line-1))
976 | if !has_sign && cur == -1
977 | call add(list[1][id], line)
978 | elseif prev > -1 && index(list[1][id], (line-1)) > -1
979 | " if a new line is inserted above an already existing line
980 | " with sign type 'add' make sure, that the already existing
981 | " sign type 'add' will be set to 'dummyadd' so that the '+'
982 | " sign appears at the correct line
983 | if cur == -1
984 | call add(list[1][id], line)
985 | endif
986 | if has_sign
987 | let previtem = s:PrevDictHasKey(line)
988 | if previtem.name == id
989 | call add(list[0], previtem)
990 | endif
991 | endif
992 | endif
993 | endfor
994 | endfor
995 | catch
996 | finally
997 | " in any case, return an empty list
998 | return list
999 | endtry
1000 | endfu
1001 | fu! s:UnPlaceSpecificSigns(dict) "{{{1
1002 | for sign in a:dict
1003 | let ind = index(s:placed_signs[0], sign)
1004 | if ind > -1
1005 | call remove(s:placed_signs[0], ind)
1006 | endif
1007 | if s:sign_api
1008 | call sign_unplace(s:sign_api_group, {'id': sign.id, 'buffer': bufnr('')})
1009 | else
1010 | exe "sign unplace ". sign.id. " buffer=".bufnr('')
1011 | endif
1012 | endfor
1013 | endfu
1014 | fu! s:PlaceSpecificSign(id, line, type, bufnr) "{{{1
1015 | if s:sign_api
1016 | call sign_place(a:id, s:sign_api_group, a:type, a:bufnr, {'lnum': a:line})
1017 | else
1018 | exe printf("sil sign place %d line=%d name=%s buffer=%d",
1019 | \ a:id, a:line, a:type, a:bufnr)
1020 | endif
1021 | endfu
1022 | fu! s:InitSignDef() "{{{1
1023 | let signs={}
1024 | let s:changes_sign_hi_style = get(s:, 'changes_sign_hi_style', 0)
1025 | let sign_hi = s:changes_sign_hi_style
1026 | if sign_hi == 2
1027 | for name in ['add', 'del', 'ch']
1028 | let signs.name = {'text': "\", 'texthl': 'SignColumn'}
1029 | endfor
1030 | else
1031 | " Added options for custom signs
1032 | let plus = (get(g:, 'changes_sign_text_utf8', 0) ? get(g:, 'changes_utf8_add_sign','➕') : get(g:,'changes_add_sign','+'))
1033 | let del = (get(g:, 'changes_sign_text_utf8', 0) ? get(g:, 'changes_utf8_delete_sign','➖') : get(g:,'changes_delete_sign','-'))
1034 | let mod = (get(g:, 'changes_sign_text_utf8', 0) ? get(g:, 'changes_utf8_modified_sign','★') : get(g:,'changes_modified_sign','*'))
1035 |
1036 | let signs['add'] = {
1037 | \ 'text': plus,
1038 | \ 'texthl': (sign_hi<2 ? "ChangesSignTextAdd" : "SignColumn"),
1039 | \ 'icon': s:MakeSignIcon(s:i_path.'add1.bmp'),
1040 | \ 'linehl': sign_hi > 0 ? 'DiffAdd' : ''}
1041 | let signs['del'] = {
1042 | \ 'text': del,
1043 | \ 'texthl': (sign_hi<2 ? "ChangesSignTextDel" : "SignColumn"),
1044 | \ 'icon': s:MakeSignIcon(s:i_path.'delete1.bmp'),
1045 | \ 'linehl': sign_hi > 0 ? 'DiffDelete' : ''}
1046 | let signs['cha'] = {
1047 | \ 'text': mod,
1048 | \ 'texthl': (sign_hi<2 ? "ChangesSignTextCh" : "SignColumn"),
1049 | \ 'icon': s:MakeSignIcon(s:i_path.'warning1.bmp'),
1050 | \ 'linehl': sign_hi > 0 ? 'DiffChange' : ''}
1051 | endif
1052 | let signs['add_dummy'] = {
1053 | \ 'text': plus,
1054 | \ 'texthl': (sign_hi<2 ? "ChangesSignTextDummyAdd": "SignColumn"),
1055 | \ 'linehl': sign_hi > 0 ? 'DiffAdd' : ''}
1056 | let signs['cha_dummy'] = {
1057 | \ 'text': mod,
1058 | \ 'texthl': (sign_hi<2 ? "ChangesSignTextDummyCh": "SignColumn"),
1059 | \ 'linehl': sign_hi > 0 ? 'DiffChange' : ''}
1060 |
1061 | for name in keys(signs)
1062 | " remove empty keys from dictionary
1063 | call filter(signs[name], {key, val -> !empty(val)})
1064 | " add name of sign to the definition, required for sign_define([list])
1065 | let signs[name].name = name
1066 | endfor
1067 |
1068 | return signs
1069 | endfu
1070 | fu! s:MakeSignIcon(path) "{{{1
1071 | " Windows seems to have problems with the gui
1072 | if has("gui_running") && !s:Is("win") &&
1073 | \ get(g:, 'changes_use_icons', 1) &&
1074 | \ filereadable(a:path)
1075 | return a:path
1076 | else
1077 | return ''
1078 | endif
1079 | endfu
1080 | fu! s:SaveRestoreChangeMarks(save) "{{{1
1081 | if a:save
1082 | let s:_change_mark = [getpos("'["), getpos("']")]
1083 | else
1084 | for i in [0,1]
1085 | call setpos("'".(i?']':'['), s:_change_mark[i])
1086 | endfor
1087 | endif
1088 | endfu
1089 | fu! s:HighlightTextChanges() "{{{1
1090 | " use the '[ and '] marks (if they are valid)
1091 | " and highlight changes
1092 | let seq_last=undotree()['seq_last']
1093 | let seq_cur =undotree()['seq_cur']
1094 | if seq_last > seq_cur && exists("b:changes_linehi_diff_match") &&
1095 | \ len(b:changes_linehi_diff_match) > 0
1096 | " change was undo
1097 | " remove last highlighting (this is just a guess!)
1098 | for [change, val] in items(b:changes_linehi_diff_match)
1099 | if change > seq_cur
1100 | sil call matchdelete(val)
1101 | unlet! b:changes_linehi_diff_match[change]
1102 | endif
1103 | endfor
1104 | return
1105 | endif
1106 | if get(g:, 'changes_linehi_diff', 0) &&
1107 | \ (getpos("'[")[1] !=? 1 ||
1108 | \ getpos("']")[1] !=? line('$')) &&
1109 | \ getpos("']") !=? getpos("'[")
1110 | " ignore those marks, if they are
1111 | " - not set (e.g. they are [0,0,0,0]
1112 | " - or '[ and '] are the same (happens when deleting lines)
1113 | " - or they do not match the complete buffer
1114 | call s:AddMatches(
1115 | \ s:GenerateHiPattern(getpos("'[")[1:2], getpos("']")[1:2]))
1116 | elseif get(g:, 'changes_linehi_diff', 0) && getpos("']") ==? getpos("'[")
1117 | " highlight the complete line
1118 | let pos1=[line("'[")] + [1]
1119 | let pos2=[line("'[")] + [1000] " just a guess
1120 | call s:AddMatches(s:GenerateHiPattern(pos1, pos2))
1121 | endif
1122 | endfu
1123 | fu! s:GenerateHiPattern(startl, endl) "{{{1
1124 | " startl - Start Position [line, col]
1125 | " endl - End Position [line, col]
1126 | " Easy way: match within a line
1127 | if a:startl[0] == a:endl[0]
1128 | return '\%'.a:startl[0]. 'l\%>'.(a:startl[1]-1).'c.*\%<'.(a:endl[1]+1).'c'
1129 | else
1130 | " Need to generate concat 3 patterns:
1131 | " 1) from startline, startcolumn till end of line
1132 | " 2) all lines between startline and end line
1133 | " 3) from start of endline until end column
1134 | "
1135 | " example: Start at line 1 col. 6 until line 3 column 12:
1136 | " \%(\%1l\%>6v.*\)\|\(\%>1l\%<3l.*\)\|\(\%3l.*\%<12v\)
1137 | return '\%(\%'. a:startl[0]. 'l\%>'. (a:startl[1]-1). 'c.*\)\|'.
1138 | \ '\%(\%>'. a:startl[0]. 'l\%<'. a:endl[0]. 'l.*\)\|'.
1139 | \ '\%(\%'. a:endl[0]. 'l.*\%<'. (a:endl[1]+1). 'c\)'
1140 | endif
1141 | endfu
1142 | fu! s:AddMatches(pattern) "{{{1
1143 | if !empty(a:pattern)
1144 | if !exists("b:changes_linehi_diff_match")
1145 | let b:changes_linehi_diff_match = {}
1146 | endif
1147 | let b:changes_linehi_diff_match[changenr()] = matchadd('CursorLine', a:pattern)
1148 | endif
1149 | endfu
1150 | fu! s:SignIdRemove() "{{{1
1151 | if !exists("b:changes_sign_id")
1152 | return
1153 | endif
1154 | " return the last id, that has been used for placing a sign
1155 | " and decrement id, so that the next call to s:SignId() will
1156 | " get a valid ID
1157 | let b:changes_sign_id -= 1
1158 | return printf("%d%02d", b:sign_prefix, b:changes_sign_id + 1)
1159 | endfu
1160 | fu! s:SignId() "{{{1
1161 | let b:changes_sign_id = get(b:, 'changes_sign_id', 0) + 1
1162 | return b:changes_sign_id
1163 | endfu
1164 | fu! s:IsUpdateAllowed(empty) "{{{1
1165 | if !empty(&buftype) || &ro || &diff || changes#CurrentBufferIsIgnored()
1166 | " Don't make a diff out of an unnamed buffer
1167 | " or of a special buffer or of a read-only buffer
1168 | if !empty(&buftype)
1169 | call s:StoreMessage("Special Buffer, not performing diffs!")
1170 | elseif &ro
1171 | call s:StoreMessage("Buffer read-only, not performing diffs!")
1172 | elseif &diff
1173 | call s:StoreMessage("disabled for diff-mode!")
1174 | else
1175 | call s:StoreMessage("Buffer is currently ignored, not performing diffs!")
1176 | endif
1177 | return 0
1178 | " only check for empty buffer when a:empty is true
1179 | elseif a:empty && empty(bufname(''))
1180 | call s:StoreMessage("Buffer hasn't been written yet. Can't diff!")
1181 | return 0
1182 | elseif !empty(bufname('')) && ((get(g:, 'changes_max_filesize', 0) > 0 &&
1183 | \ g:changes_max_filesize < getfsize(bufname(''))) ||
1184 | \ getfsize(bufname('')) < 0)
1185 | call s:StoreMessage('FileSize too large, skipping check')
1186 | return 0
1187 | endif
1188 | return 1
1189 | endfu
1190 |
1191 | if has("job") "{{{1
1192 | let s:jobs = {}
1193 |
1194 | function! s:on_exit(channel) dict abort "{{{2
1195 | if getfsize(self.output) <= 0
1196 | call s:StoreMessage("File not found or no differences found!")
1197 | if exists("b:diffhl")
1198 | " might need to remove invalid signs
1199 | call s:AfterDiff(self.bufnr)
1200 | endif
1201 | call changes#WarningMsg()
1202 | return
1203 | endif
1204 | if !exists("b:diffhl")
1205 | let b:diffhl={'add': [], 'del': [], 'cha': []}
1206 | endif
1207 | call s:ParseDiffOutput(self.output)
1208 | call s:AfterDiff(self.bufnr)
1209 | " redrawing is expansive, skipped
1210 | "redr!
1211 | if self.type != 3 || s:nodiff
1212 | let b:changes_view_enabled=1
1213 | endif
1214 | if self.type ==# 2
1215 | call s:ShowDifferentLines()
1216 | endif
1217 | call changes#WarningMsg()
1218 | call s:SaveRestoreChangeMarks(0)
1219 | " Remove from s:jobs
1220 | if has_key(s:jobs, self.file)
1221 | call remove(s:jobs, self.file)
1222 | endif
1223 | let s:jobid -= 1
1224 | endfunction
1225 |
1226 | function! s:ChangesDoAsync(cmd, file, type, outfile) "{{{2
1227 | if s:Is("win")
1228 | let cmd = (&sh =~# 'cmd.exe' ? 'cmd.exe /c ' : 'sh -c ') . a:cmd
1229 | else
1230 | let cmd = ['sh', '-c', a:cmd]
1231 | endif
1232 | if !empty(a:outfile)
1233 | let outfile=s:diff_out.'.'.s:jobid
1234 | else
1235 | let outfile=a:outfile
1236 | endif
1237 | if empty(a:file)
1238 | return
1239 | endif
1240 |
1241 | let options = {'file': a:file, 'cmd': a:cmd, 'type': a:type, 'output': outfile, 'bufnr': bufnr('')}
1242 | if has_key(s:jobs, a:file)
1243 | if job_status(get(s:jobs, a:file)) == 'run'
1244 | return
1245 | else
1246 | call job_stop(get(s:jobs, a:file))
1247 | call remove(s:jobs, a:file)
1248 | if s:jobid > 1
1249 | let s:jobid -= 1
1250 | endif
1251 | endif
1252 | endif
1253 | let id = job_start(cmd, {
1254 | \ 'err_io': 'out',
1255 | \ 'close_cb': function('s:on_exit', options)})
1256 | let s:jobs[a:file] = id
1257 | let s:jobid += 1
1258 | endfu
1259 | endif
1260 |
1261 | fu! changes#SetSignColumn() "{{{1
1262 | if exists("s:old_signcolumn") && &scl isnot# 'yes'
1263 | " user changed the setting, do not change it back again
1264 | if &scl is# 'no'
1265 | throw 'changes:abort'
1266 | else
1267 | return
1268 | endif
1269 | elseif &scl isnot# 'yes' && !&buftype ==# 'terminal'
1270 | set signcolumn=yes
1271 | let s:old_signcolumn = 1
1272 | endif
1273 | endfu
1274 | fu! changes#GetStats() "{{{1
1275 | return [ len(get(get(b:, 'diffhl', []), 'add', [])),
1276 | \ len(get(get(b:, 'diffhl', []), 'cha', [])),
1277 | \ len(get(get(b:, 'diffhl', []), 'del', []))]
1278 | endfu
1279 | fu! changes#WarningMsg() "{{{1
1280 | if !&vbs
1281 | " Set verbose to 1 to have messages displayed!
1282 | return
1283 | endif
1284 | if !empty(s:msg)
1285 | redraw!
1286 | let msg=["Changes.vim: " . s:msg[0]] + (len(s:msg) > 1 ? s:msg[1:] : [])
1287 | echohl WarningMsg
1288 | for mess in reverse(msg)
1289 | echomsg mess
1290 | endfor
1291 |
1292 | echohl Normal
1293 | let v:errmsg=msg[0]
1294 | let s:msg = []
1295 | endif
1296 | endfu
1297 | fu! changes#Output() "{{{1
1298 | let add = '+'
1299 | let ch = '*'
1300 | let del = '-'
1301 | let sign_def = s:signs
1302 | if !empty(sign_def)
1303 | let add = matchstr(sign_def['add'], 'text=\zs..')
1304 | let ch = matchstr(sign_def['cha'], 'text=\zs..')
1305 | let del = matchstr(sign_def['del'], 'text=\zs..')
1306 | endif
1307 | echohl Title
1308 | echo "Differences will be highlighted like this:"
1309 | echohl Normal
1310 | echo "========================================="
1311 | echohl ChangesSignTextAdd
1312 | echo add. " Added Lines"
1313 | echohl ChangesSignTextDel
1314 | echo del. " Deleted Lines"
1315 | echohl ChangesSignTextCh
1316 | echo ch. " Changed Lines"
1317 | echohl Normal
1318 | endfu
1319 | fu! changes#Init() "{{{1
1320 | " Message queue, that will be displayed.
1321 | let s:msg = []
1322 | " save change marks
1323 | call s:SaveRestoreChangeMarks(1)
1324 | " Ignore buffer
1325 | if !exists("s:ignore")
1326 | let s:ignore = {}
1327 | endif
1328 | let s:changes_signs_undefined=0
1329 | let s:autocmd = get(g:, 'changes_autocmd', 1)
1330 | " Check against a file in a vcs system
1331 | let s:vcs = get(g:, 'changes_vcs_check', 0)
1332 | if !exists("b:vcs_type")
1333 | if exists("g:changes_vcs_system")
1334 | let b:vcs_type = g:changes_vcs_system
1335 | endif
1336 | if exists("b:changes_vcs_system")
1337 | let b:vcs_type = b:changes_vcs_system
1338 | endif
1339 | if !exists("b:vcs_type")
1340 | let b:vcs_type = s:GuessVCSSystem()
1341 | endif
1342 | endif
1343 | if s:vcs && empty(b:vcs_type)
1344 | " disable VCS checking...
1345 | let s:vcs=0
1346 | endif
1347 | if !exists("s:vcs_cat")
1348 | let s:vcs_cat = {'git': 'show :',
1349 | \'bzr': 'cat ',
1350 | \'cvs': '-q update -p ',
1351 | \'darcs': '--show-contents ',
1352 | \'fossil': 'finfo -p ',
1353 | \'rcs': 'co -p ',
1354 | \'svn': 'cat ',
1355 | \'hg': 'cat '}
1356 |
1357 | " Define aliases...
1358 | let s:vcs_cat.mercurial = s:vcs_cat.hg
1359 | let s:vcs_cat.subversion = s:vcs_cat.svn
1360 | let s:vcs_diff = {'git': ' diff -U0 --no-ext-diff --no-color ',
1361 | \ 'hg' : ' diff -U0 '}
1362 | let s:vcs_apply = {'git': ' apply --cached --unidiff-zero ',
1363 | \ 'hg' : ' import - '}
1364 | endif
1365 |
1366 | " Settings for Version Control
1367 | if s:vcs && !empty(b:vcs_type)
1368 | if get(s:vcs_cat, b:vcs_type, 'NONE') == 'NONE'
1369 | call s:StoreMessage("Don't know VCS " . b:vcs_type)
1370 | call s:StoreMessage("VCS check will be disabled for now.")
1371 | let s:vcs=0
1372 | " Probably file not in a repository/working dir
1373 | endif
1374 | if !executable(b:vcs_type)
1375 | call s:StoreMessage("Guessing VCS: ". b:vcs_type)
1376 | call s:StoreMessage("Executable " . b:vcs_type . " not found! Aborting.")
1377 | call s:StoreMessage("You might want to set the g:changes_vcs_system variable to override!")
1378 | let s:vcs=0
1379 | " Probably file not in a repository/working dir
1380 | endif
1381 | endif
1382 | if !exists("s:diff_out")
1383 | let s:diff_out = tempname()
1384 | let s:diff_in_cur = s:diff_out.'.cur.txt'
1385 | let s:diff_in_old = s:diff_out.'.old.txt'
1386 | let s:jobid = 1 " job id
1387 | endif
1388 | let s:nodiff=0
1389 | " Make sure, we are fetching all placed signs again
1390 | unlet! s:all_signs
1391 | let s:old_signs = get(s:, 'signs', {})
1392 | let s:signs=s:InitSignDef()
1393 | " Only check the first time this file is loaded
1394 | " It should not be neccessary to check every time
1395 | if !exists("s:precheck")
1396 | try
1397 | call s:Check()
1398 | catch
1399 | call s:StoreMessage("changes plugin will not be working!")
1400 | " Rethrow exception
1401 | let s:autocmd = 0
1402 | throw "changes:abort"
1403 | return
1404 | endtry
1405 | let s:precheck=1
1406 | endif
1407 | if !get(g:, 'changes_respect_SignColumn', 0)
1408 | " Make the Sign column not standout
1409 | hi! clear SignColumn
1410 | if &l:nu || &l:rnu
1411 | hi! link SignColumn LineNr
1412 | else
1413 | hi! link SignColumn Normal
1414 | endif
1415 | endif
1416 | " This variable is a prefix for all placed signs.
1417 | " This is needed, to not mess with signs placed by the user
1418 | if !exists("b:sign_prefix")
1419 | let b:sign_prefix = (s:sign_api ? '' : s:GetSignId() + 10)
1420 | endif
1421 |
1422 | let s:placed_signs = s:PlacedSigns()
1423 | if s:old_signs !=? s:signs && !empty(s:old_signs)
1424 | " Sign definition changed, redefine them
1425 | call s:UnPlaceSpecificSigns(s:placed_signs[0])
1426 | call s:DefineSigns(1)
1427 | " need to parse placed signs again...
1428 | let s:placed_signs = s:PlacedSigns()
1429 | endif
1430 | call changes#SetSignColumn()
1431 | call changes#AuCmd(s:autocmd)
1432 | " map to update sign column, if g:changes_fast == 0
1433 | if !hasmapto('', 'i') && !get(g:, 'changes_fast', 1)
1434 | call ChangesMap('')
1435 | endif
1436 | endfu
1437 | fu! changes#CurrentBufferIsIgnored() "{{{1
1438 | return exists("s:ignore") && get(s:ignore, bufnr('%'), 0)
1439 | endfu
1440 | fu! changes#IgnoreCurrentBuffer() "{{{1
1441 | if !exists("s:ignore")
1442 | let s:ignore = {}
1443 | endif
1444 | let s:ignore[bufnr('%')]=1
1445 | endfu
1446 | fu! changes#UnignoreCurrentBuffer() "{{{1
1447 | if changes#CurrentBufferIsIgnored()
1448 | call remove(s:ignore, bufnr('%'))
1449 | endif
1450 | endfu
1451 | fu! changes#EnableChanges(arg, ...) "{{{1
1452 | " if a:1 given, make a diff against the given file
1453 | call changes#UnignoreCurrentBuffer()
1454 | try
1455 | let savevar = get(g:, 'changes_max_filesize', 0)
1456 | unlet! g:changes_max_filesize
1457 | call changes#Init()
1458 | if a:arg
1459 | call s:UnPlaceSigns(1)
1460 | endif
1461 | verbose call s:GetDiff(a:arg, (a:0 ? a:1 : ''))
1462 | catch
1463 | call changes#WarningMsg()
1464 | call changes#CleanUp()
1465 | finally
1466 | if savevar > 0
1467 | let g:changes_max_filesize = savevar
1468 | endif
1469 | endtry
1470 | endfu
1471 | fu! changes#CleanUp() "{{{1
1472 | " only delete signs, that have been set by this plugin
1473 | call s:UnPlaceSigns(1)
1474 | call changes#IgnoreCurrentBuffer()
1475 | for key in keys(get(s:, 'signs', {}))
1476 | exe "sil! sign undefine " key
1477 | endfor
1478 | if s:autocmd
1479 | call changes#AuCmd(0)
1480 | endif
1481 | let b:changes_view_enabled = 0
1482 | if exists("b:changes_linehi_diff_match")
1483 | for val in values(b:changes_linehi_diff_match)
1484 | " ignore possible errors
1485 | sil! call matchdelete(val)
1486 | endfor
1487 | endif
1488 | unlet! b:diffhl s:signs s:old_signs b:changes_linehi_diff_match b:changes_sign_id s:precheck
1489 | endfu
1490 | fu! changes#AuCmd(arg) "{{{1
1491 | if a:arg
1492 | if !exists("#Changes")
1493 | augroup Changes
1494 | autocmd!
1495 | au TextChanged,InsertLeave,FilterReadPost * :call s:UpdateView()
1496 | " make sure, hightlighting groups are not cleared
1497 | au ColorScheme,GUIEnter * :try|call s:Check() |catch|endtry
1498 | if s:Is('unix')
1499 | " FocusGained does not work well on Windows
1500 | " because calling background processess triggers
1501 | " FocusGAined autocommands recursively
1502 | au FocusGained * :call s:UpdateView(1)
1503 | endif
1504 | if !get(g:, 'changes_fast', 1)
1505 | au InsertEnter * :call changes#InsertSignOnEnter()
1506 | endif
1507 | au BufWritePost,BufWinEnter * :call s:UpdateView(1)
1508 | au VimLeave * call s:DeleteTempFiles()
1509 | augroup END
1510 | endif
1511 | else
1512 | augroup Changes
1513 | autocmd!
1514 | augroup END
1515 | augroup! Changes
1516 | endif
1517 | endfu
1518 | fu! changes#TCV() "{{{1
1519 | if get(b:, "changes_view_enabled", 0)
1520 | call s:UnPlaceSigns(0)
1521 | let b:changes_view_enabled = 0
1522 | echo "Hiding changes since last save"
1523 | else
1524 | try
1525 | call changes#Init()
1526 | call s:GetDiff(1, '')
1527 | let b:changes_view_enabled = 1
1528 | echo "Showing changes since last save"
1529 | catch
1530 | " Make sure, the message is actually displayed!
1531 | verbose call changes#WarningMsg()
1532 | call changes#CleanUp()
1533 | endtry
1534 | endif
1535 | endfu
1536 | fu! changes#MoveToNextChange(fwd, cnt) "{{{1
1537 | " Make sure, the hunks are up to date
1538 | let _fen = &fen
1539 | set nofen
1540 | call s:UpdateView()
1541 | let &fen = _fen
1542 |
1543 | let cur = line('.')
1544 | let dict = get(b:, "diffhl", {})
1545 | let lines = get(dict, "add", []) +
1546 | \ get(dict, "del", []) +
1547 | \ get(dict, "cha", [])
1548 | let lines = sort(lines, 'n')
1549 | " remove duplicates
1550 | let lines = uniq(lines)
1551 | if mode() =~? '[vs]' && index(lines, cur) == -1
1552 | " in visual mode and not within a hunk!
1553 | return "\"
1554 | endif
1555 |
1556 | let suffix = '0' " move to start of hunk
1557 | let cnt = a:cnt-1
1558 |
1559 | " only keep the start/end of a bunch of successive lines
1560 | let lines = s:RemoveConsecutiveLines(1, copy(lines)) +
1561 | \ s:RemoveConsecutiveLines(0, copy(lines))
1562 | " sort again...
1563 | let lines = sort(lines, 'n')
1564 |
1565 | if empty(lines)
1566 | echomsg "There are no ". (a:fwd ? "next" : "previous").
1567 | \ " differences!"
1568 | return "\"
1569 | elseif (a:fwd && max(lines) <= cur) ||
1570 | \ (!a:fwd && min(lines) >= cur)
1571 | echomsg "There are no more ". (a:fwd ? "next" : "previous").
1572 | \ " differences!"
1573 | return "\"
1574 | endif
1575 | if a:fwd
1576 | call filter(lines, 'v:val > cur')
1577 | if empty(lines)
1578 | return "\"
1579 | endif
1580 | else
1581 | call filter(lines, 'v:val < cur')
1582 | if empty(lines)
1583 | return "\"
1584 | else
1585 | call reverse(lines)
1586 | endif
1587 | endif
1588 | if cnt > len(lines)
1589 | let cnt=length(lines)
1590 | endif
1591 |
1592 | " Cancel the user given count
1593 | " otherwise the count would be multiplied with
1594 | " the given line number
1595 | let prefix=(cnt > 0 ? "\" : "")
1596 | return prefix.lines[cnt]. "G".suffix
1597 | endfu
1598 | fu! changes#CurrentHunk() "{{{1
1599 | if changes#MoveToNextChange(0,1) == "\"
1600 | " outside of a hunk
1601 | return "\"
1602 | else
1603 | return "[ho]h$"
1604 | endif
1605 | endfu
1606 | fu! changes#FoldDifferences(...) "{{{1
1607 | if &fde!=?'index(g:lines,v:lnum)>-1?0:1'
1608 | let b:chg_folds = {'fen': &fen, 'fdm': &fdm, 'fde': &fde}
1609 | " Use context from 'diffopt' setting, if it is set
1610 | let context = matchstr(&diffopt, 'context:\zs\d\+\ze')+0
1611 | if context == 0
1612 | let context = 3
1613 | endif
1614 | let context = empty(a:000) ? context : a:1
1615 | let g:lines = []
1616 | let dict=get(b:, 'diffhl', {})
1617 | for line in sort(get(dict, 'add', []) + get(dict, 'cha' , []) + get(dict, 'del', []), 'n')
1618 | for item in range(line-context,line+context)
1619 | " Add some context
1620 | if index(g:lines, item) > -1 || item < 1 || item > line('$')
1621 | continue
1622 | endif
1623 | call add(g:lines, item)
1624 | endfor
1625 | endfor
1626 | let g:lines=uniq(g:lines)
1627 | if !empty(g:lines)
1628 | setl fen fdm=expr fde=index(g:lines,v:lnum)>-1?0:1
1629 | else
1630 | let s:msg=[]
1631 | call s:StoreMessage('Not folding, no lines changes!')
1632 | verbose call changes#WarningMsg()
1633 | endif
1634 | else
1635 | for items in items(get(b:, 'chg_folds', {}))
1636 | exe "let &".items[0]."=".string(items[1])
1637 | endfor
1638 | endif
1639 | endfu
1640 | fu! changes#ToggleHiStyle() "{{{1
1641 | let s:changes_sign_hi_style += 1
1642 | if s:changes_sign_hi_style > 2
1643 | let s:changes_sign_hi_style = 0
1644 | endif
1645 | try
1646 | call changes#Init()
1647 | call s:GetDiff(1, '')
1648 | catch
1649 | " Make sure, the message is actually displayed!
1650 | verbose call changes#WarningMsg()
1651 | call changes#CleanUp()
1652 | endtry
1653 | endfu
1654 | fu! changes#MapCR() "{{{1
1655 | if !( pumvisible() ||
1656 | \ (exists("*wildmenumode") && wildmenumode()))
1657 | call changes#InsertSignOnEnter()
1658 | endif
1659 | return ''
1660 | endfu
1661 | fu! changes#InsertSignOnEnter() "{{{1
1662 | " prevent an expansive call to create a diff,
1663 | " simply check, if the current line has a sign
1664 | " and if not, add one
1665 | unlet! s:changes_last_inserted_sign
1666 | if !s:IsUpdateAllowed(1) || &cpo =~ '$'
1667 | " If '$' is inside 'cpo' setting, than the redraw by placing a sign
1668 | " will overwrite the '$' placed from the change command. So return
1669 | " if the user has '$' inside the cpoptions.
1670 | return
1671 | endif
1672 | try
1673 | call changes#Init()
1674 | catch changes:abort
1675 | endtry
1676 | let line = line('.')
1677 | let prev = line - 1
1678 | let next = line + 1
1679 | let name=s:PrevDictHasKey(line)
1680 | if name == {}
1681 | " no sign yet on current line, add one.
1682 | let prevname = s:PrevDictHasKey(prev)
1683 | let name = ((get(prevname, 'name', '') =~? 'add') ? 'add_dummy' : 'add')
1684 | let id=b:sign_prefix.s:SignId()
1685 | call s:PlaceSpecificSign(id, line, name, bufnr(''))
1686 | " on o in normal mode, we should keep the sign
1687 | if get(b:, 'changes_last_line', 0) == line('$')
1688 | let s:changes_last_inserted_sign={'id': id, 'line':line, 'type':name}
1689 | endif
1690 | endif
1691 | if get(s:PrevDictHasKey(next),'name', '') ==? 'add'
1692 | let item = filter(copy(s:placed_signs[0]), 'v:val.line ==? next')
1693 | call s:UnPlaceSpecificSigns(item)
1694 | call s:PlaceSpecificSign(item[0].id, next, 'add_dummy', bufnr(''))
1695 | endif
1696 | let b:changes_last_line = line('$')
1697 | endfu
1698 | fu! changes#StageHunk(line, revert) "{{{1
1699 | try
1700 | let cur = a:line
1701 | let _pwd = s:ChangeDir()
1702 | let _wsv = winsaveview()
1703 | let _vbs = &verbose
1704 | call changes#Init()
1705 | if get(b:, 'vcs_type', '') !=? 'git'
1706 | let &vbs=1
1707 | call s:StoreMessage("Sorry, staging Hunks is only supported for git!")
1708 | return
1709 | elseif !a:revert && changes#GetStats() ==? [0,0,0]
1710 | let &vbs=1
1711 | call s:StoreMessage('No changes detected, nothing to do!')
1712 | return
1713 | endif
1714 | if changes#GetStats() !=? [0,0,0] || a:revert
1715 | if &mod
1716 | sil noa write
1717 | endif
1718 | let git_rep_p = s:ReturnGitRepPath()
1719 | exe "lcd" git_rep_p
1720 | " When reverting, need to get cached diff
1721 | let diff = split(system(b:vcs_type.
1722 | \ s:vcs_diff[b:vcs_type].
1723 | \ (a:revert ? ' --cached ' : '').
1724 | \ expand('%')), "\n")
1725 | if v:shell_error
1726 | let &vbs=1
1727 | call s:StoreMessage("Error occured: ". join(diff, "\n"))
1728 | return
1729 | endif
1730 | let file=''
1731 | let found=0
1732 | let hunk=[]
1733 | let index = match(diff, '^+++')
1734 | for line in diff[index + 1 : ]
1735 | if line =~? '^@@.*@@'
1736 | if found
1737 | break
1738 | endif
1739 | let temp = split(line)[2]
1740 | let lines = split(temp, ',')
1741 | call map(lines, 'matchstr(v:val, ''\d\+'')+0')
1742 | if (len(lines) == 2 &&
1743 | \ cur >= lines[0] && cur < lines[0]+lines[1]) ||
1744 | \ (len(lines) == 1 && cur == lines[0]) ||
1745 | \ (len(lines) == 2 && lines[1] == 0 && cur == lines[0])
1746 | " this is the hunk the cursor is on
1747 | let found=1
1748 | endif
1749 | endif
1750 | if found
1751 | call add(hunk, line)
1752 | endif
1753 | endfor
1754 | if empty(hunk)
1755 | call s:StoreMessage('Cursor not on a diff hunk, aborting!')
1756 | let &vbs=1
1757 | return
1758 | endif
1759 | " Add filename to hunk
1760 | let hunk = diff[0:index] + hunk
1761 | let output=system(b:vcs_type. s:vcs_apply[b:vcs_type].
1762 | \ (a:revert ? ' --reverse - ' : ' - '),
1763 | \ s:Output(hunk))
1764 | if v:shell_error
1765 | call s:StoreMessage(output)
1766 | endif
1767 | call s:GetDiff(1, '')
1768 | endif
1769 | catch
1770 | let &vbs=1
1771 | call s:StoreMessage("Exception occured")
1772 | call s:StoreMessage(string(v:exception))
1773 | finally
1774 | exe "lcd " _pwd
1775 | call changes#WarningMsg()
1776 | if &vbs != _vbs
1777 | let &vbs = _vbs
1778 | endif
1779 | call winrestview(_wsv)
1780 | endtry
1781 | endfu
1782 | " Modeline "{{{1
1783 | " vi:fdm=marker fdl=0 ts=4 sw=4 et
1784 |
--------------------------------------------------------------------------------
/autoload/changes_icons/add.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrisbra/changesPlugin/f75e659b92a32e6653371888c3e6d0697018144a/autoload/changes_icons/add.bmp
--------------------------------------------------------------------------------
/autoload/changes_icons/add1.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrisbra/changesPlugin/f75e659b92a32e6653371888c3e6d0697018144a/autoload/changes_icons/add1.bmp
--------------------------------------------------------------------------------
/autoload/changes_icons/delete.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrisbra/changesPlugin/f75e659b92a32e6653371888c3e6d0697018144a/autoload/changes_icons/delete.bmp
--------------------------------------------------------------------------------
/autoload/changes_icons/delete1.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrisbra/changesPlugin/f75e659b92a32e6653371888c3e6d0697018144a/autoload/changes_icons/delete1.bmp
--------------------------------------------------------------------------------
/autoload/changes_icons/license_designkode.txt:
--------------------------------------------------------------------------------
1 | URL: http://www.designkode.com/blog/free-developer-icons
2 | License: Creative Commons (Attribution 3.0 Unported)
3 | "DesignKode is releasing this set of 40 free high quality icons for your web site and application GUI designs. All icons in this set are 32 x 32 pixel PNG image files. You may freely use these icons in your commercial or personal projects without attribution."
4 |
--------------------------------------------------------------------------------
/autoload/changes_icons/warning.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrisbra/changesPlugin/f75e659b92a32e6653371888c3e6d0697018144a/autoload/changes_icons/warning.bmp
--------------------------------------------------------------------------------
/autoload/changes_icons/warning1.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrisbra/changesPlugin/f75e659b92a32e6653371888c3e6d0697018144a/autoload/changes_icons/warning1.bmp
--------------------------------------------------------------------------------
/changes.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chrisbra/changesPlugin/f75e659b92a32e6653371888c3e6d0697018144a/changes.zip
--------------------------------------------------------------------------------
/doc/ChangesPlugin.txt:
--------------------------------------------------------------------------------
1 | *ChangesPlugin.txt* Print indication of changed lines for a buffer
2 |
3 | Author: Christian Brabandt
4 | Version: 0.16 Thu, 15 Jan 2015 21:16:40 +0100
5 | Copyright: (c) 2010-2019 by Christian Brabandt
6 |
7 | *ChangesPlugin-copyright*
8 | The VIM LICENSE applies to ChangesPlugin.txt (see |copyright|)
9 | except use ChangesPlugin instead of "Vim". NO WARRANTY, EXPRESS OR
10 | IMPLIED. USE AT-YOUR-OWN-RISK.
11 |
12 | ==============================================================================
13 | 1. Contents *ChangesPlugin*
14 |
15 | 1. Contents......................................: |ChangesPlugin|
16 | 2. Manual........................................: |ChangesPlugin-manual|
17 | 3. Configuration.................................: |ChangesPlugin-Config|
18 | 4. ChangesPlugin Feedback........................: |ChangesPlugin-Feedback|
19 | 5. ChangesPlugin History.........................: |ChangesPlugin-history|
20 | ==============================================================================
21 |
22 | *ChangesPlugin-manual*
23 | 2. Functionality
24 | ----------------
25 |
26 | This plugin was written to help visualize which lines have been changes since
27 | editing started for a file. The plugin was inspired by so called changed-bars,
28 | available at other editors, such as Embarcadero C++ Builder (there it is
29 | called Change Bars, see:
30 | http://edn.embarcadero.com/article/33453#6PersonalDeveloperProductivity)
31 | or Visual Studio where it is called indicator margin (see
32 | http://blog.eveningcreek.com/?p=151).
33 |
34 | ChangesPlugin.vim uses the |diff|-feature of vim and compares the actual
35 | buffer with it's saved state (or possibly against a state in a repository).
36 | In order to highlight the indicator signs at the first column, its using
37 | |signs|. For a description of the highlighting and how to customize it see
38 | |ChangesPlugin-colors|
39 |
40 | Note, that a '-' indicates, that at least one line was deleted between that
41 | particular line and the following line.
42 |
43 | This means, that in order to use this plugin you need a vim, that was built
44 | with |+signs|-support and |+diff|-support and you also need an executable diff
45 | command. If neither of these conditions are met, changePlugin.vim will issue a
46 | warning and abort.
47 |
48 | The plugin does not place signs on lines, on which a sign was already placed
49 | (by e.g. another plugin).
50 |
51 | *:EC* *:EnableChanges*
52 | By default the plugin is not enabled. To enable it enter >
53 | :EnableChanges
54 | When you run this command, ChangesPlugin.vim diffs the current file agains
55 | its saved file on disk and displays the changes in the first column.
56 |
57 | If you like to display the Changes against a different file, simply supply its
58 | filename as optional argument to that command, e.g. >
59 | :EnableChanges ~/project/foobar.c
60 | would display the Signs as if your current file has been diffed against the
61 | file ~/project/foobar.c
62 |
63 | Alternatively, you can enter the shortcut >
64 | :EC
65 | which basically calls :EnableChanges and supports the same arguments.
66 |
67 | If errors happen, the current buffer will be silently ignored when :EC
68 | is called later.
69 |
70 | *:DC* *:DisableChanges*
71 | If you want to disable the plugin, enter >
72 | :DisableChanges
73 | or alternatively, you can enter the shortcut >
74 | :DC
75 | and the Display of Changes will be disabled.
76 |
77 | *:TCV* *:ToggleChangeView*
78 | You can toggle, between turning on and off the indicator bars, using >
79 | :ToggleChangeView
80 | or alternatively: >
81 | :TCV
82 | to toggle the display of indicator bars.
83 |
84 | *:CC* *:ChangesCaption*
85 | You are probably wondering, what those strange looking signs mean. You can
86 | use either >
87 | :CC
88 | or >
89 | :ChangesCaptions
90 | to let the Plugin display a small caption, so you know what each sign means
91 | and how they are colored.
92 |
93 | *:CL* *:ChangesLineOverview*
94 | If you are editing a huge file with several hundreds of lines, it may be hard
95 | to find the lines that have been changed. >
96 | :CL
97 | or >
98 | :ChangesLineOverview
99 | provide an easy view to only see all modified lines. This will open the
100 | |location-list| buffer where you can easily see the affected lines. Pushing
101 | enter on any line, allows you to easily jump to that line in the buffer.
102 | Note: Only the first line of a hunk will be added. This makes it easier to use
103 | |:lnext| to jump from hunk to hunk.
104 |
105 | *:CD* *:ChangesDiffMode*
106 | You might want to keep the diff-window open, so you can use it for modifying
107 | your buffer using e.g. |:diffput| or |:diffget|
108 | Therefore ChangesPlugin defines the two commands >
109 | :CD
110 | and >
111 | :ChangesDiffMode
112 | If you issue any of these commands, a vertical split buffer will open,
113 | that contains the changes from either your VCS or from the unmodified buffer
114 | as it is saved on disk. See |copy-diffs| for how to merge changes between
115 | those two buffers.
116 |
117 | *:CF* *:ChangesFoldDiff*
118 | To temporarily fold aways all non-modified lines, you can use the :CF
119 | command >
120 | :CF
121 | and >
122 | :ChangesFoldDiff
123 |
124 | An optional number argument can be given to select the number of context lines
125 | to be shown (default: context value from 'diffopt' or 3 if not given).
126 | Issuing the command again, will restore the previous state.
127 |
128 | *:CT* *:ChangesStyleToggle*
129 | This command changes how changed lines will be highlighted. It toggles between
130 | three states. Initially changed lines will only be indicated by a sign in the
131 | first column. After toggling, the sign will be displayed and additionally, all
132 | lines will be highlighted. After calling it a second time, all lines will be
133 | highlighted, but there won't be an indicator sign in the first line. Use: >
134 | :CT
135 | and >
136 | :ChangesStyleToggle
137 |
138 | To toggle between all states.
139 | *:ChangesStageCurrentHunk*
140 | Stages the hunk, the cursor is currently on (works only for git). This is like
141 | incrementally save only part of a diff and commit it separately so that your
142 | commit histoy is cleaner. Use: >
143 |
144 | :ChangesStageCurrentHunk
145 | <
146 | to stage the hunk the cursor is currently on. Use: >
147 |
148 | :ChangesStageCurrentHunk!
149 |
150 | to revert a staged hunk. Note: The cursor needs to be on a line, that has been
151 | staged or that is on a diff.
152 |
153 | *ChangesPlugin-Maps* *[h* *]h*
154 | ChangesPlugin also sets up some mappings to move from one changed line to
155 | another. By default, those mappings are defined:
156 |
157 | `[h` move backwards to the next changed line,
158 | `]h` moves forward to the next changed line.
159 | *v_ah*
160 | `ah` selects the current hunk (only in Visual and Operator pending
161 | mode)
162 | *h*
163 | `h` selects the current hunk and stages it. (only possible for git).
164 | `H` selects the staged hunk and reverts it. (only possible for git).
165 |
166 | ==============================================================================
167 | *ChangesPlugin-Config*
168 | 3. Configuring ChangesPlugin.vim
169 |
170 | There are several different configuration options available.
171 |
172 | *ChangesPlugin-aucmd*
173 | g:changes_autocmd (default: 1)
174 | ------------------------------
175 |
176 | By default ChangesPlugin.vim will automatically update the view. You can
177 | however configure it to do not so. To disable this, set this variable in your
178 | |.vimrc| >
179 |
180 | let g:changes_autocmd=0
181 |
182 | The autocommands check, whether there have been changes to the file, or else
183 | they won't update the view.
184 | *ChangesPlugin-VCS*
185 | g:changes_vcs_check (default: 0)
186 | g:changes_vcs_system (default: '')
187 | ----------------------------------
188 |
189 | Those variables allow to check for differences agains a VCS.
190 | Warning: This feature is rather experimental. So use it with care.
191 |
192 | You can configure ChangesPlugin to check the differrences of the current
193 | buffer not only against the file stored on disk, but rather query a Version
194 | Control System (VCS) for its latest version and indicate changes based on
195 | this version.
196 |
197 | Currently, ChangesPlugin supports these VCS Systems:
198 | - git
199 | - hg
200 | - svn
201 | - cvs
202 | - bzr
203 | - darcs
204 | - fossil
205 | - RCS
206 |
207 | To enable this feature, you need to set the variable g:changes_vcs_check to 1.
208 | ChangesPlugin will then try to auto-detect, which of the above supported
209 | VCS-Systems is in use. This may fail obviously, so you can always force
210 | ChangesPlugin to use any of the above by setting the g:changes_vcs_system
211 | Variable.
212 |
213 | To enable this feature, you need to set the g:changes_vcs_check variable to 1.
214 | The following example enables this feature and ensures, EnablePlugin is using
215 | git as VCS, so these lines have been entered in the |.vimrc| >
216 |
217 | :let g:changes_vcs_check=1
218 | :let g:changes_vcs_system='git'
219 |
220 | Note, that depending on the VCS System you use, this might slow down
221 | ChangesPlugin significantly. Especially CVS seems to be very slow.
222 |
223 | To only specify a specific VCS for a certain buffer, set the buffer-local
224 | variable `b:changes_vcs_system` in that particular buffer. That overrides the
225 | global variable `g:changes_vcs_system`.
226 |
227 | Note also, that setting g:changes_vcs_system is setting a global variable (see
228 | |g:-var|) and therefore would set the VCS for every buffer opened in vim (thus
229 | you could use changesPlugin only with one single VCS). However, guessing the
230 | VCS System should work fairely well and in case it doesn't, please report a
231 | bug to the maintainer of the plugin. Setting g:changes_vcs_check will however
232 | disable the check against the on-disk version of a buffer.
233 |
234 | *ChangesPlugin-preview*
235 | g:changes_diff_preview (default: 0)
236 | ----------------------------------
237 |
238 | If you'd like to see the diff of the changes, you can set the variable
239 | g:changes_diff_preview. e.g. in your |.vimrc| set >
240 |
241 | :let g:changes_diff_preview = 1
242 | <
243 | This will display and update the preview window, whenever the indicator signs
244 | are displayed.
245 |
246 |
247 | g:changes_respect_SignColumn (default 0)
248 | ----------------------------------------
249 |
250 | If set, will not reset the highlighting color of the Sign Column to that of
251 | the Normal Highlighting group.
252 |
253 | g:changes_sign_text_utf8 (default 1)
254 | ------------------------------------
255 |
256 | If set, will use nice little UTF8 symbols for displaying the indicator
257 | signs.
258 |
259 | g:changes_max_filesize (default 0)
260 | ------------------------------------
261 |
262 | Maximum size in kbytes of a buffer, for which to perform the check. Larger
263 | buffers will likely slow down Vim considerably, since it needs to perform
264 | diffs and parse the result often. (default: off)
265 |
266 | *ChangesPlugin-colors*
267 | Specify different colors
268 | ------------------------
269 |
270 | By default, changes Plugin uses 3 different colors for highlighting changes
271 | to the buffer. Added Lines will be highlighted in green, Modified lines in
272 | blue and deleted lines will be highlighted in yellow, where the first line
273 | of a number of consecutive lines will be additionally marked by a letter
274 | (or a nice gui icon, if your Vim version supports it [usually only the gui
275 | version]).
276 |
277 | If you want to changes those colors, you can define your own custom
278 | highlighting. For example, you can place the following lines in your
279 | |.vimrc| >
280 |
281 | hi ChangesSignTextAdd ctermbg=yellow ctermfg=black guibg=green
282 | hi ChangesSignTextDel ctermbg=white ctermfg=black guibg=red
283 | hi ChangesSignTextCh ctermbg=black ctermfg=white guibg=blue
284 | hi ChangesSignTextDummyCh ctermfg=NONE ctermbg=white guifg=NONE guibg=white
285 | hi ChangesSignTextDummyAdd ctermfg=NONE ctermbg=green guifg=NONE guibg=green
286 |
287 | g:changes_linehi_diff (default: 0)
288 | ----------------------------------
289 |
290 | If set, will use Cursorline highlighting to overlay within a line the
291 | changes. Use |:DC| and |:EC| to reset all highlighting. Note, will only
292 | highlight changes, that have happened within the current editing session
293 | (does not attempt to parse the VCS diff to try to figure the differences
294 | out). Note: Currently very experimental!
295 |
296 | *ChangesPlugin-signs*
297 | You can customize what text will be displayed in the signcolumn by setting the
298 | following variables (this shows the defaults): >
299 |
300 | " Customize Ascii Text for Signs
301 | let g:changes_add_sign='+'
302 | let g:changes_delete_sign='-'
303 | let g:changes_modified_sign='*'
304 |
305 | " Customize UTF-8 Text for Signs
306 | " overrides the g_changes__sign
307 | let g:changes_utf8_add_sign='➕'
308 | let g:changes_utf8_delete_sign='➖'
309 | let g:changes_utf8_modifed_sign='★'
310 | *ChangesPlugin-Signcol*
311 | g:changes_fixed_sign_column (default: 0)
312 | ----------------------------------------
313 |
314 | When set, ChangesPlugin always tries to have the sign column visible, so that
315 | modifying the buffer won't make it "shift" because of a sudden disappearing or
316 | appearing SignColumn (|hl-SignColumn|) To enable, set in your |.vimrc| >
317 |
318 | let g:changes_fixed_sign_column=1
319 | <
320 | (not used for Vim 8 which has the 'signcolumn' setting.)
321 |
322 | g:changes_fast (default: 1)
323 | --------------------------
324 | If set (which it is by default), skip creating diffs at a couple occasions.
325 |
326 | Currently the following features are disabled, when g:changes_fast is set:
327 | - update within a row for small changes (e.g. hitting 'rx' repeatedly in
328 | the same row)
329 | - InsertEnter/InsertLeave autocommands to update signs
330 | - When inserting a new line in the buffer, update signs
331 |
332 | While those features are disabled, this also means Vim is more responsive,
333 | since it does not need to diff the buffer that often (at the cost of being
334 | slightly more imprecise).
335 |
336 | If you want to enable those settings, set in your |.vimrc| >
337 |
338 | let g:changes_fast=0
339 | <
340 | *ChangesPlugin-errors*
341 | By default the plugin will not display error messages. If you want to see
342 | them, set the 'verbose' option to 1 (or even 2).
343 |
344 | ==============================================================================
345 | 4. ChangesPlugin Feedback *ChangesPlugin-feedback*
346 |
347 | Feedback is always welcome. If you like the plugin, please rate it at the
348 | vim-page:
349 | http://www.vim.org/scripts/script.php?script_id=3052
350 |
351 | You can also follow the development of the plugin at github:
352 | http://github.com/chrisbra/changesPlugin
353 |
354 | Please don't hesitate to report any bugs to the maintainer, mentioned in the
355 | third line of this document.
356 |
357 | ==============================================================================
358 | 5. ChangesPlugin History *ChangesPlugin-history*
359 |
360 | BF: Bugfix
361 | NF: New Feature
362 |
363 | 0.16: unreleased:
364 | BF: |:DC| should also undefine the dummy sign
365 | BF: Check for flag for mappings
366 | BF: various improvements by Constantin Kulikov,
367 | https://github.com/chrisbra/changesPlugin/pull/14, thanks!
368 | BF: Document that |:ChangesFoldDiff| toggles
369 | BF: Make |:ChangesFoldDiff| show context lines
370 | BF: check, that the file exists in git
371 | NF: use signcolumn function if available
372 | NF: Make use of Sign API (neews Vim 8.1.616)
373 | BF: better detection of modified
374 | NF: better and simpler defaults for signs
375 | NF: Make use of Vim patch 8.1.1682 (to define and place list of signs)
376 | NF: Allow customization of signs
377 | 0.15: Jan 15, 2015: {{{1
378 | NF: use |TextChanged| autocommand to update signs faster
379 | NF: use new utf8 symbols
380 | NF: Hide SignColumn
381 | NF: include some nice looking icons for gvim
382 | BF: Code cleanup
383 | BF: resolve symlinks
384 | NF: airline integration
385 | NF: |:CF|
386 | NF: |:CT|
387 | BF: only draw at most one deleted sign, if a range of several
388 | consecutive lines have been deleted
389 | NF: only place newly changed lines (instead of always removing all
390 | previous signs and adding them again)
391 | NF: if g:changes_linehi_diff is set, tries to highlight changes within
392 | a line by overlaying |CursorColumn| highlighting over that parts.
393 | BF: try to skip expensive TextChanged calls if possible.
394 | BF: detect other signs correctly (didn't work with non-english locale)
395 | BF: do not try to detect changes on vim help files and read only files
396 | BF: calling |:DC| twice would cause an error
397 | BF: Make use of patch 7.4.341
398 | BF: When undefining signs and starting the gui, make sure no
399 | '[Deleted]' signs are left over
400 | BF: Clear SignColumn before linking it to Normal group
401 | BF: Make sure a warning message is shown for |:EC|
402 | BF: Catch |E255| when defining signs
403 | BF: prevent recursively calling |FocusGained| autocommand on windows
404 | NF: |ChangesPlugin-Signcol|
405 | BF: if vim-gitgutter is installed and the current buffer is handled by
406 | vim-gitgutter, let gitgutter handle the current buffer (issue
407 | https://github.com/chrisbra/changesPlugin/issues/12, reported by
408 | Gray-Wind, thanks!)
409 | NF: g:changes_fast variable (to enable more correct at the cost of
410 | being possibly slower placement of signs).
411 | BF: when updating signs, make sure that the first line of a
412 | consecutive number has the correcty type
413 | NF: In insert mode, when is pressed, update the signs immediately
414 | NF: InsertEnter/Leave autocommands to update signs
415 | NF: Stage hunks for git
416 | NF: Ignore special buffers (help window, quickfix buffer, command line
417 | window..)
418 | BF: Make |:CL| only add the first line of each hunk to the location
419 | list.
420 | 0.14: Aug 14, 2013: {{{1
421 | BF: Exception not caught (reported by Marco, thanks!)
422 | BF: Prevent flickering by resetting SignColumn on
423 | updates (reported by Marco, thanks!)
424 | BF: parse diff output instead of relying on vimdiff
425 | and parsing the syntax items (prevents flickering
426 | since no window needs to be split)
427 | BF: avoid error messages, when buffer is ignored
428 | (https://github.com/chrisbra/changesPlugin/issues/6
429 | reported by mpfusion, thanks!)
430 | BF: avoid error messages, using |DC|
431 | (https://github.com/chrisbra/changesPlugin/issues/7
432 | reported by mpfusion, thanks!)
433 | BF: don't mess with the |alternate-file|
434 | (https://github.com/chrisbra/changesPlugin/issues/8
435 | reported by mpfusion, thanks!)
436 | NF: |ChangesPlugin-preview|
437 | NF: |ChangesPlugin-Maps|
438 | [h/]h move to previous/next changed line
439 | 0.13: Feb 16, 2013: {{{1
440 | BF: Plugin used :EnableDisplayChanges, but
441 | documentation talked about EnableChanges
442 | (same for DisableChanges)
443 | BF: Don't load the autoload script when sourcing
444 | the plugin (reported by Sergey Kholev, thanks!)
445 | 0.12: Jan, 31, 2012: {{{1
446 | NF: Fix issue #3 from github (check changes against
447 | any file, suggested by Alessio Bolognino, Thanks!)
448 | BF: Fix E117 error (patch by Mate Gabri, Thanks!)
449 | BF: Check, that the current file is readable, before
450 | diffing (patch by Mate Gabri, Thanks!)
451 | BF: Sometimes, the previous Window is not accessible
452 | anymore
453 | (http://github.com/chrisbra/changesPlugin/issues/5
454 | by Mate Gabri, Thanks!)
455 | 0.11: May 04, 2010: {{{1
456 | BF: Document, that |InsertLeave| autocommand is used
457 | as autocommand
458 | BF: generate the help file with 'et' set, so that the
459 | README at github looks prettier
460 | BF: When staying in diff mode, don't reset 'fdm'
461 | and apply syntax coloring to scratch buffer
462 | BF: the check for the diff executable does not work
463 | as expected (Reported by Sergey Khorev),
464 | additionally outputting the Warnings did not work
465 | in that case
466 | 0.10: Apr 28, 2010: {{{1
467 | NF: Fixed Issue 1 from github
468 | (http://github.com/chrisbra/changesPlugin/issues/1/find)
469 | 0.9: Apr 24, 2010: {{{1
470 | NF: You can now use different VCS Systems for each
471 | buffer you are using.
472 | NF: Stay in diff mode
473 | BF: Fix the display of deleted signs
474 | BF: Undefining old signs, so that changing
475 | g:changes_hl_lines works
476 | BF: Some more error handling.
477 | NF: Show an overview for changed lines in location-list
478 | (|:CL|)
479 | NF: Show what each sign means using |:CC|
480 | 0.8: Apr 22, 2010: {{{1
481 | NF: Renamed the helpfile, to make it more obvious,
482 | that it refers to a plugin
483 | NF: Outputting name of checked file, if checking
484 | against VCS
485 | BF: Don't check for empty files.
486 | BF: Reworked the Message function
487 | BF: Don't try to place signs, if there are no
488 | differences
489 | (unreleased, VCS successfully tested with
490 | git, hg, svn, cvs, bzr)
491 | 0.7: Apr 19, 2010: {{{1
492 | NF: Check against a file in a VCS
493 | (unreleased, first working version,
494 | needs to be checked for each VCS)
495 | 0.6: Apr 12, 2010: {{{1
496 | BF: fixed a missing highlight for DiffText
497 | 0.5: Apr 12, 2010: {{{1
498 | BF: error when trying to access b:diffhl in the
499 | scratch buffer. This should be fixed now (thanks
500 | Jeet Sukumaran!)
501 | BF: Use the correct highlighting groups (thanks Jeet
502 | Sukumaran!)
503 | 0.4: Apr 12, 2010: {{{1
504 | NF: |ToggleChangesView|
505 | NF: The autocommand checks, if the buffer has been
506 | modified, since the last time.
507 | BF: Do not mess with signs, that have not been placed
508 | by ChangesPlugin.vim
509 | BF: CleanUp was seriously messed up (sorry, I must
510 | have been asleep, when writing that)
511 | BF: Take care of 'foldcolumn' setting, which would be
512 | overwritten by the signs-column
513 | 0.3: Apr 11, 2010: {{{1
514 | BF: redraw, so that the diff window will not be
515 | displayed
516 | NF: enabled GLVS (see |GLVS|)
517 | 0.2: Apr 11, 2010: {{{1
518 | Added Documentation
519 | created an autoload version
520 | 0.1: Apr 10, 2010: {{{1
521 | First working version
522 | Modeline: {{{1
523 | ==============================================================================
524 | vim:tw=76:ts=8:ft=help:et:fdm=marker:fdl=0
525 |
--------------------------------------------------------------------------------
/plugin/changesPlugin.vim:
--------------------------------------------------------------------------------
1 | " ChangesPlugin.vim - Using Signs for indicating changed lines
2 | " ---------------------------------------------------------------
3 | " Version: 0.16
4 | " Authors: Christian Brabandt
5 | " Last Change: Thu, 15 Jan 2015 21:16:40 +0100
6 | " Script: http://www.vim.org/scripts/script.php?script_id=3052
7 | " License: VIM License
8 | " Documentation: see :help changesPlugin.txt
9 | " GetLatestVimScripts: 3052 15 :AutoInstall: ChangesPlugin.vim
10 | " ---------------------------------------------------------------------
11 | " Load Once: {{{1
12 | if &cp || exists("g:loaded_changes")
13 | finish
14 | endif
15 | let g:loaded_changes = 1
16 | let s:keepcpo = &cpo
17 | set cpo&vim
18 | if v:version < 800 && !has('nvim')
19 | echohl WarningMsg
20 | echomsg "The ChangesPlugin needs at least a Vim version 8"
21 | echohl Normal
22 | finish
23 | endif
24 |
25 | " ---------------------------------------------------------------------
26 | " Public Functions: {{{1
27 | fu! ChangesMap(char) "{{{2
28 | if a:char is ''
29 | imap