dotfiles

My collection of dotfiles
git clone git://git.noxz.tech/dotfiles
Log | Files | Refs

commit 824657b025fb86bd1dc1eda9a15ff7f76d6ef90d
parent 8dcfdc187f8d657937adfdb3e2c11e21d181f7e1
Author: Chris Noxz <chris@noxz.tech>
Date:   Sat, 28 Mar 2020 22:03:14 +0100

[vim] add smart tabs functionality to vim

Diffstat:
Dvim/.config/vim/plugins/vim-ctab/vim-ctab.vim | 349-------------------------------------------------------------------------------
Mvim/.config/vim/vimrc | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
2 files changed, 71 insertions(+), 357 deletions(-)

diff --git a/vim/.config/vim/plugins/vim-ctab/vim-ctab.vim b/vim/.config/vim/plugins/vim-ctab/vim-ctab.vim @@ -1,349 +0,0 @@ -" 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 diff --git a/vim/.config/vim/vimrc b/vim/.config/vim/vimrc @@ -141,27 +141,90 @@ set autoindent " copy indent from current line to next set shiftround " round indent of multiple of 'shiftwidth' " }}} +" smart tab {{{ +" based on smarttabs.vim and changed to work with completion +function! SmartIndent(to) + let line = getline('.') + let tabs = matchstr(line, '^\t*') + if (line =~ '^\t* \+') + let nspaces = strlen(matchstr(line, '^\t* \+')) - strlen(tabs) + else + let line = matchstr(line, '[^\t]*$') + let nspaces = -1 + for char in a:to + let nspaces = nspaces == -1 + \ ? strridx(line, char) + 1 + \ : nspaces + endfor + if (nspaces == -1) | return "\<CR>" | endif + endif + let spaces = "" + for space in range(nspaces) + let spaces = spaces . " " + endfor + return "\<CR>\<C-W>".tabs.spaces +endfunction + +function! SmartIndentOn(chars, to) + let lastchar = strpart(getline('.'), col('.') - 2) + for char in a:chars + if (lastchar == char) | return SmartIndent(a:to) | endif + endfor + return "\<CR>" +endfunction + +function! SmartIndentUnless(chars, to) + let lastchar = strpart(getline('.'), col('.') - 2) + for char in a:chars + if (lastchar == char) | return "\<CR>" | endif + endfor + return SmartIndent(a:to) +endfunction + +function! SmartTab() + let before = strpart(getline('.'), 0, col('.') - 1) + if (before =~ '^\t*$') | return "\t" | 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) +endfunction + +function! SmartDelete() + return "\<BS>" +endfunction + +" language specifics +autocmd BufEnter *.c,*.cpp inoremap <CR> + \ <C-R>=SmartIndentUnless([')', '{'], ['('])<CR> +autocmd BufEnter *.py inoremap <CR> + \ <C-R>=SmartIndentOn([','], ['(', '[', '{'])<CR> +" }}} + " autocomplete {{{ -function! Tab_Or_Complete(reverse) - if col('.')>1 && strpart( getline('.'), col('.')-2, 3 ) =~ '^\w' - if (a:reverse) +function! TabOrComplete(shift) + if col('.')>1 && strpart(getline('.'), col('.')-2, 3 ) =~ '^\w' + if (a:shift) return "\<C-p>" else return "\<C-n>" endif else - if (a:reverse) - return "\<C-d>" + if (a:shift) + return "\t" " force tab on shift else - return "\<Tab>" + return SmartTab() elseif endif endfunction set dictionary+=/usr/share/dict/words set complete-=k -inoremap <silent> <tab> <C-r>=Tab_Or_Complete(0)<CR> -inoremap <silent> <S-tab> <C-r>=Tab_Or_Complete(1)<CR> +inoremap <silent> <tab> <C-r>=TabOrComplete(0)<CR> +inoremap <silent> <S-tab> <C-r>=TabOrComplete(1)<CR> +inoremap <silent> <bs> <C-r>=SmartDelete()<CR> if (&ft=='text' || &ft=='md' || &ft=='') set complete+=k endif