Vim Tips
Vim是个很神奇的东西。从很早以前就开始使用它作为我的默认文本编辑器。
Vim是Bram Moolenaar的作品,同时也是几乎在所有Unix平台上都带有的VI的功能增强版本。Vim其实就是个文本编辑器,与常用的UltraEdit、EditPlus一样,都是程序员们常用的代码编辑工具。
Vim能做什么?目前流行的文本编辑器所有的功能,Vim几乎都能做到。不仅如此,Vim是个可扩展的编辑器,想实现什么功能,都可以通过script完成。
Vim对于新手而言,是个超级难用的编辑器。完全和以往人们的想法不同。(虽然现在Vim也提供Easy Vim,但那毕竟不是Vim的灵魂。用Easy Vim也没办法完全体会Vim的神奇。)
Vim可以从它的主页上下载,并且是完全免费的。同时,Vim支持常见的操作系统,这当然包括Windows、Mac、Linux/Unix。对于Windows用户来说,我推荐在安装过程中选择Full。这样会在%SYSTEMROOT%中创建几个BAT文件,能够方便快速的启动Vim。
Vim的配置依赖于.vimrc文件,该文件存在于$HOME目录中(Windows系统中为%USERPROFILE%)。很奇怪的是,Windows不允许将文件重命名为以“.”开头,却允许以“.”开头的文件存在。
要创建以“.”开头的文件,只要随便创建一个文件,然后进入命令提示符中,输入“ren 文件名 .vimrc”就可以了。或者更简单,在命令提示符中输入“echo a > .a”,就可以得到一个以“.”开头的文件。
看到这里,其实你不用创建.vimrc。Vim也支持在Windows环境下使用_vimrc作为配置文件。另外,配置文件并不是必须,没有这个文件,仍然可以运行Vim,只是默认设置而已。
这里我不打算讨论Easy Vim,因为和普通的编辑器没有什么区别,而且反而不如其他编辑器好用。如果你愿意以Easy Vim入手,okay,go ahead,但下面的内容貌似没有什么太大的用处了。
Normal Mode
Vim是模式(Model)的,默认的启动状态为Normal Mode,除此之外,还有Insert Mode、Command Mode和Vistual Mode。在Normal Mode中,键盘的输入都会被作为操作命令,而不是输入的内容。试试看,在一个刚刚启动的Vim中,按键是不会得到想要的输入的。
Normal Mode是最基础的模式。任何情况下,如果想进入Normal Mode,只要按ESC键就可以。同样,如果不确定当前在什么模式下,可以通过通过按ESC键的方式(或反复按ESC键),返回最基础的Normal Mode。如果此时已经处于Normal Mode,再按下ESC,Vim会给出声音警告。这也就是判断当前处于Normal Mode的一个方法。
Tip:如果你遗失在某个模式中,反复按ESC键盘,最终是会回到Normal Mode中的。嘀嘀的声音也会提示你已经进入Normal Mode。
模式之间的切换
模式之间的切换,是相对容易的。前面已经提到,要回到Normal Mode,只要按ESC键。
要想能够写文字,则首先应该切换到Insert Mode中。在Normal Mode中,按下i,就可以进入Insert Mode,此时会在窗口的最下方显示“– INSERT –”。
Tip:Vim中的各个命令都是区分大小写的。i和I将是两个不同的功能。
编辑文本
在初始的Normal Mode中,按下进入插入模式的命令即可。这些命令包括:
a在当前字符右侧插入
i在当前字符位置插入
A在当前行尾插入
O在当前行上方插入空行
o在当前行下方插入空行
同样,使用替换命令也可以对文本进行修改:
r替换当前位置字符
R进入替换状态
对于修改大小写这样的工作,则更是可以使用简单的命令完成:
guu本行小写
gUU本行大写
移动光标
在Vim的Normal Mode中移动光标,除了可以使用上、下、左、右的方向键外,更为正宗和传统的是使用hjkl,其中h表示左移,l表示右移,j表示向下,k表示向上。这种设计可以让使用者不移动手指的位置,就可以操作光标。
Tip:同样的光标移动快捷键,也出现在Google各产品(如:Gmail、Reader等)。
需要提醒的是,hjkl只能在Normal Mode中移动光标。而在Insert Mode中,则会作为文本插入到光标的位置。
Vim的命令
Vim的命令是区分大小的,而且是可以组合的。不仅如此,还可以通过数字,指定命令的执行次数。例如:d是删除的主命令,dd表示删除本行,dw删除当前词(w表示操作对象为词,其他命令中也可能用到),d^删除到行首(^表示行首),d$删除到行尾($表示行尾),d4d或者4dd表示执行4次dd命令,d3w表示删除3个词,等等……
打开和关闭文件
写好了文本,需要保存。只要输入:w即可;如果文件未命名,则可以使用:w 文件名。默认的当前路径是$HOME(Windows环境下为%USERPROFILE%)。
打开已经存在的文件,可以使用如下命令:
:e 文件名
打开和保存文件,都支持Tab补全功能。只要输入文件或路径的开头几个字母,然后按下Tab键,就可以自动补全以此开头的文件或路径名,反复按Tab键可以在多个以此开头的名称中切换。
编辑完后,要退出Vim,只要使用
:q
命令也是可以组合的,比如要保存当前文件,并退出,可以使用
:wq
该命令也可以简化为ZZ。
命令也是可以强制执行的,如果当前文件没有保存,而使用:q退出,Vim会提示当前文件未保存。这时可以在命令后面加上“!”表示强制执行该命令(比如:q!表示不保存文本,强制退出)。同样的,如果:w也可以强制执行,表示文件即使是只读的,也强制保存。
搜索与替换
Vim的搜索可以说是强大。不仅支持正则表达式(RegEx),操作还非常方便。
搜索是以在Normal Mode中“/”开头的命令/{pattern}<CR>,例如搜索RegEx,只要在Normal Mode中输入:
/RegEx
需要注意的是,Vim默认情况下,搜索、替换都是区分大小写的,但也可以修改这项设置(:set ignorecase)。
而smartcase功能,则聪明的实现了根据你的输入来确定是否匹配大小写。例如:输入全小写单词会忽略大小写匹配功能,而任意字母大写,则会区分大小写匹配。
但是,如果只打算在一个字符上忽略大小写怎么办呢?只要在需要忽略大小写的字符前面加上\C即可,例如“/\CReg\cEx”,就会匹配RegEx、regex、Regex和regEx,但不会匹配REGEX。该选项会忽略smartcase和ignorecase的设定。
搜索还可以偏移,b表示开头,e表示结尾,正数表示右移,负数表示左移。例如:“/test/e-1”就会搜索test并把光标置为s字母上;同理,“/test/b+2”会移动到同样的位置上。
如果打开incsearch功能(set incsearch),则在输入要搜索文字的同时,就可以同步显示出找到的内容。
搜索完成后,n可以搜索下一次出现的{pattern},p可以搜索上一个。重复上次搜索,还可以直接输入“/<CR>”。
替换操作同样是这样完成,输入:{range}s[ubstitute]/{exp1}/{exp2}/{flags}。{range}可以是行号(1,2,3或者1-10),也可以是%表示所有行,如果没有写,则表示只替换当前行。{flag}是可选的,g表示替换每一行中所有的匹配项,c表示每次替换都需要confirm。
代码折叠
代码折叠功能是我印象中Vim7中增加的。挺早以前,Visual Studio、UltraEdit、Source Insight等软件中的代码折叠功能,都是非常好用,直到Vim也增加同样的功能。
Vim的代码折叠,除了可以根据语法外,还可以根据自定义的正则表达式来完成。
对于一般的程序代码而言,最简单的创建折叠的方法,就是按照代码语法创建折叠(set fdm=syntax)。之后可以zc折叠代码,zo打开代码。
剪贴板和寄存器
Vim中的每个字母都是一个寄存器,相当于有26个寄存器。寄存器既可以作为存储文本使用,也可以作为记录一组命令(宏)使用。
要复制一段选中的文本到寄存器,只要按下”{reg}y即可,其中{reg}可以是a~z的任何一个字母。不仅如此,我们还可以添加文本到寄存器中,方法是”{REG}y,区别就是把寄存器的名字变为大写。
要把寄存器的内容插入到文本中,需要按下”{reg}p。
这里有个特别的寄存器,就是+,它可以实现Vim和其他应用程序(Windows剪贴板或X11的剪贴板)的连接。所以,如果在Vim外用C+Insert(或CTRL+C)复制了文本,要粘贴到Vim中,只要按下”+p即可;反之,要把Vim的文本复制到其他应用程序中,则可以使用”+y。
如果要将Vim中的全文都复制到别的程序中,可以使用ggVG “+y。
寄存器还可以用来存储一组命令(宏),方法是q{reg},之后操作的命令都会被记录到这个寄存器中,@{reg}可以回放这组命令。同样的q{REG}可以向寄存器中添加命令。
如果对于整行的操作,复制文本也可以使用yy和p。
编写代码
当你用编辑器编写OO语言的程序(其实在普通的C语言中也可能),经常遇到每行的部分内容都一样的情况。例如,我们经常碰到这种情况:
Obj.Instance[0].Data.Set.test1 = 1; Obj.Instance[0].Data.Set.test2 = 2; Obj.Instance[0].Data.Set.test3 = 3; Obj.Instance[0].Data.Set.test5 = 4;
这种情况,Vim可以借助I命令很好的快速完成这些工作(UltraEdit也有类似的功能,叫做Column Mode)。
先写这样的代码,把每行不同的部分打出来就可以了:
1 = 1; 2 = 2; 3 = 3; 4 = 4;
然后把光标移动到左上角的1的位置,并在Normal Mode中按下CTRL+V进入VISUAL BLOCK Mode;按3次j移动到4上面,这是第一列的1~4都被选中了。接着按I,输入前面的那部分“Obj.Instance[0].Data.Set.test”,然后按下ESC键。是不是所有选中行都出现了呢?
当然,如果你只是重复上一行的输入的话,在Insert Mode下,直接按下CTRL+Y,就可以复制上一行上同位置的字符。
而代码中的缩进,可以使用下面的命令实现:
SHIFT+>>:缩进Tab
SHIFT+<<:左移一个Tab
生成目录
对于本文这样的文本,每个标题都是使用<H3>标记的,所以Vim同样支持针对这样的文本,生成目录。
:vimgrep/{pattern}/%:根据{pattern}取得目录,对于这个文档,可以是^<\Ch3>。
:cw:显示目录
:CTRL+W H:将目录显示在左侧
标签(Tags)
Tag需要另一个工具叫ctags,可以从SourceForge上下载(Unix下比较省心,不需要安装)。所谓Tag,实际上实现的功能就是代码跳转。
在实现代码跳转前,首先要调用ctags生成标签文件。命令很简单,下面的命令就是对当前目录下所有的C文件生成标签:
ctags *.c
具有标签文件后,只要在文件中按下CTRL+]就可以实现跳转。例如,代码中有个函数叫foo,如果看到别的地方调用这段代码,只要在调用的foo函数上按下ctrl+]就可以跳转到对应的定义位置。
.vimrc
.vimrc是Vim的配置文件,默认存放在$HOME中(Windows环境为%USERPROFILE%)。当然Vim并不一定要求配置文件一定存在上面的路径中,放在$VIMRUNTIME中也可以。
.vimrc可以提供对Vim的全部的配置,或者是对功能的扩充(可以写自己的script)。
这里提供一些配置:
对于GUI模式,自动展开语法菜单
" Make Syntax menu expended to show each language syntax in the menu
if has('gui_running')
let do_syntax_sel_menu=1
endif
设置UTF-8为默认编码方式
" Make UTF-8 as default encoding if $LANG !~ '\.' set encoding=utf-8 endif set fileencodings=ucs-bom,utf-8,gbk
对于未指定宽度的UTF-8编码文字,指定为双字节宽
设置本项,即可解决在UTF-8编码下,某些符号只显示半个
" Set Unknown char as double byte
" Refer to http://www-128.ibm.com/developerworks/cn/linux/l-tip-vim1/
if has('multi_byte') && v:version > 601
set ambiwidth=double
endif
设置不兼容VI
Vim提供更多的功能,设置为不兼容模式后,则可以使用这些功能
set nocompatible
设置自动缩进
set autoindent
设置CTRL+S为保存的快捷键
noremap :update vnoremap :update inoremap :update
打开当前文件目录中的文件
" Edit another file in the same directory as the current file
" uses expression to extract path from current file's path
if has("unix")
map ,e :e =expand("%:p:h") . "/"
else
map ,e :e =expand("%:p:h") . "\\"
endif
设置增量搜索,显示所有匹配项,并将F2作为取消高亮显示的快捷键
set incsearch set showmatch " Key mapping to stop the search highlight nmap :nohlsearch imap :nohlsearch
提供与其他应用程序复制粘贴的快捷键
" Key mapping to Cut/Copy/Paste from public register vnoremap "+y vnoremap "+x map "+p cmap "+p imap "+p vmap "+p
备份当前文件,并继续编辑
" backup current file with a new name then start edit
map s :up \| saveas! %:p:r-=strftime("%y%m%d")-bak \| 3sleep \| e #
重新对代码调整缩进
gg=G
我的.vimrc配置
" vim:shiftwidth=2:tabstop=8:expandtab
" Last Change: 2008-07-14 23:09:41
if v:version < 700
echoerr 'The .vimrc requires Vim 7 or later.'
quit
endif
if has('autocmd')
" Remove ALL autocommands for the current group
au!
endif
if has('gui_running')
let do_syntax_sel_menu=1
endif
" Set colorscheme, download from http://www.vim.org/scripts/script.php?script_id=2175 then copy to $VIMRUNTIME
colorscheme darkslategray
if has('multi_byte')
" Legacy encoding is the system default encoding
let legacy_encoding=&encoding
endif
if has('gui_running') && has('multi_byte')
" Set encoding (and possibly fileencodings)
if $LANG !~ '\.' || $LANG =~? '\.UTF-8$'
set encoding=utf-8
else
let &encoding=matchstr($LANG, '\.\zs.*')
let &fileencodings='ucs-bom,utf-8,' . &encoding
let legacy_encoding=&encoding
endif
endif
set nocompatible
source $VIMRUNTIME/vimrc_example.vim
set autoindent
set nobackup
set showmatch
set incsearch
set formatoptions+=mM
set fileencodings=ucs-bom,utf-8,gbk,default,latin1
set ls=2 "Always show status line
set go-=T "Do not show Toolbar
set go+=c "Use console dialog instead of popup dialog
set go+=R "Right-hand scrollbar is present when there is a vertically split window
set fdc=2
set report=0 "show which lines are modified in :command
set statusline=%<%f\ %h%m%r%=%k[%{(&fenc==\"\")?&enc:&fenc}%{(&bomb?\",BOM\":\"\")}]\ %-14.(%l,%c%V%)\ %P
set backspace=indent,eol,start
if has('mouse')
set mouse=a
endif
if has('multi_byte') && v:version > 601
" if v:lang =~? '^\(zh\)\|\(ja\)\|\(ko\)'
set ambiwidth=double
" endif
endif
" Key mappings to ease browsing long lines
noremap <C-J> gj
noremap <C-K> gk
noremap <C-Down> gj
noremap <C-Up> gk
inoremap <C-Down> <C-O>gj
inoremap <C-Up> <C-O>gk
noremap <M-Home> g0
noremap <M-End> g$
inoremap <M-Home> <C-O>g0
inoremap <M-End> <C-O>g$
" Key mappings to use shortcut from Win
noremap <C-S> :update<CR>
vnoremap <C-S> <C-C>:update<CR>
inoremap <C-S> <C-O>:update<CR>
" Edit another file in the same directory as the current file
" uses expression to extract path from current file's path
" (thanks Douglas Potts)
if has("unix")
map ,e :e <C-R>=expand("%:p:h") . "/" <CR>
else
map ,e :e <C-R>=expand("%:p:h") . "\\" <CR>
endif
" Tab support for Version 7 or above
if version > 700
" map <C-t> <C-O>:tabnew<CR>
" map <C-w> <C-O>:tabclose<CR>
" map <C-S-tab> <C-O>:tabp<CR>
" map <M-Left> <C-O>:tabp<CR>
" map <C-tab> <C-O>:tabn<CR>
" map <M-Right> <C-O>:tabn<CR>
endif
" Key mappings for quick arithmetic inside Vim
nnoremap <silent> <Leader>ma yypV:!calcu '<C-R>"'<CR>k$
vnoremap <silent> <Leader>ma yo<ESC>pV:!calcu '<C-R>"'<CR>k$
nnoremap <silent> <Leader>mr yyV:!calcu '<C-R>"'<CR>$
vnoremap <silent> <Leader>mr ygvmaomb:r !calcu '<C-R>"'<CR>"ay$dd`bv`a"ap
" Key mapping to stop the search highlight
nmap <silent> <F2> :nohlsearch<CR>
imap <silent> <F2> <C-O>:nohlsearch<CR>
" Key mapping to Cut/Copy/Paste from public register
vnoremap <C-Insert> "+y
vnoremap <S-Delete> "+x
map <S-Insert> "+p
cmap <S-Insert> "+p
imap <S-Insert> <C-O>"+p
vmap <S-Insert> "+p
" Key mapping for Select All
vnoremap <C-A> ggVG
map <C-A> ggVG
cmap <C-A> ggVG
imap <C-A> <C-O>ggVG
vmap <C-A> ggVG
" Key mapping for switch windows
nnoremap <C-S-Tab> <C-W>W
inoremap <C-S-Tab> <C-O><C-W>W
" Key mapping to maxium the window
if has('gui_running')
noremap <silent> <Leader>wx <C-O>:simalt ~x<CR>
noremap <silent> <Leader>wr <C-O>:simalt ~r<CR>
endif
" Key mappings to fold line according to syntax
nmap <silent> <F3> :set fdl=1 fdm=syntax<bar>syn sync fromstart<CR>
nmap <C-F3> zv
nmap <M-F3> zc
" Key mappings for quickfix commands, tags, and buffers
nmap <F11> :cn<CR>
nmap <F12> :cp<CR>
nmap <M-F11> :copen<CR>
nmap <M-F12> :cclose<CR>
nmap <C-F11> :tn<CR>
nmap <C-F12> :tp<CR>
nmap <S-F11> :n<CR>
nmap <S-F12> :prev<CR>
" Key mapping for the taglist.vim plugin
" These code needs TagList script which can be downloaded from http://www.vim.org/scripts/script.php?script_id=273
nmap <F9> :Tlist<CR>
imap <F9> <C-O>:Tlist<CR>
" Key mappings for the quickfix commands
nmap <F11> :cn<CR>
nmap <F12> :cp<CR>
" backup current file with a new name then start edit
map <silent> <Leader>s :up \| saveas! %:p:r-<C-R>=strftime("%y%m%d")<CR>-bak \| 3sleep \| e #<CR>
" Non-GUI setting
if !has('gui_running')
"English message only
language messages en
" Do not increase the windows width in the taglist.vim plugin
if has('eval')
let Tlist_Inc_Winwidth=0
endif
" Set text-mode menu
if has('wildmenu')
set wildmenu
set cpoptions-=<
set wildcharm=<C-Z>
nmap <F10> :emenu <C-Z>
imap <F10> <C-O>:emenu <C-Z>
endif
endif
if has('autocmd')
function! SetFileEncodings(encodings)
let b:my_fileencodings_bak=&fileencodings
let &fileencodings=a:encodings
endfunction
function! RestoreFileEncodings()
let &fileencodings=b:my_fileencodings_bak
unlet b:my_fileencodings_bak
endfunction
function! CheckFileEncoding()
if &modified && &fileencoding != ''
exec 'e! ++enc=' . &fileencoding
endif
endfunction
function! ConvertHtmlEncoding(encoding)
if a:encoding ==? 'gb2312'
return 'gbk' " GB2312 imprecisely means GBK in HTML
elseif a:encoding ==? 'iso-8859-1'
return 'latin1' " The canonical encoding name in Vim
elseif a:encoding ==? 'utf8'
return 'utf-8' " Other encoding aliases should follow here
else
return a:encoding
endif
endfunction
function! DetectHtmlEncoding()
if &filetype != 'html'
return
endif
normal m`
normal gg
if search('\c<meta http-equiv=\("\?\)Content-Type\1 content="text/html; charset=[-A-Za-z0-9_]\+">') != 0
let reg_bak=@"
normal y$
let charset=matchstr(@", 'text/html; charset=\zs[-A-Za-z0-9_]\+')
let charset=ConvertHtmlEncoding(charset)
normal ``
let @"=reg_bak
if &fileencodings == ''
let auto_encodings=',' . &encoding . ','
else
let auto_encodings=',' . &fileencodings . ','
endif
if charset !=? &fileencoding &&
\(auto_encodings =~ ',' . &fileencoding . ',' || &fileencoding == '')
silent! exec 'e ++enc=' . charset
endif
else
normal ``
endif
endfunction
function! GnuIndent()
setlocal cinoptions=>4,n-2,{2,^-2,:2,=2,g0,h2,p5,t0,+2,(0,u0,w1,m1
setlocal shiftwidth=2
setlocal tabstop=8
endfunction
function! RemoveTrailingSpace()
if $VIM_HATE_SPACE_ERRORS != '0' &&
\(&filetype == 'c' || &filetype == 'cpp' || &filetype == 'vim')
normal m`
silent! :%s/\s\+$//e
normal ``
endif
endfunction
function! UpdateLastChangeTime()
let last_change_anchor='\(" Last Change:\s\+\)\d\{4}-\d\{2}-\d\{2} \d\{2}:\d\{2}:\d\{2}'
let last_change_line=search('\%^\_.\{-}\(^\zs' . last_change_anchor . '\)', 'n')
if last_change_line != 0
let last_change_time=strftime('%Y-%m-%d %H:%M:%S', localtime())
let last_change_text=substitute(getline(last_change_line), '^' . last_change_anchor, '\1', '') . last_change_time
call setline(last_change_line, last_change_text)
endif
endfunction
" Function to insert the current date
function! InsertCurrentDate()
let curr_date=strftime('%Y-%m-%d', localtime())
silent! exec 'normal! gi' . curr_date . "\<ESC>l"
endfunction
" Key mapping to insert the current date
inoremap <silent> <C-\><C-D> <C-O>:call InsertCurrentDate()<CR>
" Highlight space errors in C/C++ source files (Vim tip #935)
if $VIM_HATE_SPACE_ERRORS != '0'
let c_space_errors=1
endif
" Use Canadian spelling convention in engspchk (Vim script #195)
let spchkdialect='can'
" Show syntax highlighting attributes of character under cursor (Vim
" script #383)
map <Leader>a :call SyntaxAttr()<CR>
" Automatically find scripts in the autoload directory
au FuncUndefined * exec 'runtime autoload/' . expand('<afile>') . '.vim'
" File type related autosetting
au FileType c,cpp setlocal cinoptions=:0,g0,(0,w1 shiftwidth=4 tabstop=8
au FileType diff setlocal shiftwidth=4 tabstop=4
au FileType html setlocal autoindent indentexpr=
au FileType changelog setlocal textwidth=76
" Text file encoding autodetection
au BufReadPre *.gb call SetFileEncodings('gbk')
au BufReadPre *.big5 call SetFileEncodings('big5')
au BufReadPre *.nfo call SetFileEncodings('cp437')
au BufReadPost *.gb,*.big5,*.nfo call RestoreFileEncodings()
au BufWinEnter *.txt call CheckFileEncoding()
" Detect charset encoding in an HTML file
au BufReadPost *.htm* nested call DetectHtmlEncoding()
" Recognize standard C++ headers
au BufEnter /usr/include/c++/* setf cpp
au BufEnter /usr/include/g++-3/* setf cpp
" Setting for files following the GNU coding standard
au BufEnter /usr/* call GnuIndent()
" Remove trailing spaces for C/C++ and Vim files
au BufWritePre * call RemoveTrailingSpace()
" Automatically update change time
"au BufWritePre *vimrc, *.vim call UpdateLastChangeTime()
" Mark .asm files MASM-type assembly
au BufNewFile,BufReadPre *.asm let b:asmsyntax='masm'
endif




Recent Comments