Language:
Vim/Vim script     Change language:
Pastebin: 116929
Author: Frogonwheels
Subject: ctab.vim
Created: 2009-07-01 00:09:32
Download and save
Toggle line numbers
1" Intelligent Indent 
2" Author: Michael Geddes < vimmer at frog dot wheelycreek dot net > 
3" Version: 2.3 
4" Last Modified: 1 July 2009 
5" 
6" Histroy: 
7"   1.0: - Added RetabIndent command - similar to :retab, but doesn't cause 
8"         internal tabs to be modified. 
9"   1.1: - Added support for backspacing over spaced tabs 'smarttab' style 
10"        - Clean up the look of it by blanking the :call 
11"        - No longer a 'filetype' plugin by default. 
12"   1.2: - Interactions with 'smarttab' were causing problems. Now fall back to 
13"          vim's 'smarttab' setting when inserting 'indent' tabs. 
14"        - Fixed compat with digraphs (which were getting swallowed) 
15"        - Made <BS> mapping work with the 'filetype' plugin mode. 
16"        - Make CTabAlignTo() public. 
17"   1.3: - Fix removing trailing spaces with RetabIndent! which was causing 
18"          initial indents to disappear. 
19"   1.4: - Fixed Backspace tab being off by 1 
20"   2.0: - Add support for alignment whitespace for mismatched brackets to be spaces. 
21"   2.1: - Fix = operator 
22" 
23 
24" This is designed as a filetype plugin (originally a 'Buffoptions.vim' script). 
25" 
26" The aim of this script is to be able to handle the mode of tab usage which 
27" distinguishes 'indent' from 'alignment'.  The idea is to use <tab> 
28" characters only at the beginning of lines. 
29" 
30" This means that an individual can use their own 'tabstop' settings for the 
31" indent level, while not affecting alignment. 
32" 
33" The one caveat with this method of tabs is that you need to follow the rule 
34" that you never 'align' elements that have different 'indent' levels. 
35" 
36" :RetabIndent[!] [tabstop] 
37"     This is similar to the :retab command, with the exception that it 
38"     affects all and only whitespace at the start of the line, changing it to 
39"     suit your current (or new) tabstop and expandtab setting. 
40"     With the bang (!) at the end, the command also strips trailing 
41"     whitespace. 
42" 
43"  CTabAlignTo(n) 
44"     'Tab' to the n'th column from the start of the indent. 
45 
46" g:ctab_filetype_maps 
47"   set this to true if script used as a filetype plugin 
48" g:ctab_disable_checkalign 
49"   set this to true to disable re-check of alignment 
50" g:ctab_enable_default_filetype_maps 
51"   disable the filetype specific maps 
52" g:ctab_disable_tab_maps 
53"   disable the (original) tab mappings 
54 
55if  exists('g:ctab_filetype_maps') && g:ctab_filetype_maps 
56  let s:buff_map=' <buffer> ' 
57else 
58  let s:buff_map='' 
59endif 
60 
61if exists('g:ctab_enable_default_filetype_maps') && ctab_enable_default_filetype_maps 
62  if s:buff_map != '' 
63    if (&filetype =~ '^\(cpp\|idl\)$' ) 
64      imap <silent> <buffer> <expr> <m-;> CTabAlignTo(20).'//' 
65      imap <silent> <buffer> <expr> <m-s-;> CTabAlignTo(30).'//' 
66      imap <silent> <buffer> ยบ <m-s-;> 
67    elseif &filetype == 'c' 
68      imap <expr> <silent> <buffer> <m-;> CTabAlignTo(10).'/*  */<left><left><left>
69    endif 
70  else 
71    au FileType cpp,idl imap <expr> <silent> <buffer> <m-;> CTabAlignTo(20).'//' 
72    au FileType cpp,idl imap <expr> <silent> <buffer> <m-:> CTabAlignTo(30).'//' 
73    au FileType c imap <expr> <silent> <buffer> <m-;> CTabAlignTo(10).'/*  */<left><left>
74  endif 
75endif 
76 
77if !exists('g:ctab_disable_tab_maps') || ! g:ctab_disable_tab_maps 
78  exe  'imap '.s:buff_map.'<silent> <expr> <tab> <SID>InsertSmartTab()' 
79  exe  'inoremap '.s:buff_map.'<silent> <expr> <BS> <SID>DoSmartDelete()."\<BS>"' 
80endif 
81 
82"exe 'imap '.s:buff_map.'<silent> <expr> <BS> <SID>KeepDelLine()."\<BS>" 
83 
84"exe 'imap '.s:buff_map.'<silent> <expr> <c-d> :call <SID>SmartDeleteTab()<CR>' 
85"exe 'imap '.s:buff_map.'<silent> <c-t> <SID>SmartInsertTab()' 
86 
87fun! s:SmartDeleteTab() 
88  let curcol=col('.')-&sw 
89  let origtxt=getline('.') 
90  let repl=matchstr(origtxt,'^\s\{-}\%'.(&sw+2)."v') 
91  if repl == '' then 
92    return "\<c-o>".':s/  *\zs  /'.repeat(' ',(&ts-&sw)).'/'."\<CR>\<c-o>".curcol.'|' 
93  else 
94    return "\<c-o>".':s/^\s\{-}\%'.(&sw+1)."v//\<CR>\<c-o>".curcol."|" 
95  end 
96 
97endfun 
98 
99" Insert a smart tab. 
100fun! s:InsertSmartTab() 
101  " Clear the status 
102  echo '' 
103  if strpart(getline('.'),0,col('.')-1) =~'^\s*$' | return "\<Tab>" | endif 
104 
105  let sts=exists("b:insidetabs")?(b:insidetabs):((&sts==0)?&sw:&sts) 
106  let sp=(virtcol('.') % sts) 
107  if sp==0 | let sp=sts | endif 
108  return strpart("                  ",0,1+sts-sp) 
109endfun 
110 
111 
112" Do a smart delete. 
113" The <BS> is included at the end so that deleting back over line ends 
114" works as expected. 
115fun! s:DoSmartDelete() 
116  " Clear the status 
117  "echo '' 
118  let uptohere=strpart(getline('.'),0,col('.')-1) 
119  " If at the first part of the line, fall back on defaults... or if the 
120  " preceding character is a <TAB>, then similarly fall back on defaults. 
121  " 
122  let lastchar=matchstr(uptohere,'.$') 
123  if lastchar == "\<tab>" || uptohere =~ '^\s*$' | return '' | endif        " Simple cases 
124  if lastchar != ' ' | return ((&digraph)?("\<BS>".lastchar): '')  | endif  " Delete non space at end / Maintain digraphs 
125 
126  " Work out how many tabs to use 
127  let sts=(exists("b:insidetabs")?(b:insidetabs):((&sts==0)?(&sw):(&sts))) 
128 
129  let ovc=virtcol('.')              " Find where we are 
130  let sp=(ovc % sts)                " How many virtual characters to delete 
131  if sp==0 | let sp=sts | endif     " At least delete a whole tabstop 
132  let vc=ovc-sp                     " Work out the new virtual column 
133  " Find how many characters we need to delete (using \%v to do virtual column 
134  " matching, and making sure we don't pass an invalid value to vc) 
135  let uthlen=strlen(uptohere) 
136  let bs= uthlen-((vc<1)?0:(  match(uptohere,'\%'.(vc-1).'v'))) 
137  let uthlen=uthlen-bs 
138  " echo 'ovc = '.ovc.' sp = '.sp.' vc = '.vc.' bs = '.bs.' uthlen='.uthlen 
139  if bs <= 0 | return  '' | endif 
140 
141  " Delete the specifed number of whitespace characters up to the first non-whitespace 
142  let ret='' 
143  let bs=bs-1 
144  if uptohere[uthlen+bs] !~ '\s'| return '' | endif 
145  while bs>=-1 
146    let bs=bs-1 
147    if uptohere[uthlen+bs] !~ '\s' | break | endif 
148    let ret=ret."\<BS>" 
149  endwhile 
150  return ret 
151endfun 
152 
153fun! s:Column(line) 
154  let c=0 
155  let i=0 
156  let len=strlen(a:line) 
157  while i< len 
158    if a:line[i]=="\<tab>" 
159      let c=(c+&tabstop) 
160      let c=c-(c%&tabstop) 
161    else 
162      let c=c+1 
163    endif 
164    let i=i+1 
165  endwhile 
166  return c 
167endfun 
168fun! s:StartColumn(lineNo) 
169  return s:Column(matchstr(getline(a:lineNo),'^\s*')) 
170endfun 
171 
172fun! CTabAlignTo(n) 
173  let co=virtcol('.') 
174  let ico=s:StartColumn('.')+a:n 
175  if co>ico 
176    let ico=co 
177  endif 
178  let spaces=ico-co 
179  let spc='' 
180  while spaces > 0 
181    let spc=spc." " 
182    let spaces=spaces-1 
183  endwhile 
184  return spc 
185endfun 
186 
187if ! exists('g:ctab_disable_checkalign') || g:ctab_disable_checkalign==0 
188  " Check the alignment of line. 
189  " Used in the case where some alignment whitespace is required .. like for unmatched brackets. 
190  fun! s:CheckAlign(line) 
191    if &expandtab || !(&autoindent || &indentexpr || &cindent) 
192      return '' 
193    endif 
194 
195    let tskeep=&ts 
196    let swkeep=&sw 
197    try 
198      set ts=50 
199      set sw=50 
200      if &indentexpr != '' 
201        let v:lnum=a:line 
202        sandbox exe 'let inda='.&indentexpr 
203        if inda == -1 
204          let inda=indent(a:line-1) 
205        endif 
206      elseif &cindent 
207        let inda=cindent(a:line) 
208      elseif &lisp 
209        let inda=lispindent(a:line) 
210      elseif &autoindent 
211        let inda=indent(a:line) 
212      elseif &smarttab 
213        return '' 
214      endif 
215    finally 
216      let &ts=tskeep 
217      let &sw=swkeep 
218    endtry 
219    let indatabs=inda / 50 
220    let indaspace=inda % 50 
221    let indb=indent(a:line) 
222    if indatabs*&tabstop + indaspace == indb 
223      let txtindent=repeat("\<Tab>",indatabs).repeat(' ',indaspace) 
224      call setline(a:line, substitute(getline(a:line),'^\s*',txtindent,'')) 
225    endif 
226    return '' 
227  endfun 
228  " Get the spaces at the end of the  indent correct. 
229  " This is trickier than it should be, but this seems to work. 
230  exe 'inoremap '.s:buff_map.'<silent> <CR> <CR><c-r>=<SID>CheckAlign(line(''.''))."\<lt>END>"<CR>' 
231  exe 'nnoremap '.s:buff_map.'<silent> o o<c-r>=<SID>CheckAlign(line(''.''))."\<lt>END>"<CR>' 
232  exe 'nnoremap '.s:buff_map.'<silent> O O<c-r>=<SID>CheckAlign(line(''.''))."\<lt>END>"<CR>' 
233 
234  " Ok.. now re-evaluate the = re-indented section 
235 
236  " The only way I can think to do this is to remap the = 
237  " so that it calls the original, then checks all the indents. 
238  exe 'map '.s:buff_map.'<silent> <expr> = <SID>SetupEqual()' 
239  fun! s:SetupEqual() 
240    set operatorfunc=CtabRedoIndent 
241    " Call the operator func so we get the range 
242    return 'g@' 
243  endfun 
244 
245  fun! CtabRedoIndent(type,...) 
246    set operatorfunc
247    let ln=line("'[") 
248    let lnto=line("']") 
249    " Do the original equals 
250    norm! '[='] 
251 
252    if ! &et 
253      " Then check the alignment. 
254      while ln <= lnto 
255        silent call s:CheckAlign(ln) 
256        let ln+=1 
257      endwhile 
258    endif 
259  endfun 
260endif 
261 
262" Retab the indent of a file - ie only the first nonspace 
263fun! s:RetabIndent( bang, firstl, lastl, tab ) 
264  let checkspace=((!&expandtab)? "^\<tab>* ": "^ *\<tab>") 
265  let l = a:firstl 
266  let force= a:tab != '' && a:tab != 0 && (a:tab != &tabstop) 
267  let checkalign = &expandtab || !(&autoindent || &indentexpr || &cindent) ! exists('g:ctab_disable_checkalign') || g:ctab_disable_checkalign==0 
268  let newtabstop = (force?(a:tab):(&tabstop)) 
269  while l <= a:lastl 
270    let txt=getline(l) 
271    let store=0 
272    if a:bang == '!' && txt =~ '\s\+$' 
273      let txt=substitute(txt,'\s\+$','','') 
274      let store=1 
275    endif 
276    if force || txt =~ checkspace 
277      let i=indent(l) 
278      let tabs= (&expandtab ? (0) : (i / newtabstop)) 
279      let spaces=(&expandtab ? (i) : (i % newtabstop)) 
280      let txtindent=repeat("\<tab>",tabs).repeat(' ',spaces) 
281      let store = 1 
282      let txt=substitute(txt,'^\s*',txtindent,'') 
283    endif 
284    if store 
285      call setline(l, txt ) 
286      if checkalign 
287        call s:CheckAlign(l) 
288      endif 
289    endif 
290 
291    let l=l+1 
292  endwhile 
293  if newtabstop != &tabstop | let &tabstop = newtabstop | endif 
294endfun 
295 
296 
297" Retab the indent of a file - ie only the first nonspace. 
298"   Optional argument specified the value of the new tabstops 
299"   Bang (!) causes trailing whitespace to be gobbled. 
300com! -nargs=? -range=% -bang -bar RetabIndent call <SID>RetabIndent(<q-bang>,<line1>, <line2>, <q-args>
301 
302 
303" vim: sts=2 sw=2 et 
304 
Download and save
Toggle line numbers
Thread:
[116929] ctab.vim by Frogonwheels at 2009-07-01 00:09:32
Tip: Click the line numbers to toggle highliting on that line.

Paste followup:

Language:
Author:
Subject:


    Tabstop:     bigger biggest
Note: You can prefix a line with "@@@" to highlight it.