Одно из правил эффективного использования редактора гласит следующее — определите, на что у Вас тратится больше всего времени при наборе текста, и улучшите это.
Как показывает практика, часто пользователи этого редактора ограничиваются установкой опций, коих конечно не мало. Затем ставят какой-нибудь плагин-мега-пак по советам знатоков, и вроде бы все устраивает, кроме… первого, второго, третьего…
Но ведь если пойти дальше, можно обнаружить бесконечный потенциал для увеличения производительности в использовании своего редактора.



В этой статье я попытаюсь описать немного продвинутый способ настройки Vim.
Мы рассмотрим с Вами внутренний скриптинг и поймем, что ничего в нем нет страшного, обычный скриптовый язык.
Данный материал рассчитан на довольно подготовленных пользователях редактора Vim. Для тех, кто разобрался, что такое режимы редактора, буферы, окна. Статья написана в стиле «Одна глава — один конкретный рецепт — одно описание синтаксической структуры языка».


История изменений или оператор if else


Приведем пример использования оператора if в Вашем vimrc для установки опций
if version >= 700
    set history=64
    set undolevels=128
    set undodir=~/.vim/undodir/
    set undofile
    set undolevels=1000
    set undoreload=10000
endif

Данный кусок кода включает весьма полезные возможности, доступные начиная с версии 7.00: после того, как вы закрываете редактор(а если точнее, то текущий буффер), в предыдущих версиях история UNDO-REDO терялась. Начиная же с 7.00 появилась возможность записи этой истории в служебные файлы по каждому ранее открытому буфферу.
То есть теперь вы можете сменить буффер, закрыть окно, вообще выключить редактор, но открыв заново какой-либо файл, история Ваших изменений восстановится.

Быстрое переключение буферов или создание своей функции


Переключение между загруженными буферами должно быть быстрым. Не совсем удобно постоянно набирать :bn, :bp, :b#. Поэтому создадим свою функцию переключения и повесим на горячие клавиши этот функционал.
function! ChangeBuf(cmd)
    if (&modified && &modifiable)
        execute ":w"
    endif
    execute a:cmd
endfunction
nnoremap <silent> <C-o> :call ChangeBuf(":b#")<CR>
nnoremap <silent> <C-n> :call ChangeBuf(":bn")<CR>
nnoremap <silent> <C-p> :call ChangeBuf(":bp")<CR>

Как вы знаете, если файл модифицирован, команды :bn, :bp, b# не сработают и выведут предупреждение о том, что надо его сохранить. Для этого мы и пишем эту функцию, в которой осуществляется проверка, модифицирован ли файл и может ли он быть модифицирован вообще.
Здесь мы создаем функцию, аргумент которой примет как раз те команды по переключению буферов, описанный выше.
nnoremap со��дает привязку определенной комбинации клавиш для какого либо действия. Аргумент \<silent\> — подавить echo вывод.

Здесь надо дать некоторое пояснение по переменным в .vimrc, а именно практически все они начинаются с какого либо префикса, отделенного от имени двоеточием. Префикс означает область видимости. Вот основные префиксы:
a:var — аргумент функции
b:var — переменная для текущего буфера
g:var — глобальная переменная
l:var — переменная, объявленная в теле функции
v:var — глобальная определенная в самом редакторе Vim


Список буферов или цикл for


Мне не очень нравится вывод буферов по команде :ls, хотя бы из-за того, что вывод многострочный. По-этому разберем пример с циклом for для вывода списка открытых буферов в одной строке. Преимуществом такого решения является то, что я могу вызвать эту функцию где угодно, в том числе в методе, описанном в предыдущем разделе. Получится, что при смене текущего буфера сразу будет отображаться список других открытых буферов.
function! BufList()
    let status = ""
    for i in range(1, last_buffer_nr()+1)
        if bufnr("%") == i 
            let status = status . '   ' . '[' . bufname(i) . ']' "объединяем строки
            continue
        endif
        if buflisted(i)
            let status = status . '   ' . bufname(i)
        endif
    endfor
    return status
endfunction

Здесь мы просто формируем строку со списком открытых буферов. Текущий буфер выделяем в квадратных скобках.
Как мы видим, цикл for весьма схож с циклом из того же Python'а

Здесь надо указать, что ряд функций редактора Vim принимают в качестве аргумента так называемое выражение. Для функции bufnr() например вы��ажением может быть, например символ "%" — даст номер текущего буфера, "$" — даст номер последнего буфера. По каждой функции лучше все-таки смотреть :help func()


Автоматически исполняемый скрипт или чтение данных из текущего буфера


Я довольно часто начинаю писать новые скрипты. И мне удобно, что файл может быть сразу исполняемым.
function ModeChange()
    if getline(1) =~ "^#!"
        if getline(1) =~ "bin/"
            silent !chmod a+x <afile>
        endif
    endif
endfunction
au BufWritePost * call ModeChange()

Здесь мы берем и читаем первую строку из файла, и если она начинается с '#!' и в ней есть 'bin/', то делаем файл исполняемым. После вешаем автокоманду на событие BufWritePost.

Автоматическая заготовка скрипта или вставка в текущий буффер


Этот пример связан с предыдущим. Если мы начинаем писать новый скрипт на python'е, было бы удобно, если сразу в нем была заготовка, например, функция main, некоторые import'ы, строка определения интерпретатора. Продемонстрируем.
function! WritePyinit()
    let @q = "
    \#\!/usr/bin/env python\n\#-*- encoding: utf-8 -*-\n\nimport sys, warnings\n\nwarnings.simplefilter('always')\n\ndef main(argv=sys.argv):\n    pass\n\nif __name__ == \"__main__\":\n           
sys.exit(main())\n"
    execute "0put q"
endfunction
autocmd BufNewFile *.py call WritePyinit()

Все просто. Если у Вас новый файл *.py, в него будет добавлен стандартный код.
Новый файл test.py
#!/usr/bin/env python
#-*- encoding: utf-8 -*-

import sys, warnings

warnings.simplefilter('always')

def main(argv=sys.argv):
    pass

if __name__ == "__main__":
    sys.exit(main())



Автоматическая документация или пишем vimrc с синтаксисом Python'а


Эта статья была бы не полной, если бы я не показал, как можно делать вставки в .vimrc на другом языке программирования. Покажу это на примере Python.
Код надо документировать. На Python документация часто пишется в виде DocStrings. Покажем пример, как можно по комбинации клавши автоматически переноситься к тому месту, где должна быть документация.
Автодокументация
function! WriteDocstrings()
python <<EOF
import vim
import re
linenr = vim.current.window.cursor[0]
indentr = vim.current.window.cursor[0]
line = vim.current.line
n = 0
for i in line:
    if i != ' ':
       break
    n += 1
if len(line) == 0:
    n = 0
vim.current.buffer.append(' '*n + ' '*4 + '"""', linenr)
vim.current.buffer.append(' '*n + ' '*4 + '', linenr)
vim.current.buffer.append(' '*n + ' '*4 + '"""', linenr)
vim.current.window.cursor = (vim.current.window.cursor[0]+2, n+4)
EOF
endfunction

Опять же, ничего хитрого, обычные HERED-Docs.
Здесь мы используем модуль для Python vim. Далее выискиваем начало текущего блока, вставляем туда кавычки для документации и переводим курсор для в начало будущей документации.

Надо учесть то, что для работы этого кода, вам необходима поддержка python в Вашем vim. Проверить это можно следующим образом:
vim --version | grep '+python'

Если есть поддержка, то все хорошо. Если нет, надо либо собрать Vim самому, либо поставить другой пакет. В Debian/Ubuntu/Mint я рекомендую ставить пакет vim-nox
apt-get install nox



Заключение


Статья получилась довольно объемной. Надеюсь, я показал, что маленькие особенности в Ваш редактор можно добавлять довольно просто.
Теперь, разобравшись с основными принципами продвинутой настройки редактора, Вы сможете написать более полезные дополнения, например, комментирование блока выделенного текста, открытие документации каких-либо функций.

Список полезных материалов

Официальный сайт Vim
VIMDOC, с гиперссылками
Документация по модулю vim для python

UPD1: чищеный от приватки vimrc
"
" Comments/uncomments strings and selected text
" When buffer changing try to save it
" Convinient view of statusline
" AutoResave sessions on exit
" Auto settings for DJANGO omni completion
"
"Mapped Keys
" Mode  Combination Description
" N     <C-n>       Next buffer with saving if possible
" N     <C-p>       Prev buffer with saving if possible
" N     <C-o>       Last viewed buffer
" N     <C-e>       Clear highlighting after searching
" I     <C-q>       Set normal mode
" N     <C-c>       Comment Lines
" N     <C-u>       Uncomment Lines
" I     <C-Space>   Show popup menu OmniCompletion
" I     <Tab>       If popupmenu is showed list it elements
" NIV   <F5>        Bonding Mode(useful when pasting from X-buffer)
" I     <A-h>       Left in insert mode
" I     <A-j>       Down in insert mode
" I     <A-k>       Up in insert mode
" I     <A-l>       Right in insert mode
" N     ,p          Paste after
" N     ,P          Paste before
" N     <C-j>       Down in long line
" N     <C-k>       Up in long line
" V     <C-c>       Comment Line
" V     <C-u>       Uncomment Line

filetype on
filetype plugin on

set tabstop=4
set shiftwidth=4
set smarttab
set expandtab
set softtabstop=4
set autoindent
let python_highlight_all = 1
set t_Co=256
autocmd FileType html,xhtml,xml,htmldjango,htmljinja,eruby,mako setlocal noexpandtab
autocmd FileType *.py set tw=80
autocmd FileType python nnoremap <C-a> :call WriteDocstrings()<CR>
"nnoremap <C-a> :call WriteDocstrings()<CR>
autocmd BufWritePre *.py normal m`:%s/\s\+$//e ``
autocmd BufRead *.py set smartindent cinwords=if,elif,else,for,while,try,except,finally,def,class
autocmd BufNewFile *.py call WritePyinit()
autocmd VimLeave * !reset
set nu
set ruler
set mousehide
syntax on
set backspace=indent,eol,start whichwrap+=<,>,[,]
set showtabline=0
set foldcolumn=1
set wrap
set linebreak
set nobackup
set noswapfile
set encoding=utf-8
set fileencodings=utf8,cp1251
"Searchig options
set showmatch
set hlsearch
set incsearch
"Layouts
set fencs=utf-8,cp1251,koi8-r,ucs-2,cp866
"memory, history, undotree
if version >= 700
    set history=64
    set undolevels=128
    set undodir=~/.vim/undodir/
    set undofile
    set undolevels=1000
    set undoreload=10000
endif
"Sessions - сохранение текущих буферов, регистров.
set sessionoptions=buffers,tabpages,help,blank,globals,localoptions,sesdir,slash,options
function! SaveSession(...)
    if v:this_session != ""
        function Tmp(filename)
            execute ":mksession! ".v:this_session
        endfunction
        autocmd VimLeavePre * :call Tmp("xxx")
    endif
endfunction
au SessionLoadPost * :call SaveSession()
"Titles, statuses
set laststatus=2
set showtabline=2
set title
set statusline=%1*%m%*%2*%r%*%3*%h%w%*%{expand(\"%:p:~\")}\ %<
set statusline+=%=Col:%3*%03c%*\ Ln:%3*%04l/%04L%*
set statusline+=%(\ File:%3*%{join(filter([&filetype,&fileformat!=split(&fileformats,\",\")[0]?&fileformat:\"\",&fileencoding!=split(&fileencodings,\",\")[0]?&fileencoding:\"\"],\"!empty(v:val)\"),\"/\")}%*%)
set titlestring=%t%(\ %m%)%(\ %r%)%(\ %h%)%(\ %w%)%(\ (%{expand(\"%:p:~:h\")})%)\ -\ VIM

"autocmd VimLeavePre * silent mksession! ~/.vim/lastSession.vim
"autocmd VimEnter * silent source! ~/.vim/lastSession.vim
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
set langmap=йq,цw,уe,кr,еt,нy,гu,шi,щo,зp,х[,ъ],фa,ыs,вd,аf,пg,рh,оj,лk,дl,ж\\;,э',яz,чx,сc,мv,иb,тn,ьm,б\\,,ю.,ё`,ЙQ,ЦW,УE,КR,ЕT,НY,ГU,ШI,ЩO,ЗP,Х{,Ъ},ФA,ЫS,ВD,АF,ПG,РH,ОJ,ЛK,ДL,Ж:,Э\\",ЯZ,ЧX,СC,МV,ИB,ТN,ЬM,Б<,Ю>,Ё~
"Buffers
function! ChangeBuf(cmd)
    if (&modified && &modifiable)
        execute ":w"
    endif
    execute a:cmd
endfunction
nnoremap <silent> <C-o> :call ChangeBuf(":b#")<CR>
nnoremap <silent> <C-n> :call ChangeBuf(":bn")<CR>
nnoremap <silent> <C-p> :call ChangeBuf(":bp")<CR>
nnoremap <C-CR> O<ESC>j
inoremap <C-q> <ESC>
nnoremap <C-e> :nohlsearch<CR>
inoremap <C-e> <ESC>I
inoremap <C-a> <ESC>A
function! BufList()
    let status = ""
    for i in range(1, last_buffer_nr()+1)
        if bufnr("%") == i
            let status = status . '   ' . '[' . bufname(i) . ']'
            continue
        endif
        if buflisted(i)
            let status = status . '   ' . bufname(i)
        endif
    endfor
    return status
endfunction
"Copy/Paste
if has("clipboard")
    set clipboard=autoselect
endif
"Autoaddition
set formatoptions-=o

"scripting
function ModeChange()
    if getline(1) =~ "^#!"
        if getline(1) =~ "bin/"
            silent !chmod a+x <afile>
        endif
    endif
endfunction
au BufWritePost * call ModeChange()
if expand("%:t") =~ "py$"
    set makeprg=python 
endif
if expand("%:t") =~ "sh$"
    set makeprg=/bin/bash 
endif

if !has("gui_running")
    imap <C-@> <C-X><C-O>
else
    imap <C-Space> <C-X><C-O>
endif

filetype plugin on
set ofu=syntaxcomplete#Complete
set complete=.,b,t,k,u,k
set completeopt-=preview 
set completeopt+=longest 
autocmd FileType python set omnifunc=pythoncomplete#Complete
autocmd FileType javascript set omnifunc=javascriptcomplete#CompleteJS
autocmd FileType html set omnifunc=htmlcomplete#CompleteTags
autocmd FileType css set omnifunc=csscomplete#CompleteCSS
if expand("%:t") =~ "^.*\.py$"
   let $PYTHONPATH = fnamemodify("%", ":p:h:h")
   let $DJANGO_SETTINGS_MODULE = fnamemodify("%", ":p:h:t").".settings"
endif
set completeopt=longest,menuone
"Режим склейки при вставке
"set paste 
"set nopaste
"set invpaste
set pastetoggle=<F5>

"Folding
set foldenable
set foldmethod=syntax
autocmd FileType tex set foldmethod=indent


"Цветовая схема
"color blackboard
let g:solarized_termcolors=256
colorscheme solarized
set background=dark


"Remaps
inoremap <A-h> <C-o>h
inoremap <A-j> <C-o>j
inoremap <A-k> <C-o>k
inoremap <A-l> <C-o>l

nnoremap ,p o<ESC>p
nnoremap ,P O<ESC>p

nnoremap <C-j> gj
nnoremap <C-k> gk

"Автокомменты
nnoremap  <C-c> :call CommentLine()<cr>
nnoremap  <C-u> :call UnCommentLine()<cr>
vmap  <C-c> :call CommentLine()<cr>
vmap  <C-u> :call UnCommentLine()<cr>
"скрипты

function! WritePyinit()
    let @q = "
    \#\!/usr/bin/env python\n\#-*- encoding: utf-8 -*-\n\nimport sys, warnings\n\nwarnings.simplefilter('always')\n\ndef main(argv=sys.argv):\n    pass\n\nif __name__ == \"__main__\":\n           sys.exit(main())\n"
    execute "0put q"
endfunction

function! WriteDocstrings()
if !has('python')
    echo "Error: Required vim compiled with +python"
    finish
endif
python <<EOF
import vim
import re
linenr = vim.current.window.cursor[0]
indentr = vim.current.window.cursor[0]
line = vim.current.line
n = 0
for i in line:
    if i != ' ':
       break
    n += 1
if len(line) == 0:
    n = 0
vim.current.buffer.append(' '*n + ' '*4 + '"""', linenr)
vim.current.buffer.append(' '*n + ' '*4 + '', linenr)
vim.current.buffer.append(' '*n + ' '*4 + '"""', linenr)
vim.current.window.cursor = (vim.current.window.cursor[0]+2, n+4)
EOF
endfunction
""""""""""""""""
"Для комметариев
""""""""""""""""
function __is_django_template()
    let l:a = getpos(".")
    if search("{\%.*\%}", '', line("$")) != 0
        let b:b = cursor(l:a[1], l:a[2], "off")
        return 1
    endif
    return 0
endfunction

function RetFileType()
    let file_name = buffer_name("%")
    if file_name =~ '\.vim'
        return ["\"", ""]
    elseif __is_django_template() == 1
        return ['{% comment %}' , '{% endcomment %}']
    elseif file_name =~ '\.html$' || file_name =~ '\.xhtml$' || file_name =~ '\.xml'
        return ["<!--", "-->"]
    endif
    return ["#", ""]
endfunction
au BufEnter * let b:comment = RetFileType()
function! CommentLine()
    let stsymbol = b:comment[0]
    let endsymbol = b:comment[1]
    execute ":silent! normal 0i" . stsymbol . "\<ESC>A" . endsymbol . "\<ESC>"
endfunction
function! UnCommentLine()
    let file_name = buffer_name("%")
    let stsymbol = b:comment[0]
    let endsymbol = b:comment[0]
    execute ":silent! normal :s/^\s*" . stsymbol . "//\<CR>"
    execute ":silent! normal :s/\s*" . endsymbol . "\s*$//\<CR>"
endfunction
let ropevim_vim_completion=1

function! CmdLine(str)
    exe "menu Foo.Bar :" . a:str
    emenu Foo.Bar
    unmenu Foo
endfunction 

" From an idea by Michael Naumann
function! VisualSearch(direction) range
    let l:saved_reg = @"
    execute "normal! vgvy"

    let l:pattern = escape(@", \/.*$^~[])
    let l:pattern = substitute(l:pattern, "\n$", "", "")

    if a:direction == b
        execute "normal ?" . l:pattern . "^M"
    elseif a:direction == gv
        call CmdLine("vimgrep " . /. l:pattern . / .  **/*.)
    elseif a:direction == f
        execute "normal /" . l:pattern . "^M"
    endif

    let @/ = l:pattern
    let @" = l:saved_reg
endfunction

"Basically you press * or # to search for the current selection
vnoremap <silent> * :call VisualSearch('f')<CR>
vnoremap <silent> # :call VisualSearch('b')<CR>
vnoremap <silent> gv :call VisualSearch('gv')<CR>
"PYDOC
if exists('*s:ShowPyDoc') && g:pydoc_perform_mappings
    call s:PerformMappings()
    finish
endif

if !exists('g:pydoc_perform_mappings')
    let g:pydoc_perform_mappings = 1
endif

if !exists('g:pydoc_highlight')
    let g:pydoc_highlight = 1
endif

if !exists('g:pydoc_cmd')
    let g:pydoc_cmd = 'pydoc'
endif

if !exists('g:pydoc_open_cmd')
    let g:pydoc_open_cmd = 'split'
endif

setlocal switchbuf=useopen
highlight pydoc cterm=reverse gui=reverse

function s:ShowPyDoc(name, type)
    if a:name == ''
        return
    endif

    if g:pydoc_open_cmd == 'split'
        let l:pydoc_wh = 10
    endif

    if bufloaded("__doc__")
        let l:buf_is_new = 0
        if bufname("%") == "__doc__"
            " The current buffer is __doc__, thus do not
            " recreate nor resize it
            let l:pydoc_wh = -1
        else
            " If the __doc__ buffer is open, jump to it
            silent execute "sbuffer" bufnr("__doc__")
            let l:pydoc_wh = -1
        endif
    else
        let l:buf_is_new = 1
        silent execute g:pydoc_open_cmd '__doc__'
        if g:pydoc_perform_mappings
            call s:PerformMappings()
        endif
    endif

    setlocal modifiable
    setlocal noswapfile
    setlocal buftype=nofile
    setlocal bufhidden=delete
    setlocal syntax=man

    silent normal ggdG
    " Remove function/method arguments
    let s:name2 = substitute(a:name, '(.*', '', 'g' )
    " Remove all colons
    let s:name2 = substitute(s:name2, ':', '', 'g' )
    if a:type == 1
        execute  "silent read !" g:pydoc_cmd s:name2
    else
        execute  "silent read !" g:pydoc_cmd "-k" s:name2
    endif
    normal 1G

    if exists('l:pydoc_wh') && l:pydoc_wh != -1
        execute "silent resize" l:pydoc_wh
    end

    if g:pydoc_highlight == 1
        execute 'syntax match pydoc' "'" . s:name2 . "'"
    endif

    let l:line = getline(2)
    if l:line =~ "^no Python documentation found for.*$"
        if l:buf_is_new
            execute "bdelete!"
        else
            normal u
            setlocal nomodified
            setlocal nomodifiable
        endif
        redraw
        echohl WarningMsg | echo l:line | echohl None
    else
        setlocal nomodified
        setlocal nomodifiable
    endif
endfunction

" Mappings
function s:PerformMappings()
    nnoremap <silent> <buffer> <Leader>pw :call <SID>ShowPyDoc('<C-R><C-W>', 1)<CR>
    nnoremap <silent> <buffer> <Leader>pW :call <SID>ShowPyDoc('<C-R><C-A>', 1)<CR>
    nnoremap <silent> <buffer> <Leader>pk :call <SID>ShowPyDoc('<C-R><C-W>', 0)<CR>
    nnoremap <silent> <buffer> <Leader>pK :call <SID>ShowPyDoc('<C-R><C-A>', 0)<CR>

    " remap the K (or 'help') key
    nnoremap <silent> <buffer> K :call <SID>ShowPyDoc(expand("<cword>"), 1)<CR>
endfunction

if g:pydoc_perform_mappings
    call s:PerformMappings()
endif

" Commands
command -nargs=1 Pydoc       :call s:ShowPyDoc('<args>', 1)
command -nargs=* PydocSearch :call s:ShowPyDoc('<args>', 0)