CLI config/dotfiles
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

349 lines
11 KiB

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