Back to Home

Advanced VIM configuration

vim · ide · python

Advanced VIM configuration

  • Tutorial
One of the rules for effective use of the editor is as follows - determine what you spend the most time on typing, and improve it.
As practice shows, often users of this editor are limited to installing options, of which there are certainly not a few. Then they put some kind of plug-in mega-pack on the advice of experts, and everything seems to be fine, except ... the first, second, third ...
But if you go further, you can discover infinite potential for increasing productivity in using your editor.



In this article, I will try to describe a slightly advanced way to configure Vim.
We will consider internal scripting with you and understand that there is nothing terrible in it, an ordinary scripting language.
This material is designed for fairly trained users of the Vim editor. For those who understand what editor modes, buffers, windows are. The article is written in the style of "One chapter - one specific recipe - one description of the syntactic structure of the language."


Change history or if else statement


Here is an example of using the if statement in your vimrc to set options
if version >= 700
    set history=64
    set undolevels=128
    set undodir=~/.vim/undodir/
    set undofile
    set undolevels=1000
    set undoreload=10000
endif

This piece of code includes some very useful features available since version 7.00: after you close the editor (or rather, the current buffer), in previous versions the history of UNDO-REDO was lost. Starting from 7.00 it became possible to record this story in the service files for each previously opened buffer.
That is, now you can change the buffer, close the window, turn off the editor altogether, but by opening a file again, the history of your changes will be restored.

Quickly switch buffers or create your own function


Switching between loaded buffers should be quick. It’s not very convenient to constantly type: bn,: bp,: b #. Therefore, we will create our own switching function and put this functionality on the hot keys.
function! ChangeBuf(cmd)
    if (&modified && &modifiable)
        execute ":w"
    endif
    execute a:cmd
endfunction
nnoremap  :call ChangeBuf(":b#")
nnoremap  :call ChangeBuf(":bn")
nnoremap  :call ChangeBuf(":bp")

As you know, if the file is modified, the commands: bn,: bp, b # will not work and will warn you that it must be saved. To do this, we write this function, which checks whether the file is modified and whether it can be modified at all.
Here we create a function, the argument of which will take just those commands for switching buffers described above.
nnoremap creates a binding of a certain key combination for any action. Argument \- suppress echo output.

Here we need to give some explanation on the variables in .vimrc, namely, almost all of them start with some kind of prefix, separated from the name by a colon. The prefix means the scope. Here are the main prefixes:
a: var - function argument
b: var - variable for the current buffer
g: var - global variable
l: var - variable declared in the body of function
v: var - global defined in the Vim editor itself


Buffer list or for loop


I don’t really like the output of the buffers on the command: ls, if only because the output is multi-line. Therefore, we will analyze an example with a for loop to display a list of open buffers on one line. The advantage of this solution is that I can call this function anywhere, including in the method described in the previous section. It turns out that when you change the current buffer, a list of other open buffers will immediately be displayed.
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

Here we simply form a line with a list of open buffers. Select the current buffer in square brackets.
As we can see, the for loop is very similar to the loop from the same Python.

Here you need to indicate that a number of functions of the Vim editor take the so-called expression as an argument. For the bufnr () function, for example, the expression can be, for example, the symbol "%" - will give the number of the current buffer, "$" - will give the number of the last buffer. For each function, it's better to watch: help func ()


Automatically executed script or reading data from the current buffer


I often start writing new scripts. And it’s convenient for me that the file can be immediately executable.
function ModeChange()
    if getline(1) =~ "^#!"
        if getline(1) =~ "bin/"
            silent !chmod a+x 
        endif
    endif
endfunction
au BufWritePost * call ModeChange()

Here we take and read the first line from the file, and if it starts with '#!' and it has 'bin /', then make the file executable. After we hang the auto command on the BufWritePost event.

Automatically prepare a script or paste into the current buffer


This example is related to the previous one. If we start writing a new script in python, it would be convenient if there was a blank in it right away, for example, the main function, some imports, and the interpreter definition line. Demonstrate.
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()

Everything is simple. If you have a new * .py file, the standard code will be added to it.
New test.py file
#!/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())



Automated documentation or writing vimrc with Python syntax


This article would not be complete if I had not shown how to insert in .vimrc in another programming language. I will show it on the example of Python.
The code must be documented. In Python, documentation is often written as DocStrings. Let us show an example of how, by a combination of keys, it is automatically transferred to the place where the documentation should be.
Auto Documentation
function! WriteDocstrings()
python <

Опять же, ничего хитрого, обычные 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            Next buffer with saving if possible
" N            Prev buffer with saving if possible
" N            Last viewed buffer
" N            Clear highlighting after searching
" I            Set normal mode
" N            Comment Lines
" N            Uncomment Lines
" I        Show popup menu OmniCompletion
" I            If popupmenu is showed list it elements
" NIV           Bonding Mode(useful when pasting from X-buffer)
" I            Left in insert mode
" I            Down in insert mode
" I            Up in insert mode
" I            Right in insert mode
" N     ,p          Paste after
" N     ,P          Paste before
" N            Down in long line
" N            Up in long line
" V            Comment Line
" V            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  :call WriteDocstrings()
"nnoremap  :call WriteDocstrings()
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  :call ChangeBuf(":b#")
nnoremap  :call ChangeBuf(":bn")
nnoremap  :call ChangeBuf(":bp")
nnoremap  Oj
inoremap 
nnoremap  :nohlsearch
inoremap I
inoremap 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 
        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 
else
    imap 
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=
"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 h
inoremap j
inoremap k
inoremap l
nnoremap ,p op
nnoremap ,P Op
nnoremap  gj
nnoremap  gk
"Автокомменты
nnoremap   :call CommentLine()
nnoremap   :call UnCommentLine()
vmap   :call CommentLine()
vmap   :call UnCommentLine()
"скрипты
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 <A" . endsymbol . "\"
endfunction
function! UnCommentLine()
    let file_name = buffer_name("%")
    let stsymbol = b:comment[0]
    let endsymbol = b:comment[0]
    execute ":silent! normal :s/^\s*" . stsymbol . "//\"
    execute ":silent! normal :s/\s*" . endsymbol . "\s*$//\"
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  * :call VisualSearch('f')
vnoremap  # :call VisualSearch('b')
vnoremap  gv :call VisualSearch('gv')
"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 pw :call ShowPyDoc('', 1)
    nnoremap pW :call ShowPyDoc('', 1)
    nnoremap pk :call ShowPyDoc('', 0)
    nnoremap pK :call ShowPyDoc('', 0)
    " remap the K (or 'help') key
    nnoremap  K :call ShowPyDoc(expand(""), 1)
endfunction
if g:pydoc_perform_mappings
    call s:PerformMappings()
endif
" Commands
command -nargs=1 Pydoc       :call s:ShowPyDoc('', 1)
command -nargs=* PydocSearch :call s:ShowPyDoc('', 0)

Read Next