Berny's Knowledgebase als Newsfeed

Warum vi(m)?

vi ist der Texteditor, der sich auf wirklich jedem Unix-System findet - egal ob AIX, Solaris, HP-UX oder jedes beliebige Linux: Man kann sich (mit mindestens 99,9%iger Wahrscheinlichkeit) darauf verlassen, dass vi bereits im Grundsystem installiert ist. Die grundegende Bedienung sollte also jeder beherrschen, der mit Unix-artigen Betriebssystemen zu tun hat, denn damit kommt man immer weiter (z. B. um Konfigurationsdateien anzupassen).

vim (Vi IMproved) ist darüber hinaus ein extrem leistungsfähiger Texteditor. Er kann zumindest auf jedem Linux und darüber hinaus auf vielen anderen Unix-artigen Betriebssystemen nachinstalliert werden. Die Bedienung von beiden ist im Grunde weitgehend gleich, vim bietet einfach nur mehr Funktionen.

Einstieg in vim

Während man viele grafische Editoren einfach dadurch erlernen kann, dass man sie startet und in den vorhandenen Menüs die verschiedenen Funktionen anklickt, erfordert vim, dass man sich am Anfang zumindest einmal grundlegend mit seinen Funktionen beschäftigt. Es gibt sehr viele vim Einsteiger-Tutorials im Internet. Sehr gut gemacht ist aber auch das von vim mitgelieferte Tutorial "vimtutor". Viele Distributionen installieren vimtutor gleich mit, wenn man vim installiert, sonst in der Paketverwaltung danach suchen.

Einfach

vimtutor

auf der Kommmandozeile eingeben und das Tutorial startet.

Ich will nicht bei Adam und Eva anfangen und alles nochmal schreiben, was schon oft und gut beschrieben wurde, daher gehe ich im folgenden davon aus, dass die absoluten Basics von vim bekannt sind, d. h. dass Ihr z. B. den vimtutor schon mal durchgearbeitet habt.

The power of vim

Richtig eingesetzt kann vim viele Aufgaben schneller, effektiver und weniger fehleranfällig erledigen als andere Texteditoren - egal ob man damit jetzt einfache Texte, LaTeX, HTML, CSS, AsciiDoc oder Quellcode der verschiedensten Programmier- und Scriptsprachen bearbeitet.

Dazu sollte man einerseits ein paar nützliche Einstellungen vornehmen (normalerweise in ~/.vimrc) und andererseits ein paar interessante Befehle und Tastenkombinationen kennen. Ich habe mich dazu entschlossen, die beiden Dinge nicht in zwei unterschiedliche Files zu packen, sondern in ein gemeinsames. Damit kann man sich die .vimrc runterladen und in sein Home-Verzeichnis packen. Von dort läd vim die Einstellungen und man kann dort auch schnell mal nachschauen, wenn man einen Befehl sucht - sogar direkt von vim aus und ohne das aktuelle Dokument zu verlassen, z. B. mit

:tabnew ~/.vimrc

Gerade zum Nachschlagen ist es bestimmt nützlich, dass die einzelnen Abschnitte in vim auf- und zugeklappt werden können.

Berny’s vimrc nutzen

Meine vimrc steht zum Download zur Verfügung unter http://pub.stroessenreuther.info/vimrc/vimrc und muss direkt in Eurem Homeverzeichnis als .vimrc abgelegt werden. Das bekommt Ihr am einfachsten hin mit:

wget -O ~/.vimrc http://pub.stroessenreuther.info/vimrc/vimrc
Warning
Vorsicht:

Wenn Ihr Euch schon eine .vimrc angelegt habt, mit eigenen Inhalten, wollt Ihr diese vielleicht vorher wegsichern.

Wer meine vimrc nutzt, hat dazu grundsätzlich zwei Möglichkeiten:

Berny’s vimrc als Vorlage

Ihr könnt meine vimrc einfach als "Inspiration" nutzen, diese also einmalig runterladen und so lange anpassen, bis Eure eigene vimrc daraus geworden ist, mit allen Einstellungen, die Euch persönlich am besten gefallen.

Berny’s vimrc immer aktuell

Meine vimrc includiert am Ende eine Datei

~/.vimrc.local

Damit habt Ihr prinzipiell auch die Möglichkeit, Eure persönlichen Einstellungen in dieser Datei vorzunehmen und meine vimrc unverändert zu lassen. Das bietet Euch die Chance, später eine aktualisierte Version meiner vimrc runterzuladen, ohne dass Eure persönlichen Einstellungen verloren gehen. Da .vimrc.local immer ganz am Ende geladen wird, gelten im Zweifelsfall die Einstellungen die da drin stehen (sie überschreiben bei Bedarf die Werte aus meiner vimrc).

Berny’s vimrc

Hier schon mal zum vorab lesen:

"   .--pathogen------------------------------------------------------------.
"   |                        _   _                                         |
"   |            _ __   __ _| |_| |__   ___   __ _  ___ _ __               |
"   |           | '_ \ / _` | __| '_ \ / _ \ / _` |/ _ \ '_ \              |
"   |           | |_) | (_| | |_| | | | (_) | (_| |  __/ | | |             |
"   |           | .__/ \__,_|\__|_| |_|\___/ \__, |\___|_| |_|             |
"   |           |_|                          |___/                         |
"   '----------------------------------------------------------------------'

" pathogen (https://github.com/tpope/vim-pathogen)
" uses ~/.vim/autoload and ~/.vim/bundle for easy installation of addons
if filereadable($HOME . "/.vim/autoload/pathogen.vim")
    execute pathogen#infect()
endif

"#.
"   .--tabs + trailing characters------------------------------------------.
"   |    _        _                   _             _ _ _                  |
"   |   | |_ __ _| |__  ___     _    | |_ _ __ __ _(_) (_)_ __   __ _      |
"   |   | __/ _` | '_ \/ __|  _| |_  | __| '__/ _` | | | | '_ \ / _` |     |
"   |   | || (_| | |_) \__ \ |_   _| | |_| | | (_| | | | | | | | (_| |     |
"   |    \__\__,_|_.__/|___/   |_|    \__|_|  \__,_|_|_|_|_| |_|\__, |     |
"   |                                                           |___/      |
"   '----------------------------------------------------------------------'

" put 4 blanks when typing tab
set expandtab
set tabstop=4
set shiftwidth=4

" use:
" :retab      to convert all literal tabs to blanks according to the current
"             settings (e. g. like above)

" make the settings (about tabs and trailing characters) below work on every encoding
" according to
" http://stackoverflow.com/questions/18321538/vim-error-e474-invalid-argument-listchars-tab-trail
scriptencoding utf-8
set encoding=utf-8

" Show tabs and trailing characters.
"set listchars=tab:»·,trail:·,eol:¬
set listchars=tab:»·,trail:·
set list

"#.
"   .--autocommands (asciidoc syntax highlighting, puppet-lint as make cmd).
"   |                          _                           _               |
"   |               __ _ _   _| |_ ___   ___ _ __ ___   __| |              |
"   |              / _` | | | | __/ _ \ / __| '_ ` _ \ / _` |              |
"   |             | (_| | |_| | || (_) | (__| | | | | | (_| |              |
"   |              \__,_|\__,_|\__\___/ \___|_| |_| |_|\__,_|              |
"   '----------------------------------------------------------------------'

" define syntax highlighting for asciidoc files
autocmd BufRead,BufNewFile *.ad,*.asciidoc
        \ setlocal autoindent expandtab tabstop=8 softtabstop=2 shiftwidth=2 filetype=asciidoc
        \ textwidth=70 wrap formatoptions=tcqn
        \ formatlistpat=^\\s*\\d\\+\\.\\s\\+\\\\|^\\s*<\\d\\+>\\s\\+\\\\|^\\s*[a-zA-Z.]\\.\\s\\+\\\\|^\\s*[ivxIVX]\\+\\.\\s\\+
        \ comments=s1:/*,ex:*/,://,b:#,:%,:XCOMM,fb:-,fb:*,fb:+,fb:.,fb:>

" use puppet-lint as make command for puppet files
" put results into the quickfix list (by defining it's format as errorformat)
autocmd FileType puppet
        \ setlocal makeprg=puppet-lint\ --with-filename\ %
        \ errorformat=%f\ -\ %m\ on\ line\ %l

" type in *.pp files
" :make                to execute puppet-lint on the current file
"                      (jumps to the line of first ERROR or WARNING)
" :cn(ext) / :cp(rev)  to jump to the next / previous
" :cnf(ile)            to jump to the first match in the next file
" :cpf(ile)            to jump to the last match in the previous file
" :cfirst  / :clast    to jump to the first / last
" :copen   / :cclose   open / close a window showing the complete quickfix list
"                      <CR> in this window jumps to the given line
"
"                      see section 'split window' below on how to jump
"                      between quickfix window and the window with your code

"#.
"   .--autocomplete--------------------------------------------------------.
"   |                _                                  _      _           |
"   |     __ _ _   _| |_ ___   ___ ___  _ __ ___  _ __ | | ___| |_ ___     |
"   |    / _` | | | | __/ _ \ / __/ _ \| '_ ` _ \| '_ \| |/ _ \ __/ _ \    |
"   |   | (_| | |_| | || (_) | (_| (_) | | | | | | |_) | |  __/ ||  __/    |
"   |    \__,_|\__,_|\__\___/ \___\___/|_| |_| |_| .__/|_|\___|\__\___|    |
"   |                                            |_|                       |
"   '----------------------------------------------------------------------'

" For autocompletion always use content of ~/.vimdictionary too.
" You might want to put any word into this file you always want to have
" available for autocompletion. Depends on what kind of files you edit often.
" If you e. g. often edit Puppet classes maybe put Puppet's keywords here.
set dictionary=~/.vimdictionary
set complete+=k
" use <Ctrl-n> or <Ctrl-p> to navigate
" <Ctrl-e> to abort

"#.
"   .--block folding-------------------------------------------------------.
"   |      _     _            _       __       _     _ _                   |
"   |     | |__ | | ___   ___| | __  / _| ___ | | __| (_)_ __   __ _       |
"   |     | '_ \| |/ _ \ / __| |/ / | |_ / _ \| |/ _` | | '_ \ / _` |      |
"   |     | |_) | | (_) | (__|   <  |  _| (_) | | (_| | | | | | (_| |      |
"   |     |_.__/|_|\___/ \___|_|\_\ |_|  \___/|_|\__,_|_|_| |_|\__, |      |
"   |                                                          |___/       |
"   '----------------------------------------------------------------------'

" Start block with   .--   end block with   #.
" You might want to use the figheader script
" (http://git.mathias-kettner.de/git/?p=check_mk.git;a=blob;f=doc/helpers/figheader)
" for this, like I did for producing the paragraph headings in this file.

set foldmethod=marker
set foldmarker=.--,#.

" type za to fold/unfold one block
" zR to unfold all blocks, zM to fold all

"#.
"   .--macros and shortcuts------------------------------------------------.
"   |                 _ __ ___   __ _  ___ _ __ ___  ___                   |
"   |                | '_ ` _ \ / _` |/ __| '__/ _ \/ __|                  |
"   |                | | | | | | (_| | (__| | | (_) \__ \                  |
"   |                |_| |_| |_|\__,_|\___|_|  \___/|___/                  |
"   '----------------------------------------------------------------------'

" Insert prepared textblocks.
" For this to work you need a script called textblock in your PATH,
" that just prints the wanted textblock to STDOUT. The makros read them
" into the current file (at cursor position).

let @h=':r !textblock puppet-header
'
let @x=':r !textblock puppet-header-xml
'
let @f=':r !textblock puppet-file
'
let @d=':r !textblock puppet-dir
'
let @l=':r !textblock puppet-link
'
let @s=':r !textblock puppet-service
'
let @e=':r !textblock puppet-exec
'

" record macros

" qa<keys to record>q     to record macro a
"                         (you may use other letters for more macros)
" @a                      to replay macro a

" qA<keys to record>q     to append more keys to macro a
"                         (with lower case a it would be overwritten)


" switch paste mode on and off
set pastetoggle=<f5>

"#.
"   .--split window--------------------------------------------------------.
"   |                _ _ _              _           _                      |
"   |      ___ _ __ | (_) |_  __      _(_)_ __   __| | _____      __       |
"   |     / __| '_ \| | | __| \ \ /\ / / | '_ \ / _` |/ _ \ \ /\ / /       |
"   |     \__ \ |_) | | | |_   \ V  V /| | | | | (_| | (_) \ V  V /        |
"   |     |___/ .__/|_|_|\__|   \_/\_/ |_|_| |_|\__,_|\___/ \_/\_/         |
"   |         |_|                                                          |
"   '----------------------------------------------------------------------'

" <Ctrl-w>s         split horizontal
" :split            split horizontal
" :split <file>     split horizontal and load <file>

" <Ctrl-w>v         split vertical
" :vsplit           split vertical
" :vsplit <file>    split vertical and load <file>

" <Ctrl-w>w    or   <Ctrl-w><Ctrl-w>    to switch between windows
" <Ctrl-w>h         one window left
" <Ctrl-w>j         one window down
" <Ctrl-w>k         one window up
" <Ctrl-w>l         one window right

" <Ctrl-w>_         maximum height for current window
" <Ctrl-w>|         maximum width for current window
" <Ctrl-w>=         equal size for all windows

"#.
"   .--tabbed view---------------------------------------------------------.
"   |       _        _     _              _         _                      |
"   |      | |_ __ _| |__ | |__   ___  __| | __   _(_) _____      __       |
"   |      | __/ _` | '_ \| '_ \ / _ \/ _` | \ \ / / |/ _ \ \ /\ / /       |
"   |      | || (_| | |_) | |_) |  __/ (_| |  \ V /| |  __/\ V  V /        |
"   |       \__\__,_|_.__/|_.__/ \___|\__,_|   \_/ |_|\___| \_/\_/         |
"   '----------------------------------------------------------------------'

" :tabnew <file>     open a new tab and load <file>
" gt                 to switch to next tab
" gT                 to switch to previous tab

" vim -p <file1> <file2> ...    to start vim with each file in one tab

"#.
"   .--colours-------------------------------------------------------------.
"   |                            _                                         |
"   |                   ___ ___ | | ___  _   _ _ __ ___                    |
"   |                  / __/ _ \| |/ _ \| | | | '__/ __|                   |
"   |                 | (_| (_) | | (_) | |_| | |  \__ \                   |
"   |                  \___\___/|_|\___/ \__,_|_|  |___/                   |
"   '----------------------------------------------------------------------'

set background=light
syntax on

" color settings for autocomplete menu
" refer to http://vim.wikia.com/wiki/VimTip634
" section 'Color numbers for xterm and gvim'
" for selecting colors
" and http://vimdoc.sourceforge.net/htmldoc/syntax.html
" for the items (Pmenu, PmenuSel, ...)
highlight Pmenu ctermfg=0 ctermbg=3 gui=bold
highlight PmenuSel ctermfg=8 ctermbg=4 gui=bold

" Colored or not colored
" If you want color you should set this to true
let color = "true"
if has("syntax")
    if color == "true"
        " This will switch colors ON
        so ${VIMRUNTIME}/syntax/syntax.vim
    else
        " this switches colors OFF
        syntax off
        set t_Co=0
    endif
endif

"#.
"   .--misc----------------------------------------------------------------.
"   |                                  _                                   |
"   |                        _ __ ___ (_)___  ___                          |
"   |                       | '_ ` _ \| / __|/ __|                         |
"   |                       | | | | | | \__ \ (__                          |
"   |                       |_| |_| |_|_|___/\___|                         |
"   '----------------------------------------------------------------------'

" We use a vim (and not good old vi) and want to really use vim's features
set nocompatible

" when using tab completion on ex commands: show list of possible completions
set wildmenu
set wildmode=full

" keep more than default of 20 entries in the history of ex commands
set history=100

" do not activate visual mode on mouse selection
set mouse-=a

"#.
"   .--movements-----------------------------------------------------------.
"   |                                                      _               |
"   |        _ __ ___   _____   _____ _ __ ___   ___ _ __ | |_ ___         |
"   |       | '_ ` _ \ / _ \ \ / / _ \ '_ ` _ \ / _ \ '_ \| __/ __|        |
"   |       | | | | | | (_) \ V /  __/ | | | | |  __/ | | | |_\__ \        |
"   |       |_| |_| |_|\___/ \_/ \___|_| |_| |_|\___|_| |_|\__|___/        |
"   '----------------------------------------------------------------------'

" h/j/k/l       left/down/up/right one character / one line

" w/W           next word / WORD
" b/B           back one word / WORD
" e/E           end of current word / WORD
"               where "WORD" includes punctuation, while "word" does not

" f<Character>  jump to next occurance of <Character> in current line
" F<Character>  jump to previous occurance of <Character> in current line
" ;             repeat search in same direction
" ,             repeat search in oposit direction

" g;            jump to the position of last change (can be used multiple times)
" g,            jump backward
" gi            jump to the position of last insert and change to insert mode

" gf            (goto file): open file with filename under the cursor,
"               <Ctrl-o> to return to original file

" m[a-zA-Z]     set mark
" '[a-zA-Z]     jump back to mark
"               where marks [a-z] are per buffer and [A-Z] are global

" %             jump to matching brace - works with () [] and {}

"#.
"   .--tipps + tricks------------------------------------------------------.
"   |      _   _                           _        _      _               |
"   |     | |_(_)_ __  _ __  ___     _    | |_ _ __(_) ___| | _____        |
"   |     | __| | '_ \| '_ \/ __|  _| |_  | __| '__| |/ __| |/ / __|       |
"   |     | |_| | |_) | |_) \__ \ |_   _| | |_| |  | | (__|   <\__ \       |
"   |      \__|_| .__/| .__/|___/   |_|    \__|_|  |_|\___|_|\_\___/       |
"   |           |_|   |_|                                                  |
"   '----------------------------------------------------------------------'

" <Ctrl-g>    show info about current file

" gv          re-select the last visual selection

" zt          puts current line to top of screen
" z.  or  zz  puts current line to center of screen
" zb          puts current line to bottom of screen

" daw         delete one word
" ciw         change one word
"             with
" iw          current word only
" aw          current word + one space
" iW / aW     current WORD
" is / aS     current sentence
" ip / ap     current paragraph only / + one blank line

"#.
"   .--netrw---------------------------------------------------------------.
"   |                               _                                      |
"   |                    _ __   ___| |_ _ ____      __                     |
"   |                   | '_ \ / _ \ __| '__\ \ /\ / /                     |
"   |                   | | | |  __/ |_| |   \ V  V /                      |
"   |                   |_| |_|\___|\__|_|    \_/\_/                       |
"   |                                                                      |
"   '----------------------------------------------------------------------'

" you can use vim with netrw plugin to edit files on a remote system
" (included directory browsing)

" # start browsing in your HOME directory
" vim scp://remotehost.example.com/

" # start browsing in root directory
" vim scp://remotehost.example.com//

" # edit a concrete file
" # (in this case: relative to HOME)
" vim scp://remotehost.example.com/data/test.txt

"#.
"   .--copy & paste--------------------------------------------------------.
"   |                               ___                     _              |
"   |       ___ ___  _ __  _   _   ( _ )    _ __   __ _ ___| |_ ___        |
"   |      / __/ _ \| '_ \| | | |  / _ \/\ | '_ \ / _` / __| __/ _ \       |
"   |     | (_| (_) | |_) | |_| | | (_>  < | |_) | (_| \__ \ ||  __/       |
"   |      \___\___/| .__/ \__, |  \___/\/ | .__/ \__,_|___/\__\___|       |
"   |               |_|    |___/           |_|                             |
"   '----------------------------------------------------------------------'

" normally yy, dd, p use the unamed register, also known as "
" prefix with    "<register>    to use named register <register>
" e. g.          "ayy
" and later      "ap
"                ""yy    is the same as    yy    because unnamed register is "

"                "_      is the black whole register
" therefor       "_dd    really deletes
"                        (and does not cut the line into the unnamed register)

"                "0      is the copy register: it is only filled while yank (y)
"                        actions, not while d, x or c
" therefor       "0p     pastes what you copied, even if you did e. g. delete
"                        action (dd, ...) in the mean time

"                "+      represents the system clipboard
"                "*      represents the X11 mouse clipboard
"                "%      always contains the name of the current file (read only)

" :reg                   lists content of all registers

" <Ctrl-r><register>     pastes in insert mode
" <Ctrl-r>"              pastes unnamed register
" <Ctrl-r>0              pastes copy register

"#.
"   .--search/replace------------------------------------------------------.
"   |                          _        __              _                  |
"   |  ___  ___  __ _ _ __ ___| |__    / / __ ___ _ __ | | __ _  ___ ___   |
"   | / __|/ _ \/ _` | '__/ __| '_ \  / / '__/ _ \ '_ \| |/ _` |/ __/ _ \  |
"   | \__ \  __/ (_| | | | (__| | | |/ /| | |  __/ |_) | | (_| | (_|  __/  |
"   | |___/\___|\__,_|_|  \___|_| |_/_/ |_|  \___| .__/|_|\__,_|\___\___|  |
"   |                                            |_|                       |
"   '----------------------------------------------------------------------'

" highlight all search results
set hlsearch
" :noh    or   :nohlsearch   to temporary disable highlighting
"                            (until next search)

" show first hit while typing the search string
set incsearch

" star (*) and hash (#) in visual mode do a search for the visual selection
xnoremap * :<C-u>call <SID>VSetSearch()<CR>/<C-R>=@/<CR><CR>
xnoremap # :<C-u>call <SID>VSetSearch()<CR>?<C-R>=@/<CR><CR>

function! s:VSetSearch()
  let temp = @s
  norm! gv"sy
  let @/ = '\V' . substitute(escape(@s, '/\'), '\n', '\\n', 'g')
  let @s = temp
endfunction

" /\v<regex>            Search term is used as (mostly like Perl style) regex
"                       braces () and curly braces {} have special meanings
"                       with \v you might want to use:
"                       \d   instead of   [0-9]
"                       \a   instaed of   [A-Za-z]
"                       \l   instead of   [a-z]
"                       \u   instaed of   [A-Z]
"                       and see
"                       :h /character-classes
"                       for more predefined character classes

" /\V<literal text>     Search term is used literally
"                       no character has a special meaning except backslash \
"                       especially: dot . means a literal dot,
"                       not 'any character'

" :s/<source>/<target>/<flags>
"                       with flags in
"                       g    global (replace all matches, not only the first
"                            in the line
"                       c    ask for every match befor replacing it
"                            while being asked you have the following answer
"                            possibilities
"                            y       yes, replace this one
"                            n       no, do not replace this one
"                            q       quit
"                            l       last - replace this one and quit
"                            a       all  - replace this one and all further

" intelligent guess if search should be case sensitiv or not
" if searchpattern includes upper case letters: search case sensitive
" if not: search case insensitive
set ignorecase
set smartcase

" /\c<search string>    explicitly search case insensitive
" /\C<search string>    explicitly search case sensitive

"#.
"   .--(vim)grep-----------------------------------------------------------.
"   |              __      _         __                                    |
"   |             / /_   _(_)_ __ ___\ \  __ _ _ __ ___ _ __               |
"   |            | |\ \ / / | '_ ` _ \| |/ _` | '__/ _ \ '_ \              |
"   |            | | \ V /| | | | | | | | (_| | | |  __/ |_) |             |
"   |            | |  \_/ |_|_| |_| |_| |\__, |_|  \___| .__/              |
"   |             \_\                /_/ |___/         |_|                 |
"   '----------------------------------------------------------------------'

" :g/<string>/d               delete all lines matching <string>
"                             (think of g like grep)
" :v/<string>/d               delete all lines NOT matching <string>
"                             (think of v like grep -v)
"
"                             for patterns in <string> see section
"                             'search/replace' above

" :grep <string> *            uses external grep command to find <string>
" :grep <string> **/*.txt     in all given files, opens matching files in vim
"                             and adds matching lines to the quickfix list
"
" :grep -v <string> *         same with non matching lines

" :vim[grep] /<string>/[g] *  internal vim function working similar, but with
"                             vim style search patterns
"                             g    multiple entries in the quickfix list if
"                                  there is more than one match in one line

" on how to navigate in the quickfix list see section autocmd above

"#.
"   .--.vimrc.local--------------------------------------------------------.
"   |                _                      _                 _            |
"   |         __   _(_)_ __ ___  _ __ ___  | | ___   ___ __ _| |           |
"   |         \ \ / / | '_ ` _ \| '__/ __| | |/ _ \ / __/ _` | |           |
"   |          \ V /| | | | | | | | | (__ _| | (_) | (_| (_| | |           |
"   |         (_)_/ |_|_| |_| |_|_|  \___(_)_|\___/ \___\__,_|_|           |
"   '----------------------------------------------------------------------'

" in ~/.vimrc.local you can do your own or local settings
" e. g. not managed by Puppet
if filereadable($HOME . "/.vimrc.local")
    source ~/.vimrc.local
endif

"#.
" vim:filetype=vim