-
Notifications
You must be signed in to change notification settings - Fork 165
/
Copy pathindent_guides.vim
295 lines (259 loc) · 9.46 KB
/
indent_guides.vim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
" Author: Nate Kane <nathanaelkane AT gmail DOT com>
" Homepage: http://github.com/preservim/vim-indent-guides
"
" Toggles the indent guides on and off.
"
function! indent_guides#toggle() abort
call indent_guides#init_matches()
if empty(w:indent_guides_matches)
call indent_guides#enable()
else
call indent_guides#disable()
endif
endfunction
"
" Called from autocmds, keeps indent guides enabled or disabled when entering
" other buffers and windows.
"
function! indent_guides#process_autocmds() abort
if g:indent_guides_autocmds_enabled
call indent_guides#enable()
else
call indent_guides#disable()
end
endfunction
"
" Enables the indent guides for the current buffer and any other buffer upon
" entering it.
"
function! indent_guides#enable() abort
let g:indent_guides_autocmds_enabled = 1
if &diff || indent_guides#exclude_filetype()
call indent_guides#clear_matches()
return
end
call indent_guides#init_script_vars()
call indent_guides#highlight_colors()
call indent_guides#clear_matches()
" loop through each indent level and define a highlight pattern
" will automagically figure out whether to use tabs or spaces
for l:level in range(s:start_level, s:indent_levels)
let l:group = 'IndentGuides' . ((l:level % 2 == 0) ? 'Even' : 'Odd')
let l:column_start = (l:level - 1) * s:indent_size + 1
" define the higlight patterns and add to matches list
if g:indent_guides_space_guides
let l:soft_pattern = indent_guides#indent_highlight_pattern(g:indent_guides_soft_pattern, l:column_start, s:guide_size)
call add(w:indent_guides_matches, matchadd(l:group, l:soft_pattern))
end
if g:indent_guides_tab_guides
let l:hard_pattern = indent_guides#indent_highlight_pattern('\t', l:column_start, s:indent_size)
call add(w:indent_guides_matches, matchadd(l:group, l:hard_pattern))
end
endfor
endfunction
"
" Disables the indent guides for the current buffer and any other buffer upon
" entering it.
"
function! indent_guides#disable() abort
let g:indent_guides_autocmds_enabled = 0
call indent_guides#clear_matches()
endfunction
"
" Clear all highlight matches for the current window.
"
function! indent_guides#clear_matches() abort
call indent_guides#init_matches()
if !empty(w:indent_guides_matches)
let l:index = 0
for l:match_id in w:indent_guides_matches
try
call matchdelete(l:match_id)
catch /E803:/
" Do nothing
endtry
call remove(w:indent_guides_matches, l:index)
let l:index += l:index
endfor
endif
" Make sure to clear indent guide if remembered match id has gone somehow.
for l:match in getmatches()
if l:match.group =~# '^IndentGuides\v(Even|Odd)$'
call matchdelete(l:match.id)
endif
endfor
endfunction
"
" Automagically calculates and defines the indent highlight colors.
"
function! indent_guides#highlight_colors() abort
if s:auto_colors
if has('gui_running') || has('nvim') || (has('termguicolors') && &termguicolors)
call indent_guides#gui_highlight_colors()
else
call indent_guides#basic_highlight_colors()
endif
endif
endfunction
"
" Defines some basic indent highlight colors that work for Terminal Vim and
" gVim when colors can't be automatically calculated.
"
function! indent_guides#basic_highlight_colors() abort
let l:cterm_colors = (&g:background ==# 'dark') ? ['darkgrey', 'black'] : ['lightgrey', 'white']
let l:gui_colors = (&g:background ==# 'dark') ? ['grey15', 'grey30'] : ['grey70', 'grey85']
exe 'hi IndentGuidesEven guibg=' . l:gui_colors[0] . ' guifg=' . l:gui_colors[1] . ' ctermbg=' . l:cterm_colors[0] . ' ctermfg=' . l:cterm_colors[1]
exe 'hi IndentGuidesOdd guibg=' . l:gui_colors[1] . ' guifg=' . l:gui_colors[0] . ' ctermbg=' . l:cterm_colors[1] . ' ctermfg=' . l:cterm_colors[0]
endfunction
"
" Automagically calculates and defines the indent highlight colors for gui
" vim.
"
function! indent_guides#gui_highlight_colors() abort
let l:hi_normal_guibg = ''
" capture the backgroud color from the normal highlight
if s:hi_normal =~ s:color_hex_bg_pat
" hex color code is being used, eg. '#FFFFFF'
let l:hi_normal_guibg = matchstr(s:hi_normal, s:color_hex_bg_pat)
elseif s:hi_normal =~ s:color_name_bg_pat
" color name is being used, eg. 'white'
let l:color_name = matchstr(s:hi_normal, s:color_name_bg_pat)
let l:hi_normal_guibg = color_helper#color_name_to_hex(l:color_name)
else
" background color could not be detected, default to basic colors
call indent_guides#basic_highlight_colors()
endif
if l:hi_normal_guibg =~ s:color_hex_pat
" calculate the highlight background colors
let l:hi_odd_bg = indent_guides#lighten_or_darken_color(l:hi_normal_guibg)
let l:hi_even_bg = indent_guides#lighten_or_darken_color(l:hi_odd_bg)
" define the new highlights
exe 'hi IndentGuidesOdd guibg=' . l:hi_odd_bg . ' guifg=' . l:hi_even_bg
exe 'hi IndentGuidesEven guibg=' . l:hi_even_bg . ' guifg=' . l:hi_odd_bg
end
endfunction
"
" Takes a color and darkens or lightens it depending on whether a dark or light
" colorscheme is being used.
"
function! indent_guides#lighten_or_darken_color(color) abort
let l:new_color = ''
if (&g:background ==# 'dark')
let l:new_color = color_helper#hex_color_lighten(a:color, s:change_percent)
else
let l:new_color = color_helper#hex_color_darken (a:color, s:change_percent)
endif
return l:new_color
endfunction
"
" Define default highlights.
"
function! indent_guides#define_default_highlights() abort
hi default clear IndentGuidesOdd
hi default clear IndentGuidesEven
endfunction
"
" Init the w:indent_guides_matches variable.
"
function! indent_guides#init_matches() abort
let w:indent_guides_matches = exists('w:indent_guides_matches') ? w:indent_guides_matches : []
endfunction
"
" We need to initialize these vars every time a buffer is entered while the
" plugin is enabled.
"
function! indent_guides#init_script_vars() abort
if &l:shiftwidth > 0 && &l:expandtab
let s:indent_size = &l:shiftwidth
else
let s:indent_size = &l:tabstop
endif
let s:guide_size = indent_guides#calculate_guide_size()
let s:hi_normal = indent_guides#capture_highlight('Normal')
" remove 'font=<value>' from the s:hi_normal string (only seems to happen on Vim startup in Windows)
let s:hi_normal = substitute(s:hi_normal, ' font=[A-Za-z0-9:]\+', '', '')
" shortcuts to the global variables - this makes the code easier to read
let s:debug = g:indent_guides_debug
let s:indent_levels = g:indent_guides_indent_levels
let s:auto_colors = g:indent_guides_auto_colors
let s:color_hex_pat = g:indent_guides_color_hex_pattern
let s:color_hex_bg_pat = g:indent_guides_color_hex_guibg_pattern
let s:color_name_bg_pat = g:indent_guides_color_name_guibg_pattern
let s:start_level = g:indent_guides_start_level
" str2float not available in vim versions <= 7.1
if has('float')
let s:change_percent = g:indent_guides_color_change_percent / str2float('100.0')
else
let s:change_percent = g:indent_guides_color_change_percent / 100.0
endif
if s:debug
echo 's:indent_size = ' . s:indent_size
echo 's:guide_size = ' . s:guide_size
echo 's:hi_normal = ' . s:hi_normal
echo 's:indent_levels = ' . s:indent_levels
echo 's:auto_colors = ' . s:auto_colors
echo 's:change_percent = ' . string(s:change_percent)
echo 's:color_hex_pat = ' . s:color_hex_pat
echo 's:color_hex_bg_pat = ' . s:color_hex_bg_pat
echo 's:color_name_bg_pat = ' . s:color_name_bg_pat
echo 's:start_level = ' . s:start_level
endif
endfunction
"
" Calculate the indent guide size. Ensures the guide size is less than or
" equal to the actual indent size, otherwise some weird things can occur.
"
" NOTE: Currently, this only works when soft-tabs are being used.
"
function! indent_guides#calculate_guide_size() abort
let l:guide_size = g:indent_guides_guide_size
if l:guide_size == 0 || l:guide_size > s:indent_size
let l:guide_size = s:indent_size
endif
return l:guide_size
endfunction
"
" Captures and returns the output of highlight group definitions.
"
" Example: indent_guides#capture_highlight('normal')
" Returns: 'Normal xxx guifg=#323232 guibg=#ffffff'
"
function! indent_guides#capture_highlight(group_name) abort
let l:output = execute('hi ' . a:group_name, 'silent')
let l:output = substitute(l:output, '\n', '', '')
return l:output
endfunction
"
" Returns a regex pattern for highlighting an indent level.
"
" Example: indent_guides#indent_highlight_pattern(' ', 1, 4)
" Returns: /^ *\%1v\zs *\%5v\ze/
"
" Example: indent_guides#indent_highlight_pattern('\s', 5, 2)
" Returns: /^\s*\%5v\zs\s*\%7v\ze/
"
" Example: indent_guides#indent_highlight_pattern('\t', 9, 2)
" Returns: /^\t*\%9v\zs\t*\%11v\ze/
"
function! indent_guides#indent_highlight_pattern(indent_pattern, column_start, indent_size) abort
let l:pattern = '^' . a:indent_pattern . '*\%' . a:column_start . 'v\zs'
let l:pattern .= a:indent_pattern . '*\%' . (a:column_start + a:indent_size) . 'v'
let l:pattern .= '\ze'
return l:pattern
endfunction
"
" Detect if any of the buffer filetypes should be excluded.
"
function! indent_guides#exclude_filetype() abort
if exists('g:indent_guides_exclude_buftype')
if g:indent_guides_exclude_buftype && &buftype !=# ''
return 1
endif
endif
for ft in split(&ft, '\.', 1)
if index(g:indent_guides_exclude_filetypes, ft) > -1
return 1
end
endfor
return 0
endfunction