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
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
|
" Intelligent Indent
" Author: Michael Geddes < vimmer at frog dot wheelycreek dot net >
" Version: 2.6
" Last Modified: December 2010
"
" Histroy:
" 1.0: - Added RetabIndent command - similar to :retab, but doesn't cause
" internal tabs to be modified.
" 1.1: - Added support for backspacing over spaced tabs 'smarttab' style
" - Clean up the look of it by blanking the :call
" - No longer a 'filetype' plugin by default.
" 1.2: - Interactions with 'smarttab' were causing problems. Now fall back to
" vim's 'smarttab' setting when inserting 'indent' tabs.
" - Fixed compat with digraphs (which were getting swallowed)
" - Made <BS> mapping work with the 'filetype' plugin mode.
" - Make CTabAlignTo() public.
" 1.3: - Fix removing trailing spaces with RetabIndent! which was causing
" initial indents to disappear.
" 1.4: - Fixed Backspace tab being off by 1
" 2.0: - Add support for alignment whitespace for mismatched brackets to be spaces.
" 2.1: - Fix = operator
" 2.3: - Fix (Gene Smith) for error with non C files
" - Add option for filetype maps
" - Allow for lisp indentation
" 2.4: - Fix bug in Retab
" 2.5: - Fix issue with <CR> not aligning
" 2.6: - Fix issue with alignment not disappearing.
" This is designed as a filetype plugin (originally a 'Buffoptions.vim' script).
"
" The aim of this script is to be able to handle the mode of tab usage which
" distinguishes 'indent' from 'alignment'. The idea is to use <tab>
" characters only at the beginning of lines.
"
" This means that an individual can use their own 'tabstop' settings for the
" indent level, while not affecting alignment.
"
" The one caveat with this method of tabs is that you need to follow the rule
" that you never 'align' elements that have different 'indent' levels.
"
" :RetabIndent[!] [tabstop]
" This is similar to the :retab command, with the exception that it
" affects all and only whitespace at the start of the line, changing it to
" suit your current (or new) tabstop and expandtab setting.
" With the bang (!) at the end, the command also strips trailing
" whitespace.
"
" CTabAlignTo(n)
" 'Tab' to the n'th column from the start of the indent.
" g:ctab_filetype_maps
" set this to true if script used as a filetype plugin
" g:ctab_disable_checkalign
" set this to true to disable re-check of alignment
" g:ctab_enable_default_filetype_maps
" disable the filetype specific maps
" g:ctab_disable_tab_maps
" disable the (original) tab mappings
if exists('g:ctab_filetype_maps') && g:ctab_filetype_maps
let s:buff_map=' <buffer> '
else
let s:buff_map=''
endif
if exists('g:ctab_enable_default_filetype_maps') && ctab_enable_default_filetype_maps
if s:buff_map != ''
if (&filetype =~ '^\(cpp\|idl\)$' )
imap <silent> <buffer> <expr> <m-;> CTabAlignTo(20).'//'
imap <silent> <buffer> <expr> <m-s-;> CTabAlignTo(30).'//'
imap <silent> <buffer> � <m-s-;>
elseif &filetype == 'c'
imap <expr> <silent> <buffer> <m-;> CTabAlignTo(10).'/* */<left><left><left>'
endif
else
au FileType cpp,idl imap <expr> <silent> <buffer> <m-;> CTabAlignTo(20).'//'
au FileType cpp,idl imap <expr> <silent> <buffer> <m-:> CTabAlignTo(30).'//'
au FileType c imap <expr> <silent> <buffer> <m-;> CTabAlignTo(10).'/* */<left><left>'
endif
endif
if !exists('g:ctab_disable_tab_maps') || ! g:ctab_disable_tab_maps
exe 'imap '.s:buff_map.'<silent> <expr> <tab> <SID>InsertSmartTab()'
exe 'inoremap '.s:buff_map.'<silent> <expr> <BS> <SID>DoSmartDelete()."\<BS>"'
endif
"exe 'imap '.s:buff_map.'<silent> <expr> <BS> <SID>KeepDelLine()."\<BS>"
" MRG: TODO
"exe 'imap '.s:buff_map.'<silent> <expr> <c-d> :call <SID>SmartDeleteTab()<CR>'
"exe 'imap '.s:buff_map.'<silent> <c-t> <SID>SmartInsertTab()'
" fun! s:SmartDeleteTab()
" let curcol=col('.')-&sw
" let origtxt=getline('.')
" let repl=matchstr(origtxt,'^\s\{-}\%'.(&sw+2)."v')
" if repl == '' then
" return "\<c-o>".':s/ *\zs /'.repeat(' ',(&ts-&sw)).'/'."\<CR>\<c-o>".curcol.'|'
" else
" return "\<c-o>".':s/^\s\{-}\%'.(&sw+1)."v//\<CR>\<c-o>".curcol."|"
" end
"
" endfun
" Insert a smart tab.
fun! s:InsertSmartTab()
" Clear the status
echo ''
if strpart(getline('.'),0,col('.')-1) =~'^\s*$'
if exists('b:ctab_hook') && b:ctab_hook != ''
exe 'return '.b:ctab_hook
elseif exists('g:ctab_hook') && g:ctab_hook != ''
exe 'return '.g:ctab_hook
endif
return "\<Tab>"
endif
let sts=exists("b:insidetabs")?(b:insidetabs):((&sts==0)?&sw:&sts)
let sp=(virtcol('.') % sts)
if sp==0 | let sp=sts | endif
return strpart(" ",0,1+sts-sp)
endfun
fun! s:CheckLeaveLine(line)
if ('cpo' !~ 'I') && exists('b:ctab_lastalign') && (a:line == b:ctab_lastalign)
s/^\s*$//e
endif
endfun
" Check on blanks
aug Ctab
au! InsertLeave * call <SID>CheckLeaveLine(line('.'))
aug END
" Do a smart delete.
" The <BS> is included at the end so that deleting back over line ends
" works as expected.
fun! s:DoSmartDelete()
" Clear the status
"echo ''
let uptohere=strpart(getline('.'),0,col('.')-1)
" If at the first part of the line, fall back on defaults... or if the
" preceding character is a <TAB>, then similarly fall back on defaults.
"
let lastchar=matchstr(uptohere,'.$')
if lastchar == "\<tab>" || uptohere =~ '^\s*$' | return '' | endif " Simple cases
if lastchar != ' ' | return ((&digraph)?("\<BS>".lastchar): '') | endif " Delete non space at end / Maintain digraphs
" Work out how many tabs to use
let sts=(exists("b:insidetabs")?(b:insidetabs):((&sts==0)?(&sw):(&sts)))
let ovc=virtcol('.') " Find where we are
let sp=(ovc % sts) " How many virtual characters to delete
if sp==0 | let sp=sts | endif " At least delete a whole tabstop
let vc=ovc-sp " Work out the new virtual column
" Find how many characters we need to delete (using \%v to do virtual column
" matching, and making sure we don't pass an invalid value to vc)
let uthlen=strlen(uptohere)
let bs= uthlen-((vc<1)?0:( match(uptohere,'\%'.(vc-1).'v')))
let uthlen=uthlen-bs
" echo 'ovc = '.ovc.' sp = '.sp.' vc = '.vc.' bs = '.bs.' uthlen='.uthlen
if bs <= 0 | return '' | endif
" Delete the specifed number of whitespace characters up to the first non-whitespace
let ret=''
let bs=bs-1
if uptohere[uthlen+bs] !~ '\s'| return '' | endif
while bs>=-1
let bs=bs-1
if uptohere[uthlen+bs] !~ '\s' | break | endif
let ret=ret."\<BS>"
endwhile
return ret
endfun
fun! s:Column(line)
let c=0
let i=0
let len=strlen(a:line)
while i< len
if a:line[i]=="\<tab>"
let c=(c+&tabstop)
let c=c-(c%&tabstop)
else
let c=c+1
endif
let i=i+1
endwhile
return c
endfun
fun! s:StartColumn(lineNo)
return s:Column(matchstr(getline(a:lineNo),'^\s*'))
endfun
fun! CTabAlignTo(n)
let co=virtcol('.')
let ico=s:StartColumn('.')+a:n
if co>ico
let ico=co
endif
let spaces=ico-co
let spc=''
while spaces > 0
let spc=spc." "
let spaces=spaces-1
endwhile
return spc
endfun
if ! exists('g:ctab_disable_checkalign') || g:ctab_disable_checkalign==0
" Check the alignment of line.
" Used in the case where some alignment whitespace is required .. like for unmatched brackets.
fun! s:CheckAlign(line)
if &expandtab || !(&autoindent || &indentexpr || &cindent)
return ''
endif
let tskeep=&ts
let swkeep=&sw
try
if a:line == line('.')
let b:ctab_lastalign=a:line
else
unlet b:ctab_lastalign
endif
set ts=50
set sw=50
if &indentexpr != ''
let v:lnum=a:line
sandbox exe 'let inda='.&indentexpr
if inda == -1
let inda=indent(a:line-1)
endif
elseif &cindent
let inda=cindent(a:line)
elseif &lisp
let inda=lispindent(a:line)
elseif &autoindent
let inda=indent(a:line)
elseif &smarttab
return ''
else
let inda=0
endif
finally
let &ts=tskeep
let &sw=swkeep
endtry
let indatabs=inda / 50
let indaspace=inda % 50
let indb=indent(a:line)
if indatabs*&tabstop + indaspace == indb
let txtindent=repeat("\<Tab>",indatabs).repeat(' ',indaspace)
call setline(a:line, substitute(getline(a:line),'^\s*',txtindent,''))
endif
return ''
endfun
fun! s:SID()
return matchstr(expand('<sfile>'), '<SNR>\zs\d\+\ze_SID$')
endfun
" Get the spaces at the end of the indent correct.
" This is trickier than it should be, but this seems to work.
fun! s:CheckCR()
" echo 'SID:'.s:SID()
if getline('.') =~ '^\s*$'
if ('cpo' !~ 'I') && exists('b:ctab_lastalign') && (line('.') == b:ctab_lastalign)
return "^\<c-d>\<CR>"
endif
return "\<CR>"
else
return "\<CR>\<c-r>=<SNR>".s:SID().'_CheckAlign(line(''.''))'."\<CR>\<END>"
endif
endfun
"exe 'inoremap '.s:buff_map.'<silent> <CR> <CR><c-r>=<SID>CheckAlign(line(''.''))."\<lt>END>"<CR>'
exe 'inoremap '.s:buff_map.'<silent> <expr> <CR> <SID>CheckCR()'
exe 'nnoremap '.s:buff_map.'<silent> o o<c-r>=<SID>CheckAlign(line(''.''))."\<lt>END>"<CR>'
exe 'nnoremap '.s:buff_map.'<silent> O O<c-r>=<SID>CheckAlign(line(''.''))."\<lt>END>"<CR>'
" Ok.. now re-evaluate the = re-indented section
" The only way I can think to do this is to remap the =
" so that it calls the original, then checks all the indents.
exe 'map '.s:buff_map.'<silent> <expr> = <SID>SetupEqual()'
fun! s:SetupEqual()
set operatorfunc=CtabRedoIndent
" Call the operator func so we get the range
return 'g@'
endfun
fun! CtabRedoIndent(type,...)
set operatorfunc=
let ln=line("'[")
let lnto=line("']")
" Do the original equals
norm! '[=']
if ! &et
" Then check the alignment.
while ln <= lnto
silent call s:CheckAlign(ln)
let ln+=1
endwhile
endif
endfun
endif
" Retab the indent of a file - ie only the first nonspace
fun! s:RetabIndent( bang, firstl, lastl, tab )
let checkspace=((!&expandtab)? "^\<tab>* ": "^ *\<tab>")
let l = a:firstl
let force= a:tab != '' && a:tab != 0 && (a:tab != &tabstop)
let checkalign = ( &expandtab || !(&autoindent || &indentexpr || &cindent)) && (!exists('g:ctab_disable_checkalign') || g:ctab_disable_checkalign==0)
let newtabstop = (force?(a:tab):(&tabstop))
while l <= a:lastl
let txt=getline(l)
let store=0
if a:bang == '!' && txt =~ '\s\+$'
let txt=substitute(txt,'\s\+$','','')
let store=1
endif
if force || txt =~ checkspace
let i=indent(l)
let tabs= (&expandtab ? (0) : (i / newtabstop))
let spaces=(&expandtab ? (i) : (i % newtabstop))
let txtindent=repeat("\<tab>",tabs).repeat(' ',spaces)
let store = 1
let txt=substitute(txt,'^\s*',txtindent,'')
endif
if store
call setline(l, txt )
if checkalign
call s:CheckAlign(l)
endif
endif
let l=l+1
endwhile
if newtabstop != &tabstop | let &tabstop = newtabstop | endif
endfun
" Retab the indent of a file - ie only the first nonspace.
" Optional argument specified the value of the new tabstops
" Bang (!) causes trailing whitespace to be gobbled.
com! -nargs=? -range=% -bang -bar RetabIndent call <SID>RetabIndent(<q-bang>,<line1>, <line2>, <q-args> )
" vim: sts=2 sw=2 et
|