[Vim] Learn Vimscript the Hard Way 筆記

軟體:Vim
官方電子書
中文版

簡介:記錄各章的重點

0. 預備知識
可用此指令得知 .vimrc 位置
:echo $MYVIMRC
1. 打印信息
:echo "Hello, world!"

:echom "Hello again, world!"
:messages
2. 設置選項
//打開
:set <name>
:set number
//關閉
:set no<name>
:set nonumber
//切換
:set <name>!
:set number!
//設定值
:set <name>=<value>
:set numberwidth=10
//目前的值
:set <name>?
:set number?
3. 基本映射
map 的註解會失效
:map <space> viw " Select word
4. 模式映射
會形成 recursive 的 map,不建議使用
// 建立
*map
nmap
vmap
imap
// 取消
*unmap
nunmap
vunmap
iunmap
5. 精確映射
任何情況都使用 noremap
// 建立
*noremap
nnoremap
vnoremap
inoremap
// 取消
*unmap
nunmap
vunmap
iunmap
6. Leaders
:let mapleader = ","
:let maplocalleader = "\\"
7. 編輯你的 Vimrc 文件
:nnoremap <leader>ev :vsplit $MYVIMRC<cr>
:nnoremap <leader>sv :source $MYVIMRC<cr>
8. Abbreviations
:iabbrev waht what
:cnoreabbrev W! w!
:abbrev both Both
help abbrev
9. 更多的 Mappings
viw<esc>a"<esc>hbi"<esc>lel
10. 鍛鍊你的手指
:inoremap <esc> <nop>
11. 本地緩衝區的選項設置和映射
:nnoremap <buffer> <leader>x dd
:setlocal wrap
12. 自動命令
:autocmd BufWritePre,BufRead *.html :normal gg=G
:autocmd FileType python nnoremap <buffer> <localleader>c I#<esc>
:help autocmd-events
13. 本地緩衝區縮寫
:autocmd FileType python     :iabbrev <buffer> iff if:<left>
:autocmd FileType javascript :iabbrev <buffer> iff if ()<left>
14. 自動命令組
// 同樣的命令會被重覆建立
:autocmd BufWrite * :sleep 200m
:autocmd BufWrite * :sleep 200m
:autocmd BufWrite * :sleep 200m

// 善用 Group
:augroup testgroup
:    autocmd!
:    autocmd BufWrite * :echom "Cats"
:augroup END
15. Operator-Pending 映射
:onoremap p i(
// <c-u> 清空命令
:onoremap in( :<c-u>normal! f(vi(<cr>
16. 更多 Operator-Pending 映射
:normal gg
:execute "normal! ?^==\\+$\r:nohlsearch\rkvg_"<cr>
17. 狀態條
:set statusline=%f         " 文件的路徑
:set statusline+=\ -\      " 分隔符
:set statusline+=FileType: " 標籤
:set statusline+=%y        " 文件的類型

:set statusline=%l         " 當前行號
:set statusline+=/         " 分隔符
:set statusline+=%L        " 總行數

:help statusline
18. 負責任的編碼
:setlocal wrap
// 不要在 ~/.vimrc 或者是編寫的插件中使用縮寫
:setl wrap
19. 變量
:let foo = "bar"
:echo foo
:set textwidth=80
:echo &textwidth
// 本地變數
:let &l:number = 1
// 存入 register a
:let @a = "hello!"
// " 是 “unnamed” register,在複製的時候沒有指定 register 的文本都會放到這裡
:echo @"
20. 變量作用域
:help internal-variables
21. 條件語句
:echom "foo" | echom "bar"

// 10
:echom "hello" + 10
// 20
:echom "10hello" + 10
// 10
:echom "hello10" + 10

:if 0
:    echom "if"
:elseif "nope!"
:    echom "elseif"
:else
:    echom "finally!"
:endif
// output:
// finally!
22. 比較
// == 的行為取決於用戶的設置
:set noignorecase
:if "foo" ==? "FOO"
:    echom "first"
:elseif "foo" ==? "foo"
:    echom "second"
:endif

:set ignorecase
:if "foo" ==# "FOO"
:    echom "one"
:elseif "foo" ==# "foo"
:    echom "two"
:endif

:help expr4
23. 函數
// 如果一個 Vimscript 函數不返回一個值,它隱式返回 0
:function TextwidthIsTooWide()
:  if &l:textwidth ># 80
:    return 1
:  endif
:endfunction
24. 函數參數
// 不能對參數變量重新賦值
:function Varg2(foo, ...)
:  echom a:foo
:  echom a:0
:  echom a:1
:  echo a:000
:endfunction

:call Varg2("a", "b", "c")
25. 數字
// 十進位
:echom 100
// 十六進位
:echom 0xff
// 八進位
:echom 017

:echo 100.1
:echo 15.45e-2
// 出錯,小數點和小數點後面的數字是必須要有的
:echo 5e10
26. 字符串
:echom "Hello, " . "world"
:echom "foo\\bar"
// 等同 python 的 r""
:echom '\n\\'

:help i_CTRL-V
27. 字符串函數
:echom strlen("foo")
:echom len("foo")
:echo split("one,two,three", ",")
:echo join(split("foo bar"), ";")
:echom tolower("Foo")
:echom toupper("Foo")

:help functions
28. Execute 命令
:execute "rightbelow vsplit " . bufname("#")
29. Normal 命令
:normal G
// 避免映射
:normal! G
30. 執行 normal!
:execute "normal! mqA;\<esc>`q"
31. 基本的正則表達式
:execute "normal! gg" . '/\vfor .+ in .+:' . "\<cr>"
32. 實例研究:Grep 運算符 (Operator),第一部分
:nnoremap <leader>g :silent execute "grep! -R " . shellescape(expand("<cWORD>")) . " ."<cr>:copen<cr>
33. 實例研究:Grep 運算符 (Operator),第二部分
nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
vnoremap <leader>g :<c-u>call GrepOperator(visualmode())<cr>

function! GrepOperator(type)
    if a:type ==# 'v'
        normal! `<v`>y
    elseif a:type ==# 'char'
        normal! `[v`]y
    else
        return
    endif

    silent execute "grep! -R " . shellescape(@@) . " ."
    copen
endfunction
34. 實例研究:Grep 運算符 (Operator),第三部分
nnoremap <leader>g :set operatorfunc=<SID>GrepOperator<cr>g@
vnoremap <leader>g :<c-u>call <SID>GrepOperator(visualmode())<cr>

function! s:GrepOperator(type)
    let saved_unnamed_register = @@

    if a:type ==# 'v'
        normal! `<v`>y
    elseif a:type ==# 'char'
        normal! `[v`]y
    else
        return
    endif

    silent execute "grep! -R " . shellescape(@@) . " ."
    copen

    let @@ = saved_unnamed_register
endfunction
35. 列表
:echo ['a', 'b', 'c', 'd', 'e'][0:2]
// ['a','b','c']

:echo ['a', 'b', 'c', 'd', 'e'][0:100000]
// ['a', 'b', 'c', 'd', 'e']

:echo ['a', 'b', 'c', 'd', 'e'][-2:-1]
// ['d','e']

:echo ['a', 'b', 'c', 'd', 'e'][:1]
// ['a','b']

:echo ['a', 'b', 'c', 'd', 'e'][3:]
// ['d','e']

help List
help add()
help len()
help get()
help index()
help join()
help reverse()
36. 循環
:help for
:help while
37. 字典
// 鍵會被強制轉換成字符串
:echo {'a': 1, 100: 'foo'}

:echo {'a': 1, 100: 'foo',}['a']
:echo {'a': 1, 100: 'foo',}[100]
:echo {'a': 1, 100: 'foo',}.a
:echo {'a': 1, 100: 'foo',}.100

:let foo = {'a': 1}
:let foo.b = 200

:let test = remove(foo, 'a')
:unlet foo.b

:help Dictionary
:help get()
:help has_key()
:help items()
:help keys()
:help values()
38. 切換
nnoremap <leader>q :call QuickfixToggle()<cr>

let g:quickfix_is_open = 0

function! QuickfixToggle()
    if g:quickfix_is_open
        cclose
        let g:quickfix_is_open = 0
        execute g:quickfix_return_to_window . "wincmd w"
    else
        let g:quickfix_return_to_window = winnr()
        copen
        let g:quickfix_is_open = 1
    endif
endfunction
39. 函數式編程
function! Append(l, val)
    let new_list = deepcopy(a:l)
    call add(new_list, a:val)
    return new_list
endfunction

function! Pop(l, i)
    let new_list = deepcopy(a:l)
    call remove(new_list, a:i)
    return new_list
endfunction

:let funcs = [function("Append"), function("Pop")]
:echo funcs[1](['a', 'b', 'c'], 1)

function! Mapped(fn, l)
    let new_list = deepcopy(a:l)
    call map(new_list, string(a:fn) . '(v:val)')
    return new_list
endfunction
40. 路徑
:echom expand('%')
:echom expand('%:p')
:echom fnamemodify('foo.txt', ':p')

:echo split(globpath('.', '*'), '\n')
42. 舊社會下的插件配置方式
基本配置方式
~/.vim/colors/
~/.vim/plugin/
~/.vim/ftdetect/
~/.vim/ftplugin/
~/.vim/indent/
~/.vim/compiler/
~/.vim/after/
~/.vim/autoload/
~/.vim/doc/
43. 新希望:用 Pathogen 配置插件
:help runtimepath
44. 檢測文件類型
:help ft
:help setfiletype
45. 基本語法高亮
if exists("b:current_syntax")
    finish
endif

syntax keyword potionKeyword loop times to while
syntax keyword potionKeyword if elsif else
syntax keyword potionKeyword class return

syntax keyword potionFunction print join string

highlight link potionKeyword Keyword
highlight link potionFunction Function

let b:current_syntax = "potion"

//=========================================================
:help group-name
46. 高級語法高亮
// 因為分開了- 和 -= 的正則表達式,必須在定義 - 之後才定義 -=
syntax match potionOperator "\v\*"
syntax match potionOperator "\v/"
syntax match potionOperator "\v\+"
syntax match potionOperator "\v-"
syntax match potionOperator "\v\?"
syntax match potionOperator "\v\*\="
syntax match potionOperator "\v/\="
syntax match potionOperator "\v\+\="
syntax match potionOperator "\v-\="

:help syn-priority
47. 更高級的語法高亮
syntax region potionString start=/\v"/ skip=/\v\\./ end=/\v"/
highlight link potionString String

:help syn-region
48. 基本摺疊
:help foldmethod
49. 高級摺疊
:setlocal foldmethod=indent
:echom foldlevel(1) // 0
:echom foldlevel(2) // 1

a           0
    b       1
    c       1
        d   2
        e   2
    f       1
g           0

//==============================================================
setlocal foldmethod=expr
setlocal foldexpr=GetPotionFold(v:lnum)

function! NextNonBlankLine(lnum)
    let numlines = line('$')
    let current = a:lnum + 1

    while current <= numlines
        if getline(current) =~? '\v\S'
            return current
        endif

        let current += 1
    endwhile

    return -2
endfunction

function! GetPotionFold(lnum)
    if getline(a:lnum) =~? '\v^\s*$'
        return '-1'
    endif

    let this_indent = IndentLevel(a:lnum)
    let next_indent = IndentLevel(NextNonBlankLine(a:lnum))

    if next_indent == this_indent
        return this_indent
    elseif next_indent < this_indent
        return this_indent
    elseif next_indent > this_indent
        return '>' . next_indent
    endif
endfunction

//==============================================================
:help fold-expr
50. 段移動原理
:help [[
:help ]]
:help []
:help ][
51. Potion 段移動
/factorial/e

:help search()
52. 外部命令
:!ls
// 執行後可能需要 :redraw!
:silent !echo Hello, world.

// ========================================================================
if !exists("g:potion_command")
    // 無反應的話,請確認 runtimepath
    let g:potion_command = "potion"
endif

nnoremap <buffer> <localleader>b :call PotionShowBytecode()<cr>

function! PotionShowBytecode()
    " Get the bytecode.
    let bytecode = system(g:potion_command . " -c -V " . bufname("%") . " 2>&1")

    " Open a new split and set it up.
    vsplit __Potion_Bytecode__
    normal! ggdG
    setlocal filetype=potionbytecode
    setlocal buftype=nofile

    " Insert the bytecode.
    call append(0, split(bytecode, '\v\n'))
endfunction
53. 自動加載
// autoload/myplugin/somefile.vim
:call myplugin#somefile#Hello()

:help autoload
// 步驟
// 1. 它首先是否已經存在同名的函數了。如果是,就調用它
// 2. 否則,查找名字對應的文件,並 source 它
// 3. 然後試圖調用那個函數。如果成功,太棒了。如果失敗,就輸出一個錯誤
// 可利用不存在的 function 重新加載,搭配 silent 使用
54. 文檔
:help help-writing

留言