Wikia

Vim Tips Wiki

Changes: Smart mapping for tab completion

Edit

Back to page

(add todo)
(Remove html character entities)
Line 12: Line 12:
 
|category2=
 
|category2=
 
}}
 
}}
I'm used to complete words with <tab>, however when editing source I can't just map that to Vim keyword completion because I sometime need to insert real tabs, since it mostly happen when at the beginning of the line or after a ; and before a one line comma (java, c++ or perl anyone...)
+
I'm used to complete words with <tab>, however when editing source I can't just map that to Vim keyword completion because I sometime need to insert real tabs, since it mostly happen when at the beginning of the line or after a ; and before a one line comma (java, c++ or perl anyone)
   
 
I've come to find the following really useful.
 
I've come to find the following really useful.
   
This is how you can map the &lt;tab&gt; key in insert mode while still being able to use it when at the start of a line or when the preceding char is not a keyword character.
+
This is how you can map the <tab> key in insert mode while still being able to use it when at the start of a line or when the preceding char is not a keyword character.
   
 
in a script file in a plugin directory or in your .vimrc file:
 
in a script file in a plugin directory or in your .vimrc file:
   
first define a function which returns a &lt;tab&gt; or a &lt;C-N&gt; depending on the context:
+
first define a function which returns a <tab> or a <C-N> depending on the context:
   
 
<pre>
 
<pre>
 
function! InsertTabWrapper(direction)
 
function! InsertTabWrapper(direction)
let col = col('.') - 1
+
let col = col('.') - 1
if !col || getline('.')[col - 1] !~ '\k'
+
if !col || getline('.')[col - 1] !~ '\k'
return "\&lt;tab&gt;"
+
return "\<tab>"
elseif "backward" == a:direction
+
elseif "backward" == a:direction
return "\&lt;c-p&gt;"
+
return "\<c-p>"
else
+
else
return "\&lt;c-n&gt;"
+
return "\<c-n>"
endif
+
endif
 
endfunction
 
endfunction
 
</pre>
 
</pre>
Line 37: Line 37:
 
Then define the appropriate mappings:
 
Then define the appropriate mappings:
   
inoremap &lt;tab&gt; &lt;c-r&gt;=InsertTabWrapper ("forward")&lt;cr&gt;
+
inoremap <tab> <c-r>=InsertTabWrapper ("forward")<CR>
inoremap &lt;s-tab&gt; &lt;c-r&gt;=InsertTabWrapper ("backward")&lt;cr&gt;
+
inoremap &lt;s-tab> <c-r>=InsertTabWrapper ("backward")<CR>
   
The trick here is the use of the &lt;c-r&gt;= in insert mode to be able to call your function without leaving insert mode. See {{help|i_CTRL-R}}.
+
The trick here is the use of the <tt><c-r>=</tt> in insert mode to be able to call your function without leaving insert mode. See {{help|i_CTRL-R}}.
   
Benoit
 
 
==Comments==
 
==Comments==
 
{{todo}}
 
{{todo}}
Line 56: Line 55:
   
 
I could type :
 
I could type :
ab&lt;TAB&gt;
+
ab<TAB>
 
and it would expand to abs(VALUE)
 
and it would expand to abs(VALUE)
   
 
<pre>
 
<pre>
 
function! InsertTabWrapper(direction)
 
function! InsertTabWrapper(direction)
let oldisk=&amp;isk "save the iskeyword options
+
let oldisk=&isk "save the iskeyword options
set isk+=(,),, "add '(' ')' and ',' character
+
set isk+=(,),, "add '(' ')' and ',' character
let col = col('.') - 1
+
let col = col('.') - 1
if !col || getline('.')[col - 1] !~ '\k'
+
if !col || getline('.')[col - 1] !~ '\k'
return "\&lt;tab&gt;"
+
return "\<tab>"
elseif "backward" == a:direction
+
elseif "backward" == a:direction
return "\&lt;c-n&gt;"
+
return "\<c-n>"
else
+
else
return "\&lt;c-p&gt;"
+
return "\<c-p>"
endif
+
endif
set &amp;isk=oldisk "restore the iskeyword options
+
set &isk=oldisk "restore the iskeyword options
endfunction
+
endfunction
 
</pre>
 
</pre>
   
Line 78: Line 77:
 
<pre>
 
<pre>
 
fun! Iskcompletion()
 
fun! Iskcompletion()
let oldisk=&amp;isk
+
let oldisk=&isk
set isk+=(,)
+
set isk+=(,)
normal &lt;C-P&gt;
+
normal <C-P>
set &amp;isk=oldisk
+
set &isk=oldisk
 
endfun
 
endfun
 
</pre>
 
</pre>
Line 90: Line 89:
 
e.g.
 
e.g.
   
cat /usr/share/vim/vim61/syntax/sh.vim | grep keyword | grep -v nextgroup | awk '{ $1=""; $2=""; $3=""; print}' | perl -pe 's/\s+/\n/g' | grep -v contained | grep -v '^$' | sort | uniq &gt; /home/user/.vim/dict/sh.dict
+
cat /usr/share/vim/vim61/syntax/sh.vim | grep keyword | grep -v nextgroup | awk '{ $1=""; $2=""; $3=""; print}' | perl -pe 's/\s+/\n/g' | grep -v contained | grep -v '^$' | sort | uniq > /home/user/.vim/dict/sh.dict
   
 
the above command will probably need tweaking depending on the syntax file.
 
the above command will probably need tweaking depending on the syntax file.
Line 102: Line 101:
 
" load the dictionary according to syntax
 
" load the dictionary according to syntax
 
:au BufReadPost * if exists("b:current_syntax")
 
:au BufReadPost * if exists("b:current_syntax")
:au BufReadPost * let &amp;dictionary = substitute("~/.vim/dict/FT.dict", "FT", b:current_syntax, "")
+
:au BufReadPost * let &dictionary = substitute("~/.vim/dict/FT.dict", "FT", b:current_syntax, "")
 
:au BufReadPost * endif
 
:au BufReadPost * endif
 
</pre>
 
</pre>
Line 113: Line 112:
 
" toggle tab completion
 
" toggle tab completion
 
function! TabCompletion()
 
function! TabCompletion()
if mapcheck("\&lt;tab&gt;", "i") != ""
+
if mapcheck("\<tab>", "i") != ""
:iunmap &lt;tab&gt;
+
:iunmap <tab>
:iunmap &lt;s-tab&gt;
+
:iunmap &lt;s-tab>
:iunmap &lt;c-tab&gt;
+
:iunmap <c-tab>
echo "tab completion off"
+
echo "tab completion off"
else
+
else
:imap &lt;tab&gt; &lt;c-n&gt;
+
:imap <tab> <c-n>
:imap &lt;s-tab&gt; &lt;c-p&gt;
+
:imap &lt;s-tab> <c-p>
:imap &lt;c-tab&gt; &lt;c-x&gt;&lt;c-l&gt;
+
:imap <c-tab> <c-x><c-l>
echo "tab completion on"
+
echo "tab completion on"
endif
+
endif
 
endfunction
 
endfunction
+
map <Leader>tc :call TabCompletion()<CR>
map &lt;Leader&gt;tc :call TabCompletion()&lt;cr&gt;
 
 
</pre>
 
</pre>
   
Line 131: Line 130:
 
Have to chime in... here's my silly attempt at making the Tab key more
 
Have to chime in... here's my silly attempt at making the Tab key more
 
intelligent. The function below (derived work of course) allows one to get
 
intelligent. The function below (derived work of course) allows one to get
the original effect of &lt;Tab&gt; if the previous character is a space. I.e.,
+
the original effect of <Tab> if the previous character is a space. I.e.,
 
you can type:
 
you can type:
   
foo&lt;Space&gt;&lt;Tab&gt;
+
foo<Space><Tab>
   
and it'll end up as foo&lt;Tab&gt; with the space deleted. This is *not* the same
+
and it'll end up as foo<Tab> with the space deleted. This is *not* the same
as &lt;C-v&gt;&lt;Tab&gt;, which would always insert a real Tab character rather than
+
as <C-v><Tab>, which would always insert a real Tab character rather than
 
honoring 'softtabstop'.
 
honoring 'softtabstop'.
   
 
<pre>
 
<pre>
 
" Intelligent tab completion
 
" Intelligent tab completion
inoremap &lt;silent&gt; &lt;Tab&gt; &lt;C-r&gt;=&lt;SID&gt;InsertTabWrapper(1)&lt;CR&gt;
+
inoremap <silent> <Tab> <C-r>=<SID>InsertTabWrapper(1)<CR>
inoremap &lt;silent&gt; &lt;S-Tab&gt; &lt;C-r&gt;=&lt;SID&gt;InsertTabWrapper(-1)&lt;CR&gt;
+
inoremap <silent> &lt;S-Tab> <C-r>=<SID>InsertTabWrapper(-1)<CR>
+
function! <SID>InsertTabWrapper(direction)
function! &lt;SID&gt;InsertTabWrapper(direction)
+
let idx = col('.') - 1
let idx = col('.') - 1
+
let str = getline('.')
let str = getline('.')
+
if a:direction > 0 && idx >= 2 && str[idx - 1] == ' '
if a:direction &gt; 0 &amp;&amp; idx &gt;= 2 &amp;&amp; str[idx - 1] == ' '
+
\&& str[idx - 2] =~? '[a-z]'
\&amp;&amp; str[idx - 2] =~? '[a-z]'
+
if &softtabstop && idx % &softtabstop == 0
if &amp;softtabstop &amp;&amp; idx % &amp;softtabstop == 0
+
return "\<BS>\<Tab>\<Tab>"
return "\&lt;BS&gt;\&lt;Tab&gt;\&lt;Tab&gt;"
+
else
else
+
return "\<BS>\<Tab>"
return "\&lt;BS&gt;\&lt;Tab&gt;"
+
endif
endif
+
elseif idx == 0 || str[idx - 1] !~? '[a-z]'
elseif idx == 0 || str[idx - 1] !~? '[a-z]'
+
return "\<Tab>"
return "\&lt;Tab&gt;"
+
elseif a:direction > 0
elseif a:direction &gt; 0
+
return "\<C-p>"
return "\&lt;C-p&gt;"
+
else
else
+
return "\<C-n>"
return "\&lt;C-n&gt;"
+
endif
endif
 
 
endfunction
 
endfunction
 
</pre>
 
</pre>
Line 167: Line 166:
 
What about this to dump all the syntax files into properly formatted dict files
 
What about this to dump all the syntax files into properly formatted dict files
   
for i in /usr/share/vim/syntax/*;do cat $i | grep keyword | grep -v nextgroup | awk '{ $1=""; $2=""; $3=""; print}' | perl -pe 's/\s+/\n/g' | grep -v contained | grep -v '^$' | sort | uniq&gt;~/.vim/dict/`basename $i .vim`.dict;done
+
for i in /usr/share/vim/syntax/*;do cat $i | grep keyword | grep -v nextgroup | awk '{ $1=""; $2=""; $3=""; print}' | perl -pe 's/\s+/\n/g' | grep -v contained | grep -v '^$' | sort | uniq>~/.vim/dict/`basename $i .vim`.dict;done
   
 
----
 
----
One small suggestion for daniel elstner's script: replace '[a-z]' with '[^&lt;space&gt;&lt;tab&gt;]'.
+
One small suggestion for daniel elstner's script: replace '[a-z]' with '[^<Space><tab>]'.
   
 
----
 
----
 
My derivation of this wonderful tip: (aimed at C++ // comments)
 
My derivation of this wonderful tip: (aimed at C++ // comments)
   
This as it happens has nothing to do with &lt;tab&gt; nor completion.
+
This as it happens has nothing to do with <tab> nor completion.
   
 
<pre>
 
<pre>
Line 181: Line 180:
 
" Feral This is inspired by how multi-edit does things.
 
" Feral This is inspired by how multi-edit does things.
 
let DaLine = getline('.')
 
let DaLine = getline('.')
if match(DaLine, '\c^\s*//$') &gt; -1
+
if match(DaLine, '\c^\s*//$') > -1
 
" [Feral:213/03@04:59] Just eat the // chars
 
" [Feral:213/03@04:59] Just eat the // chars
" return "\&lt;bs&gt;\&lt;bs&gt;"
+
" return "\<BS>\<BS>"
 
"
 
"
 
"This method:
 
"This method:
 
" // -[Feral:213/03@04:59]----------------------------------------------
 
" // -[Feral:213/03@04:59]----------------------------------------------
 
" //
 
" //
" 75 is the column we wish to not go beyond.
+
" 75 is the column we wish to not go beyond.
" 2 = ' -'
+
" 2 = ' -'
" 20 = my time stamp: [Feral:213/03@04:52]
+
" 20 = my time stamp: [Feral:213/03@04:52]
" 1 = '-'
+
" 1 = '-'
let AmountForFiller = 75 - (virtcol('.')+2+20+1)
+
let AmountForFiller = 75 - (virtcol('.')+2+20+1)
let Filler = ""
+
let Filler = ""
while strlen(Filler) &lt; AmountForFiller
+
while strlen(Filler) < AmountForFiller
let Filler = Filler.'-'
+
let Filler = Filler.'-'
endwhile
+
endwhile
let DaLine = " -[Feral:".strftime('%j/%y@%H:%M')."]-".Filler."\&lt;cr&gt;"
+
let DaLine = " -[Feral:".strftime('%j/%y@%H:%M')."]-".Filler."\<CR>"
return DaLine
+
return DaLine
elseif match(DaLine, '\c^\s*//\s$') &gt; -1
+
elseif match(DaLine, '\c^\s*//\s$') > -1
return "\&lt;bs&gt;\&lt;bs&gt;\&lt;bs&gt;"
+
return "\<BS>\<BS>\<BS>"
 
else
 
else
return "\&lt;cr&gt;"
+
return "\<CR>"
 
endif
 
endif
 
endfunction
 
endfunction
   
inoremap &lt;cr&gt; &lt;c-r&gt;=SpecialCR()&lt;cr&gt;
+
inoremap <CR> <c-r>=SpecialCR()<CR>
 
</pre>
 
</pre>
   
This will make &lt;cr&gt; eat C++ line comments when they are the only think on the line, or more precisely:
+
This will make <CR> eat C++ line comments when they are the only think on the line, or more precisely:
 
"//\s" will be get 3 backspaces which should erase it and "//" will get a simple timestamp separator.
 
"//\s" will be get 3 backspaces which should erase it and "//" will get a simple timestamp separator.
   
Line 222: Line 221:
 
" Remap TAB to keyword completion
 
" Remap TAB to keyword completion
 
function! InsertTabWrapper(direction)
 
function! InsertTabWrapper(direction)
let col = col('.') - 1
+
let col = col('.') - 1
if !col || getline('.')[col - 1] !~ '\k'
+
if !col || getline('.')[col - 1] !~ '\k'
return "\&lt;tab&gt;"
+
return "\<tab>"
elseif "backward" == a:direction
+
elseif "backward" == a:direction
return "\&lt;c-p&gt;"
+
return "\<c-p>"
elseif "forward" == a:direction
+
elseif "forward" == a:direction
return "\&lt;c-n&gt;"
+
return "\<c-n>"
else
+
else
return "\&lt;c-x&gt;\&lt;c-k&gt;"
+
return "\<c-x>\<c-k>"
endif
+
endif
 
endfunction
 
endfunction
   
inoremap &lt;tab&gt; &lt;c-r&gt;=InsertTabWrapper ("forward")&lt;cr&gt;
+
inoremap <tab> <c-r>=InsertTabWrapper ("forward")<CR>
inoremap &lt;s-tab&gt; &lt;c-r&gt;=InsertTabWrapper ("backward")&lt;cr&gt;
+
inoremap &lt;s-tab> <c-r>=InsertTabWrapper ("backward")<CR>
inoremap &lt;c-tab&gt; &lt;c-r&gt;=InsertTabWrapper ("startkey")&lt;cr&gt;
+
inoremap <c-tab> <c-r>=InsertTabWrapper ("startkey")<CR>
   
 
" toggle tab completion
 
" toggle tab completion
 
function! ToggleTabCompletion()
 
function! ToggleTabCompletion()
if mapcheck("\&lt;tab&gt;", "i") != ""
+
if mapcheck("\<tab>", "i") != ""
:iunmap &lt;tab&gt;
+
:iunmap <tab>
:iunmap &lt;s-tab&gt;
+
:iunmap &lt;s-tab>
:iunmap &lt;c-tab&gt;
+
:iunmap <c-tab>
echo "tab completion off"
+
echo "tab completion off"
else
+
else
:imap &lt;tab&gt; &lt;c-n&gt;
+
:imap <tab> <c-n>
:imap &lt;s-tab&gt; &lt;c-p&gt;
+
:imap &lt;s-tab> <c-p>
:imap &lt;c-tab&gt; &lt;c-x&gt;&lt;c-l&gt;
+
:imap <c-tab> <c-x><c-l>
echo "tab completion on"
+
echo "tab completion on"
endif
+
endif
 
endfunction
 
endfunction
   
map &lt;Leader&gt;tc :call ToggleTabCompletion()&lt;cr&gt;
+
map <Leader>tc :call ToggleTabCompletion()<CR>
   
 
" tell complete to look in the dictionary
 
" tell complete to look in the dictionary
Line 260: Line 259:
 
" load the dictionary according to syntax
 
" load the dictionary according to syntax
 
:au BufReadPost * if exists("b:current_syntax")
 
:au BufReadPost * if exists("b:current_syntax")
:au BufReadPost * let &amp;dictionary = substitute("C:\\vim\\vimfiles\\dict\\FT.dict", "FT", b:current_syntax, "")
+
:au BufReadPost * let &dictionary = substitute("C:\\vim\\vimfiles\\dict\\FT.dict", "FT", b:current_syntax, "")
 
:au BufReadPost * endif
 
:au BufReadPost * endif
 
</pre>
 
</pre>
Line 273: Line 272:
   
 
----
 
----
your tab-completion tip is very nice. Although my kde-konsole didn't recognize the shift-tab, &lt;s-tab&gt; combination.
+
your tab-completion tip is very nice. Although my kde-konsole didn't recognize the shift-tab, &lt;s-tab> combination.
   
 
I solved this by adding the following line to /usr/share/apps/konsole/linux.keytab
 
I solved this by adding the following line to /usr/share/apps/konsole/linux.keytab
Line 285: Line 284:
   
 
<pre>
 
<pre>
&gt;" load the dictionary according to syntax
+
>" load the dictionary according to syntax
&gt;:au BufReadPost * if exists("b:current_syntax")
+
>:au BufReadPost * if exists("b:current_syntax")
&gt;:au BufReadPost * let &amp;dictionary = substitute("C:\\vim\\vimfiles\\dict\\FT.dict", "FT", b:current_syntax, "")
+
>:au BufReadPost * let &dictionary = substitute("C:\\vim\\vimfiles\\dict\\FT.dict", "FT", b:current_syntax, "")
&gt;:au BufReadPost * endif
+
>:au BufReadPost * endif
 
</pre>
 
</pre>
   
 
----
 
----
 
In {{help|ins-completion}} you can find following settings.
 
In {{help|ins-completion}} you can find following settings.
  +
<pre>
  +
function! CleverTab()
  +
if strpart( getline('.'), 0, col('.')-1 ) =~ '^\s*$'
  +
return "\<Tab>"
  +
else
  +
return "\<C-N>"
  +
endfunction
  +
inoremap <Tab> <C-R>=CleverTab()<CR>
  +
</pre>
   
function! CleverTab()
+
It works fine, but this never uses "omni-completion" introduced from Vim 7.
if strpart( getline('.'), 0, col('.')-1 ) =~ '^\s*$'
 
return "\&lt;Tab&gt;"
 
else
 
return "\&lt;C-N&gt;"
 
endfunction
 
inoremap &lt;Tab&gt; &lt;C-R&gt;=CleverTab()&lt;CR&gt;
 
 
It works fine, but this never uses "omni-completion" introduced from vim7.
 
 
To use omni-completion if available, then it should be followings.
 
To use omni-completion if available, then it should be followings.
+
<pre>
function! CleverTab()
+
function! CleverTab()
if pumvisible()
+
if pumvisible()
return "\&lt;C-N&gt;"
+
return "\<C-N>"
endif
+
endif
if strpart( getline('.'), 0, col('.')-1 ) =~ '^\s*$'
+
if strpart( getline('.'), 0, col('.')-1 ) =~ '^\s*$'
return "\&lt;Tab&gt;"
+
return "\<Tab>"
elseif exists('&amp;omnifunc') &amp;&amp; &amp;omnifunc != ''
+
elseif exists('&omnifunc') && &omnifunc != ''
return "\&lt;C-X&gt;\&lt;C-O&gt;"
+
return "\<C-X>\<C-O>"
else
+
else
return "\&lt;C-N&gt;"
+
return "\<C-N>"
endif
+
endif
endfunction
+
endfunction
inoremap &lt;Tab&gt; &lt;C-R&gt;=CleverTab()&lt;CR&gt;
+
inoremap <Tab> <C-R>=CleverTab()<CR>
  +
</pre>
   
 
----
 
----

Revision as of 08:27, September 28, 2008

Tip 102 Printable Monobook Previous Next

created August 21, 2001 · complexity basic · author benoit cerrina · version 6.0


I'm used to complete words with <tab>, however when editing source I can't just map that to Vim keyword completion because I sometime need to insert real tabs, since it mostly happen when at the beginning of the line or after a ; and before a one line comma (java, c++ or perl anyone)

I've come to find the following really useful.

This is how you can map the <tab> key in insert mode while still being able to use it when at the start of a line or when the preceding char is not a keyword character.

in a script file in a plugin directory or in your .vimrc file:

first define a function which returns a <tab> or a <C-N> depending on the context:

function! InsertTabWrapper(direction)
  let col = col('.') - 1
  if !col || getline('.')[col - 1] !~ '\k'
    return "\<tab>"
  elseif "backward" == a:direction
    return "\<c-p>"
  else
    return "\<c-n>"
  endif
endfunction

Then define the appropriate mappings:

inoremap <tab> <c-r>=InsertTabWrapper ("forward")<CR>
inoremap <s-tab> <c-r>=InsertTabWrapper ("backward")<CR>

The trick here is the use of the <c-r>= in insert mode to be able to call your function without leaving insert mode. See :help i_CTRL-R.

Comments

 TO DO 

  • Clean up! This is a nice tip...
  • Use an <expr> mapping instead of <C-R>=

See also script#182. It is a complete system, and the completion remembers the completion mode!


for those of us that would like to use dictionary files for filetypes ( in order to use function prototypes et. al.) you can modify the 'iskeyword' option temporarily to complete function names for example : given a dictionary with abs(VALUE)

I could type :

ab<TAB>

and it would expand to abs(VALUE)

function! InsertTabWrapper(direction)
  let oldisk=&isk "save the iskeyword options
  set isk+=(,),, "add '(' ')' and ',' character
  let col = col('.') - 1
  if !col || getline('.')[col - 1] !~ '\k'
    return "\<tab>"
  elseif "backward" == a:direction
    return "\<c-n>"
  else
    return "\<c-p>"
  endif
  set &isk=oldisk "restore the iskeyword options
endfunction

fun! Iskcompletion()
  let oldisk=&isk
  set isk+=(,)
  normal <C-P>
  set &isk=oldisk
endfun

If you want to use keywords in completion, you'll need to make dictionary files for each of the languages whose keywords you want to use.

e.g.

cat /usr/share/vim/vim61/syntax/sh.vim | grep keyword | grep -v nextgroup | awk '{ $1=""; $2=""; $3=""; print}' | perl -pe 's/\s+/\n/g' | grep -v contained | grep -v '^$' | sort | uniq > /home/user/.vim/dict/sh.dict

the above command will probably need tweaking depending on the syntax file.

and in your .vimrc:

" tell complete to look in the dictionary
set complete-=k complete+=k

" load the dictionary according to syntax
:au BufReadPost * if exists("b:current_syntax")
:au BufReadPost * let &dictionary = substitute("~/.vim/dict/FT.dict", "FT", b:current_syntax, "")
:au BufReadPost * endif

I prefer to be able to toggle tab completion manually. The following function works for me:

" toggle tab completion
function! TabCompletion()
  if mapcheck("\<tab>", "i") != ""
    :iunmap <tab>
    :iunmap <s-tab>
    :iunmap <c-tab>
    echo "tab completion off"
  else
    :imap <tab> <c-n>
    :imap <s-tab> <c-p>
    :imap <c-tab> <c-x><c-l>
    echo "tab completion on"
  endif
endfunction
map <Leader>tc :call TabCompletion()<CR>

Have to chime in... here's my silly attempt at making the Tab key more intelligent. The function below (derived work of course) allows one to get the original effect of <Tab> if the previous character is a space. I.e., you can type:

foo<Space><Tab>

and it'll end up as foo<Tab> with the space deleted. This is *not* the same as <C-v><Tab>, which would always insert a real Tab character rather than honoring 'softtabstop'.

" Intelligent tab completion
inoremap <silent> <Tab> <C-r>=<SID>InsertTabWrapper(1)<CR>
inoremap <silent> <S-Tab> <C-r>=<SID>InsertTabWrapper(-1)<CR>
function! <SID>InsertTabWrapper(direction)
  let idx = col('.') - 1
  let str = getline('.')
  if a:direction > 0 && idx >= 2 && str[idx - 1] == ' '
        \&& str[idx - 2] =~? '[a-z]'
    if &softtabstop && idx % &softtabstop == 0
      return "\<BS>\<Tab>\<Tab>"
    else
      return "\<BS>\<Tab>"
    endif
  elseif idx == 0 || str[idx - 1] !~? '[a-z]'
    return "\<Tab>"
  elseif a:direction > 0
    return "\<C-p>"
  else
    return "\<C-n>"
  endif
endfunction

What about this to dump all the syntax files into properly formatted dict files

for i in /usr/share/vim/syntax/*;do cat $i | grep keyword | grep -v nextgroup | awk '{ $1=""; $2=""; $3=""; print}' | perl -pe 's/\s+/\n/g' | grep -v contained | grep -v '^$' | sort | uniq>~/.vim/dict/`basename $i .vim`.dict;done

One small suggestion for daniel elstner's script: replace '[a-z]' with '[^<Space><tab>]'.


My derivation of this wonderful tip: (aimed at C++ // comments)

This as it happens has nothing to do with <tab> nor completion.

function! SpecialCR()
 " Feral This is inspired by how multi-edit does things.
 let DaLine = getline('.')
 if match(DaLine, '\c^\s*//$') > -1
" [Feral:213/03@04:59] Just eat the // chars
" return "\<BS>\<BS>"
"
"This method:
" // -[Feral:213/03@04:59]----------------------------------------------
" //
" 75 is the column we wish to not go beyond.
" 2 = ' -'
" 20 = my time stamp: [Feral:213/03@04:52]
" 1 = '-'
let AmountForFiller = 75 - (virtcol('.')+2+20+1)
let Filler = ""
while strlen(Filler) < AmountForFiller
  let Filler = Filler.'-'
endwhile
let DaLine = " -[Feral:".strftime('%j/%y@%H:%M')."]-".Filler."\<CR>"
return DaLine
 elseif match(DaLine, '\c^\s*//\s$') > -1
   return "\<BS>\<BS>\<BS>"
 else
   return "\<CR>"
 endif
endfunction

inoremap <CR> <c-r>=SpecialCR()<CR>

This will make <CR> eat C++ line comments when they are the only think on the line, or more precisely: "//\s" will be get 3 backspaces which should erase it and "//" will get a simple timestamp separator.


Here's the section of my vimrc that has all this wired up (this is on a windows box, I jumped on a unix machine to run the shell script that makes all the dict files). My addition is Ctrl-TAB to begin a keyword search instead of hitting Ctrl-X Ctrl-K, then I can regular TAB through the entries.

Watch out for those dang spaces at the end of the lines!

Thanks to everybody who contributed, this is great.

" Remap TAB to keyword completion
function! InsertTabWrapper(direction)
  let col = col('.') - 1
  if !col || getline('.')[col - 1] !~ '\k'
    return "\<tab>"
  elseif "backward" == a:direction
    return "\<c-p>"
  elseif "forward" == a:direction
    return "\<c-n>"
  else
    return "\<c-x>\<c-k>"
  endif
endfunction

inoremap <tab> <c-r>=InsertTabWrapper ("forward")<CR>
inoremap <s-tab> <c-r>=InsertTabWrapper ("backward")<CR>
inoremap <c-tab> <c-r>=InsertTabWrapper ("startkey")<CR>

" toggle tab completion
function! ToggleTabCompletion()
  if mapcheck("\<tab>", "i") != ""
    :iunmap <tab>
    :iunmap <s-tab>
    :iunmap <c-tab>
    echo "tab completion off"
  else
    :imap <tab> <c-n>
    :imap <s-tab> <c-p>
    :imap <c-tab> <c-x><c-l>
    echo "tab completion on"
  endif
endfunction

map <Leader>tc :call ToggleTabCompletion()<CR>

" tell complete to look in the dictionary
set complete-=k complete+=k

" load the dictionary according to syntax
:au BufReadPost * if exists("b:current_syntax")
:au BufReadPost * let &dictionary = substitute("C:\\vim\\vimfiles\\dict\\FT.dict", "FT", b:current_syntax, "")
:au BufReadPost * endif

This script doesn't work with multibyte (utf-8 chars)


Try replace condition with

!col || strpart(getline('.'), col-1, col) =~ '\s'

your tab-completion tip is very nice. Although my kde-konsole didn't recognize the shift-tab, <s-tab> combination.

I solved this by adding the following line to /usr/share/apps/konsole/linux.keytab

key Backtab : "\E[Z"

and set the keyboard settings in konsole to "linux console".


In the following bit, it's more useful if you substituted "BufEnter" for "BufReadPost". At least, for those who switch between Vim windows of differing file types (for example I go between perl and cpp and bash quit a bit)

>" load the dictionary according to syntax
>:au BufReadPost * if exists("b:current_syntax")
>:au BufReadPost * let &dictionary = substitute("C:\\vim\\vimfiles\\dict\\FT.dict", "FT", b:current_syntax, "")
>:au BufReadPost * endif

In :help ins-completion you can find following settings.

function! CleverTab()
  if strpart( getline('.'), 0, col('.')-1 ) =~ '^\s*$'
    return "\<Tab>"
  else
    return "\<C-N>"
endfunction
inoremap <Tab> <C-R>=CleverTab()<CR>

It works fine, but this never uses "omni-completion" introduced from Vim 7. To use omni-completion if available, then it should be followings.

function! CleverTab()
  if pumvisible()
    return "\<C-N>"
  endif
  if strpart( getline('.'), 0, col('.')-1 ) =~ '^\s*$'
    return "\<Tab>"
  elseif exists('&omnifunc') && &omnifunc != ''
    return "\<C-X>\<C-O>"
  else
    return "\<C-N>"
  endif
endfunction
inoremap <Tab> <C-R>=CleverTab()<CR>

Around Wikia's network

Random Wiki