沒穿方服

封存

顯示╱隱藏內文

寫個 script 在 shell 透過 rhino.jar 跑 fulljslint.js,在 Vim 也寫個 function 呼叫它,於是可以用 quickfix window 除錯。

就像有個超級不吝指教的真人陪你寫程式

  • 在 Windows 上沒用。
  • 不使用 jslint.vim。 雖然跨系統、放 jslintrc 設定檔等作法很棒,但是 1) 即時檢查和 highlight 標錯的顯示方式都不是我要的,甚至有點惱人。 2) 想試試直接用大師寫的 fulljslint.js,在 Vim 以外的地方可能也較好整合。
  • 不使用 javaScriptLint.vim,因為 JavaScript Lint 不是 JSLint 啊,跟這主題無關。
  • 使用 Rhino 當 JavaScript engine,因為最好裝。
    不過它的 JavaScript 版本只實作到 1.7(Firefox 2 的程度)所以像 JSON 就不能直接吃,要多 include 個 JSON2.js
  • 具體用到的檔案:
    • ~/bin/jslint (唯一實際被叫的指令,內容主要是 java -cp rhino.jar …)
    • ~/scripts/fulljslint.js (即 Douglas Crockford 的 fulljslint.js)
    • ~/scripts/jslint.js (由官網的 rhino.js(現已移除)修改而來,連接 fulljslint 和 rhino)
    • ~/scripts/json2.js
    • ~/scripts/rhino.jar
  • 修改錯誤訊息的格式,改成跟 closure compiler 相似,就不必再調 Vim 的 errorformat。
  • 目前 JSLint 用的預設 option 直接寫在呼叫 fulljslint.js 用的 script(jslint.js)裡。
  • 在 shell 中呼叫的指令是 jslint file.js '{"extraOptionKey":value}'。 extraOption 可以用來蓋掉預設選項。
  • 在 Vim 中呼叫的指令是 :JSLint

取得檔案

工作滿一年

  • 還經常冒出剛找到工作的爽爽心情
    • 回家在公車上會忍不住偷笑。(不過可以搭公車的這間倒了……)
    • 從洗手間走回辦公室也莫名地樂。
    • 應該是同事都很夢幻的緣故。
  • 剛開始還覺得上班跟自己在家做的事差不多,就是有錢拿
    • 久了技術有進步,不過作品都不能表達自己,這麼說也就沒什麼了不起的事。
    • 所以不上班也可能更好。
  • 某些場合還是會發現自己的缺點
    • 以前發現的、不能改的就當作人格特質,所以年紀到了就不該有新的缺點才是。
    • 這時發現技術不能解決的問題就毀了。

此外未滿七個月就拿不到失業救濟金這回事,也是難得的爛經驗。

左邊會顯示 mark 位置

在沒有規則的位置間移動

雖然 ``ga 等跳轉指令有不少,但是要跳到沒特殊意義的位置——例如「剛剛有看到,不過具體看到什麼完全沒印象」——這種情形除了 mark 應該也沒有更好的解了。

於是用 mamb、……放了幾個 mark,結果更難用:

  1. 視覺上看不見哪兒有 mark,所以不知道某 mark 的位置,比方 `b 會跳到哪裡去。
    事實上按了 ` 之後,就會卡在不知道要按哪個字母。
  2. 不知道哪些 mark 已經用過,例如下 md 的時候要擔心會不會蓋掉舊的。
    事實上按了 m 之後,就會卡在不知道要按哪個字母。

ShowMarks plugin

ShowMarks 會利用 sign column(在行號左邊的一欄,預設不會有)顯示 mark 的位置。雖然是很老的 plugin,不過先進替代品從缺,功能也冇問題,就用吧。

唯一不滿是用了 CursorHold autocmd 在閒置時自動開啟(如果開啟檔案後,突然畫面左邊冒出一條 bar,就是這功能),所以 fork 一份 bootleq/ShowMarks,加上 let g:showmarks_auto_toggle=0 即關閉它。

wokmarks.vim plugin

wokmarks.vim 提供不必記憶 mark 名稱(例:a-z)的操作。指令會變成 Prev、Next、Set、Toggle、Kill 這樣的感覺;另外設置 mark、在 mark 間移動時也會在狀態列顯示訊息。

具體設定

gist: 738007 - [vimrc] ShowMarks and wokmarks.vim- GitHub

完成後,看到有感覺的行就按 mm 放標記(sign column 會自動開啟),覺得這行沒用就按 mm 取消標記,想瀏覽標過的地方就按 mjmk 前後移動。若新開的檔案沒有顯示任何標記,就按 momt 顯示。

限制

因為使用 Vim 本身的 mark,所以 mark 總數有限(a-zA-Z,其中大寫的又不適合單一檔案使用)。

如果真的需要 26 個以上,也有一些 plugin 會另行記錄標記資訊,不過「不用 Vim 的 mark」是否比較好,就要考慮一下了。

煩惱許久的需求—— git difftool, open all diff files immediately, not in serial - Stack Overflow。具體狀況是:用 git-difftool 呼叫 vimdiff 看變更,會重複「2 files to edit ~ 自動進入 Vim 比較一個檔案 ~ 手動離開 Vim」這個流程,有 10 個(變更的)檔案就會重複 10 次,而且中間不知道怎麼停下來……

原文的解法我沒試成功,而是靠之前寫的 function,加上在 .zshrc 裡設定:

alias gitdiffall='vim -p `git diff --name-only` -c "tabdo GitDiff" -c "tabfirst"'

之後只要輸入指令 gitdiffall 即可。

檔案改到一半發現自己失憶,忘記改了哪些東西,只好跟上次 git push 的狀態比較看看了。

正統作法是在 shell 下指令 git diff,或下 git difftool 用 vimdiff 分割視窗顯示,如下圖,前者在檔案多時容易快速概觀,但單一檔案時還是 vimdiff 方便。

狀況單純的話也很好用 缺點是一次只 diff 一個檔案

目前看到兩個插件 hypergitvcscommand 都可以在 Vim 裡直接顯示 diff 結果,前者是 git-diff 型式,後者是 vimdiff 型式(取出舊版內容,再切割視窗作 vimdiff)。我的需求比較接近後者,但又不想裝整支 vcscommand,於是擅自取出原始碼,改寫進 vimrc。

用到的 git 指令

  • git rev-parse --show-prefix
    若指令來自 git 的子目錄,輸出該目錄對 .git 的相對路徑。
  • git log -1 --format=format:%s\ %n%h' -- '%an' -- '%ai\ '('%ar') HEAD
    顯示 HEAD 所在的一條 log,輸出格式為 [主題] \n[hash 縮寫] -- [作者] -- [日期] ([相對日期]),
    例如 fefe3c8 -- bootleq -- 2010-08-30 18:14:37 +0800 (13 hours ago)
  • git show HEAD:some_path/some_file
    顯示 HEAD 中 some_file 的檔案內容。

原始碼

gist: 558647 - [.vimrc] git diff.

功能說明

  • GitDiff 指令

    輸入 :GitDiff 即可切割視窗顯示目前檔案的 HEAD 版本,可加一個參數指定更早的 HEAD,例如 :GitDiff 99 取得 HEAD~99 版本。顯示後會進入 diff 模式。另外可用 echo t:git_diff_info 顯示該 commit 的簡易資訊。
    GitDiff t:git_diff_info 顯示
    註:指令取的 HEAD~ 並無限制「有變更當前檔案」所以可能往前跳好幾個 HEAD 也看不出變化。
  • MyDiffOff 指令

    因為預設的 :diffoff 指令可能會把本來的 foldmethod 等 diff 相關選項蓋掉,所以改用 :MyDiffOff 來關閉 diff。
    不過也只是設回我的預設值,沒有真的建立暫存/重設機制。
  • GitDiffOff 指令

    進入 :GitDiff 後,用 :GitDiffOff 快速恢復到原始狀態。(目前只支援從兩視窗的狀態復原)

Vim 簡短入門提示

艱難學習至今,提出三項。


<Esc> 難按

切換 normal mode 是最硬的基本技,<Esc> 不快你立馬被打爆。
可以用 <C-[> 取代,或配合 <Leader>(預設是 \,Enter 鍵是一字或 L 型會影響其手感)或 <LocalLeader> 鍵。
例:按兩下 , 即進入 normal mode

let maplocalleader = ","
noremap  <LocalLeader>, <C-\><C-N>
noremap! <LocalLeader>, <C-\><C-N>

早裝早輕鬆的插件

GSession 配合 git branch(不過 Windows 下比較難使用 git)是管理 session 的快樂方案。參考 Vim-Taiwan 上的討論串
因為多檔編輯需求大,但 session 操作繁瑣,所以認定首要插件就此一支。


學會查 help

大量招式(睇 :help quickref)不是單純常用就能學會的,已知指令也值得翻文複習。畢竟軟體演變下來,誰比較懂得善用靠的還是知識。

  • 更新 help

    如果 Vim 版本很舊,可考慮更新整個 $VIMRUNTIME(包含 help 以外的各種檔案,所以會花點時間)。
    以下為系統有 aap 支援的情形:
    :!cd $VIMRUNTIME
    :!aap -f ftp://ftp.vim.org/pub/vim/runtime/main.aap fetch
    另外安裝插件等操作動到 doc 檔案時,也需要重建一下 tag(讀 help 時用來 jump 的、類似超連結的東西)
    :helptags ~/.vim/doc
    (其中 ~/.vim/doc 可能是其他位置,即放說明文件的地方)
  • 益於求助的 map

    預設 <F1> 等同於 :help 其實不易利用,可改為:

    • 於 normal mode 查詢游標所在單字的說明。
    • 於 visual mode 查詢選取文字的說明,對付 'sb' 這般有引號的項目較方便。
    • 以上若先按 <LocalLeader> 則將說明開在新分頁。
    map  <F1> :help <C-R>=expand('<cword>')<CR><CR>
    map  <LocalLeader><F1> :tab help <C-R>=expand('<cword>')<CR><CR>
    xmap <F1> :<C-U>call RegStash(1)<CR>gvy:let b:tempReg=@"<CR>:call RegStash()<CR>:help <C-R>=b:tempReg<CR><CR>
    xmap <LocalLeader><F1> :<C-U>call RegStash(1)<CR>gvy:let b:tempReg=@"<CR>:call RegStash()<CR>:help <C-R>=b:tempReg<CR><CR>
    (其中 RegStash 來自暫存與還原 register 的 function,也可移掉)

    在 help 文件中,預設可用 <C-]> 跳到游標所在單字的說明(類似超連結),按 <C-O><C-I> 則是在次次跳轉的位置來回(類似上一頁、下一頁),這裡也可稍微 map:

    • 若檔案唯讀(help 文件皆唯讀),就讓 Enter 也可跳進 tag、BS 也可回上一頁。
    • Alt-LeftAlt-Right 當成上一頁、下一頁使用。
    nnoremap <expr> <CR> &modifiable ? "i<CR><C-\><C-N>" : "<C-]>"
    nnoremap <expr> <BS> &modifiable ? "i<C-W><C-\><C-N>" : "<C-O>"
    nnoremap <M-Left> <C-O>
    nnoremap <M-Right> <C-I>

註:以上只是讓動作變簡單,要真正愛用 help,還是要親身體會、自求多福~

部分操作會將相關資訊暫存備用,例如 d 掉的文字會被放進 register 變數,以便之後貼上。

執行 vim script(例如自訂的 map)時若做了這類操作,便可能把本來的 register 等資料蓋掉。以下幾個 function 先將暫存資訊另存,以供 script 結束後還原。

原始碼

gist: 555806 - [.vimrc] simple functions to save/restore position, register or mark.

使用例

:call PosStash(1)   " 暫存目前游標位置
:call PosStash()    " 還原游標位置
:call RegStash(1)   " 暫存目前 register
:call RegStash()    " 還原 register
:call MarkStash(1)  " 暫存目前 mark
:call MarkStash()   " 還原 mark

一般選擇連續多行的註解時,會按 V 進入 visual mode,然後移動游標框住想要的;行數一多,懶惰就出現了。

選取整個註解區是件懊懆事

以下簡陋模擬一個註解用的 text object,方便在 normal mode 按 vac 選取整塊註解(不含空行);同樣按 dac 刪除、yac 複製。

gist: 554634 - [.vimrc] Simple text object for continuous comment

實作上只判斷每行的第一個非空白字元是否被 syntax highlight 為 Comment,所以並不十分可靠,也僅能做 linewise 的選取。若要改善功能,可考慮導入 'comments''commentstring''filetype' 等選項(參考 EnhancedCommentifytComment 的做法),不過複雜度的增加也令人卻步。

附 tComment 用的 map,若游標在註解區就反轉整塊註解,否則照常 :TComment。

map <silent><expr> <LocalLeader>ca IsInComment() ? "vac:TComment<CR>" : ":TComment<CR>"

以前都靠 PSPad 用來製作 bookmarklet 的簡易 script,現在改讓 Vim 處理。

:Bookmarklet 沒錯誤的話即出現此視窗

這個 funcion 需要上一篇的 Vim funcion:使用 closure compiler 壓縮 js 文件,還有 Vim 的 ruby 支援(其實只是用來 encodeURI ……)

原始碼展開如下,但以後更新會放在 gist: 546828 - [.vimrc] make bookmarklet

command! -nargs=* Bookmarklet call Bookmarklet(<f-args>)
fun! Bookmarklet(...)
  let result = JsCompress(0, 0, '--compilation_level=WHITESPACE_ONLY')
  if len(getqflist()) == 0 && strlen(result) > 0
    let reuse_win = 0
    for winnr in tabpagebuflist(tabpagenr())
      if bufname(winnr) == '[Bookmarklet]'
        exec winnr . 'wincmd w'
        let reuse_win = winnr
      endif
    endfor
    if ! reuse_win
      exec 'belowright 5new [Bookmarklet]'
      setlocal noswapfile bufhidden=wipe winfixheight filetype=javascript
    endif
    let result = substitute(result, '[\n]', '', '')
    setlocal buftype=
    call setline(1, 'javascript:(function(){' . result . '})();')
    if has('ruby')
      ruby require("uri"); VIM::Buffer.current.line = URI.escape(VIM::Buffer.current.line);
    else
      echohl WarningMsg | echomsg "No ruby support. Can't do URI encoding." | echohl None
    endif
    setlocal buftype=nofile
  endif
endf

註:僅測試於 Cygwin console Vim + Windows 版的 java,其他環境只測過 Win32 gVim ——失敗。

通用的解決方案請參考 othree 寫的 Vim 儲存完 JavaScript 檔案後自動用 yuicompressor
本篇特徵為:

  • 只使用 google closure-compiler
  • 採用 :make 執行,故能以 Quick Fix 視窗除錯。

    warning_level 設 DEFAULT 的話,壓 MooTools 居然不過……

  • 壓縮前未存檔、實行壓縮前、……幾個場合能先詢問確認。

    不存檔是壓什麼意思的? 若不詢問,每次 :w 就自動跑也是很費時的

  • 壓縮結果可選擇存到改名的檔案(傳回檔名)或存到暫存檔(傳回壓縮後的文字)。
    自動命名規則為 foo.js → foo.min.js 或 foo-debug.js → foo.js。

使用方式

  1. 把本文原始碼加進 .vimrc。 gist: 545665 - [.vimrc] function to use closure-compiler
  2. 下載 closure-compiler 將 jar 放到程式找得到的地方(如程式第一行 let jar = '/scripts/google-compiler-20100616.jar'
  3. 幾種用法:
    • function:call JsCompress(save [, interact [, options ]])
      save: 若為 1 則自動命名存檔,0 則存到暫存檔。
      interact: 若為 1,執行 make 前會詢問確認。
      options: compiler 用的參數字串,例如 '--compilation_level=WHITESPACE_ONLY'。這裡留意若不指定的話, warning_level 用的並非預設值而是 QUITE,因為 warnning 就會有 quickfix 而中斷壓縮。
      :call JsCompress(1, 0, '--warning_level=DEFAULT')
    • command:JsCompress[!] [interact] [options]
      同上但 save 參數被 ! (bang) 取代。
      :JsCompress! 1 '--compilation_level=WHITESPACE_ONLY'
    • autocmd:在 .vimrc 加上 au FileWritePost,BufWritePost *-debug.js :JsCompress! 1
      以後編完 foo-debug.js 存檔,便會詢問是否壓縮。

已知問題

中文檔名在 make 出錯時,quickfix 和產生的新檔是亂碼。

懷疑是 Windows java 的問題所以先不處理

Color scheme: bootleg

用了半年的 Vim,一向認為預設的 slate 加上少許自訂就相當管用,不必再創招煩人了。
直到開始調 gVim,才發現 gui 配色跟 term 實在差很多……
(對付此問題可參考依云寫的 gui2term.py,由 gui 轉出 256 term 的配色。 ——只可惜我的需求是 term2gui)

8/22 放上自訂的 colorscheme bootleg(不是 bootleq 喔)

  • 對稱性:只支援 256 色 term 和 gui,且盡量讓兩者顯示效果相同。
    這裡會特別處理粗體(bold):gui 不使用粗體,cterm 以 MinTTY 預設顯示為準(粗體會被轉為亮色)。
  • 保守:基本配色是從 slate(256 色 term,非 gui 的)繼承而來。
  • 易讀:暗色背景、高對比。
    如果還是有字感覺太暗,歸咎於螢幕設定不同吧。
  • 個人調整:最後原則還是以個人使用為重,像 Pmenu、Diff 的顏色都跟 slate 沒有關聯了。另外還有加幾個自訂 highlight group(主要是 TabLine 系列)若沒特別配合調過,這些都是沒用的。

原始碼

gist: 543652 - Vim colorscheme: bootleg

擷圖參考 (cterm/gui)

畫面很亂不全是配色的錯啊 gui 較少用,問題可能較多

另付敝人調色時愛用的寶貝色表

這圖我也忘記怎麼做的了

Vim Tips Wiki 已經有一篇 CamelCase to under lined and vice versa 提供 visual mode 用的兩份 map。

這篇則是在 normal mode 取得游標所在的 word,若符合 CamelCase 或 under_score 型式便直接變更文字。

特別考慮游標不在 keyword 上的情形,例如 <camelCase>,游標在 < 上也能轉換。
但游標在行尾時,因為 non-blank 的字皆被 <cword> 接受,就先不研究怎麼排除 non-keyword 了。

原始碼:gist: 541723 - [.vimrc] toggle between CamelCased/under_scored word

TYPE IV~沒穿方服~ 從地下的屍體放置場冒出來的僵屍,即使已經死了,但還保留著吃東西的本能。 
文獻來自 PS 恐怖遊戲之非官方印刷品。

遊戲結局數分鐘前才登場的 lo-fi 多邊形喪屍,已經失去 9 成以上的嚇人能力,然後惡靈古堡就爆炸了。

跟洋館的怪物相比實在是很無味的角色,幸好有個多麼好聽的名字而被人記住。

編譯沒有 gui 的 Vim 7.3,無特殊需求,純升級。

  1. ftp.vim.org/pub/vim/unix/ 下載 unix 用的原始碼 vim-7.3.tar.bz2,解開後進入 vim73 目錄。
  2. 直接選 big 包的 features(包括 multi_byte 等沒理由不要的功能)。
    ./configure --with-features=big

(也可加 --enable-rubyinterp 等 optional feature,詳見 ./configure --help)

  1. make
  2. make install

之後下 vim --version 應該就能看到新設定。


部份相關設定(非全部的新功能)

vimrc 基本設定部份

if version >= 703
  set conceallevel=1
  set concealcursor=nc
  set colorcolumn=+1
  set cinoptions+=L0
  set undofile
  set undodir=~/.vim/undofiles
  if !isdirectory(&undodir)
    call mkdir((&undodir, "p")
  endif
  map  <C-ScrollWheelDown> <ScrollWheelRight>
  map  <C-ScrollWheelUp>   <ScrollWheelLeft>
  imap <C-ScrollWheelDown> <ScrollWheelRight>
  imap <C-ScrollWheelUp>   <ScrollWheelLeft>
endif
  • conceal 的設定以不影響排版為主。下圖說明:

    上半 normal mode,下半 insert mode

    以 help 檔為例,平時只要把 |:autocmd| 左右的「|」塗黑,不必真的移除。
    但游標移到該行時,因為 CursorLine 有上深灰,所以能識別有文字被隱藏掉。
    最後在 insert  mode(如圖下半部)或  visual mode,應照實顯示原文。

  • colorcolumn 在 textwidth 右邊標顏色。
    從 PSPad 轉到 Vim 的時候就想要這功能,不過出來以後效果挺醜的(也太麤了吧),所以最後配合 colorscheme 調成只有在游標所在的行能看見。前面擷圖中最右邊黑 column 即是。
  • L 是 cindent 新增的設定值,參考 :help cinoptions-values。
    另有新增 J 給 JavaScript 用,不過似乎預設就包含了。
  • undofile 沒設定的話就完全沒效果……
    這可是讓 :e! 或下次進入 Vim 後還能 undo 的便利功能啊。
  • 新增的水平 ScrollWheel 系列,測試沒效,先放置不管。

偵測到檔案類型為 help 時要跑的設定(help_rc 這種寫法是參考 c9s 的 gist 學來的)

fun! s:help_rc()
  set number
  if version >= 703
    set conceallevel=1
    set concealcursor=nc
    set colorcolumn=+1
  endif
endf
au FileType help :call s:help_rc()

因 help 類型檔案會重設 conceal 設定所以為之。

colorscheme

if version >= 703
  hi Conceal ctermfg=240 ctermbg=Black guifg=#585860 guibg=Black
  hi ColorColumn ctermbg=Black guibg=Black
endif

為了前述效果所用的顏色設定(換個跟我不一樣的 colorscheme 就不適用了。)

FuzzyFinder 找分頁

如果用了之前寫的 Vim 分頁列(tabline)設定,script 空間就會出現變數 s:tabLineTabs,內含所有分頁編號、檔名等資料。

於是可以用 FuzzyFinder 尋找分頁,例如 map <Leader>fg 叫出下面選單:

僅供參考,某些檔案是沒法用 Vim 編輯的

接著按 <CR> 便會跳到該分頁,按 <LocalLeader><CR> 則是關閉該分頁。(當然 g:fuf_keyOpen 系列變數要先設好)


原始碼展開如下(放在 .vimrc)

let g:fuf_tabListener = {}

function! g:fuf_tabListener.onComplete(item, method)
  let l:tabnr = matchstr(a:item, '\d\+')
  if a:method == 4
    silent exec 'normal ' . l:tabnr . 'gt'
  else
    silent exec 'tabclose ' . l:tabnr
  endif
endf

function g:fuf_tabListener.onAbort()
endf

function! g:fuf_tabFinder()
  if exists("s:tabLineTabs")
    let l:tabList = []
    for tab in s:tabLineTabs
      let label = tab.n . '. ' . (strlen(tab.split) > 0 ? ('(' . tab.split . ')') : '') . tab.flag . tab.filename
      if tab.n == tabpagenr()
        let label = '*' . label
      endif
      call add(l:tabList, label)
    endfor
    call fuf#callbackitem#launch('', 1, 'tabs>', g:fuf_tabListener, l:tabList, 0)
  endif
endf

nnoremap <LocalLeader>fg :call g:fuf_tabFinder()<CR>

若沒有用之前寫的 tabline 設定,也可嘗試取該 tabline 原始碼產生 s:tabLineTabs 的部分來用;或從 :tabs 抓分頁資料,再仿照 用 FuzzyFinder 尋找 register 自己建立分頁資料 list。

預設分頁列的不足

  1. 無顯示分頁編號。想用 Ngt 跳到第 N 個分頁時,不易判斷 N 要下多少。
  2. 分頁很多時,超出畫面外的分頁無法顯示。(gVim 會顯示捲動按鈕,故無此問題)
原始的 tabline

修改方式

修改方式是重寫 tabline 這個選項,實際上頗為麻煩,因為整條 tabline 必須一次輸出,而非設定個別 tab 即可。參考 :help setting-tabline,其中給的例子也只提示基本功能而已。

受惠於 Vim 7.3 的幾個進化,這才把 tabline 調成比較堪用的狀態。調整如下:

  1. 顯示分頁編號
  2. 變更了分頁文字內容
    • 僅顯示檔名(不含路徑)
    • 顯示是否修改過(+)和分頁中的分割視窗數(n),大致上和預設相同
    • 新 buffer 顯示 '[New]' ,無檔案的 buffer 顯示 '[Scratch]'
  3. 不顯示關閉按鈕(原本擺在分頁列尾端的 'X')
  4. 使用幾個額外的自訂 highlight group
  5. 可指定分頁最小/最大寬度
  6. 分頁過多時,可指定是否自動平均分頁寬度;且可指定此狀況下的最小分頁寬度
  7. 可指定分頁過長而被截短時,要補上去的省略字串(例:'…'
  8. 可指定目前分頁左右,至少要顯示幾個分頁

原始碼

gist: 523783 - Vim tabline setting

console 擷圖

最後分頁第一個 ~ 來自 strCrop(中文不能只砍半個字),第二個 ~ 來自分頁的截短

gui 擷圖(「分割視窗數」使用了斜體)

前後都有省略符號

選項

因為沒寫成 plugin,只是放在 .vimrc 的 function,所以請直接改原始碼的下列項目:

  • let tabMinWidth = 0
    最小分頁寬度(0: 不限)
  • let tabMaxWidth = 40
    最大分頁寬度(0: 不限)
  • let tabMinWidthResized = 15
    設定 tabDivideEquel 有效時,採用的最小分頁寬度
  • let tabScrollOff = 5
    目前分頁左右至少顯示幾個分頁
  • let tabEllipsis = '…'
    分頁過長而被截短時,要顯示的替代文字
  • let tabDivideEquel = 0
    分頁總長超出畫面時,是否自動均分各分頁寬度

已知問題

在 Vim 7.3e BETA 測試,全型字和自訂 highlight 有時會造成 tabline 重繪不正確。有先發到 vim-dev 再看看情況。
不幸地 Vim 7.3f 狀況仍然存在。

為什麼要 Vim 7.3?

為了計算字串的實際顯示寬度(column 數),需用到新增的 strwidth() 函數。(目前程式若找不到此函數,就會改用 strlen(),計算全型字寬度時結果便不正確)
註:關於全型半型甚至無法確定的狀況,可參考 LGJ Notes | 全型字(Full-Width)、半型字(Half-Width)

此外 Vim 7.3 還新增了滑鼠拖曳分頁調整順序的功能,比起以往只能用 :tabmove N 方便得多。

相關配套調整

  • 永久顯示分頁列
    set showtabline=2
  • gui 也使用純文字的 tabline
    set guioptions-=e
  • 未知寬度的字以兩個 column 顯示
    一般中文字型建議設定 set ambiwidth=double 以避免 '…' 等不定寬度的字顯示錯誤。
  • 相關 highlight group
    因為增加了新的 gorup 而非使用預設值,所以需特別設定下列 highlight。
    建議放在自己的 colorscheme 裡面,或寫成 function 在 autocmd ColorScheme 時呼叫,否則根據經驗,Vim 讀取 session 後自訂的 highlight 會被重設。
    hi TabLine           cterm=underline ctermfg=15    ctermbg=242   gui=underline guibg=#6c6c6c guifg=White
    hi TabLineSel        cterm=bold      gui=NONE      guifg=White
    hi TabLineNr         cterm=underline ctermbg=238   guibg=#444444
    hi TabLineNrSel      cterm=bold      ctermfg=45    guifg=#00d7ff
    hi TabLineFill       cterm=reverse   gui=reverse
    hi TabLineMore       cterm=underline ctermfg=White ctermbg=236   gui=underline guifg=White   guibg=#303030
    hi TabLineSplitNr    cterm=underline ctermfg=148 ctermbg=240   gui=underline,italic guifg=#afd700   guibg=#6c6c6c
    hi TabLineSplitNrSel cterm=NONE      ctermfg=148 ctermbg=236   gui=NONE,italic      guifg=#afd700   guibg=#303030

Vim 外掛程式 FuzzyFinder 實在方便。(但這篇不含介紹)

除了找檔案、buffer、help 等 13 目標外,亦有 API 讓其他資料享用其搜尋介面。

以下使用 FuzzyFinder 的 Callback-Item 模式,列出所有 register,選取項目並開啟時,便直接貼上 register 內容,或噴到一個新視窗(可編輯後再手動 :%y x 存回 x register)。 尚有毛病但還堪用。

具體用例:

  • <LocalLeader>fp → 開啟 registers 選單。選好按 <CR> 貼上,或按 <LocalLeader><CR> 開到新視窗。
  • <LocalLeader>fP → 同上,但貼在游標之前。

選單展開(看得出顯示有問題)

單行太長時,顯示有問題

將 @7 內容開到新視窗

上面視窗即 @7 的內容


程式碼展開如下(放在 .vimrc)

let g:fuf_regListener = {}
let g:fuf_regListener.putBefore = 0   " 0: p, 1: P

function! g:fuf_regListener.onComplete(item, method)
  let l:regName = strpart(a:item, 1, 1)
  if a:method == 4
    silent exec 'normal "' . l:regName . (g:fuf_regListener.putBefore ? 'P' : 'p')
  else
    exec '7new [@' . escape( l:regName, '"' ) . ']'
    setlocal noswapfile buftype=nofile bufhidden=wipe
    exec '0put=@' . l:regName
    redraw
    setlocal nomodified
  endif
endf

function! g:fuf_regListener.onAbort()
endf

function! g:fuf_regFinder(putBefore)
  let g:fuf_regListener.putBefore = a:putBefore
  redir => l:regs
  silent exec ':registers'
  redir END
  let l:regList = split(l:regs, '\n')
  let l:regList = filter(l:regList, 'v:val =~ "' . escape('\m^".\s\{3,}\S\+', '\"') . '"')  " remove non-register lines
  "let l:regList = map(l:regList, 'substitute(v:val, "\\m.\\{' . &columns . '}\\zs.*", "...", "")')  " has problem with long line
  call fuf#callbackitem#launch('', 1, 'registers>', g:fuf_regListener, l:regList, 0)
endf

nnoremap <LocalLeader>fp :call g:fuf_regFinder(0)<CR>
nnoremap <LocalLeader>fP :call g:fuf_regFinder(1)<CR>

其中 onCompletemethod 參數,取決於選擇項目時按了哪個 FuzzyFinder 的 mapping(詳見 |fuf-options|):

  • 1 → 普通開啟
  • 2 → split 開啟
  • 3 → vsplit 開啟(我 map 到 <LocalLeader><CR>
  • 4 → tab 開啟(我 map 到 <CR>

程式在 register 單行內容很長時會有問題:選單顯示錯誤/無法取得 register 名稱。把後面截斷可解決(但截掉部份當然搜不到)。暫且加上 nowrap 減少狀況:autocmd BufEnter \[fuf\] setlocal nowrap


幾支 register 相關 plugin,也許這才是你要的:

  • YankRing.vimp 貼完以後,按 <CTRL-P> 可以立刻換成其他剪下的內容。也可開視窗檢視過去 yank 內容。
  • tregisters: 可列出 register 還能直接編輯,不過我沒裝成功。

2016-08-06: 新版的 Vim 已支援 TabClosed 事件,更正確/簡單的實作見 Vim 7.4.2077 新增 TabClosed 事件

網路上有看到在 TabLeave 時,記錄最後 tab 編號的方法;缺點是 tab 關閉時,次序就亂了。

可能的困難是沒有 TabClose 事件,既有的 TabLeave 和 TabEnter 要判斷 tab 關閉又有點麻煩。例如開了 4 個 tab,關閉 tab 3 時,會進入 TabLeave,再 TabEnter 到 tab 4,這時 tabpagenr() 還是 4 —— 也就是會先進旁邊的 tab,TabEnter 事件後 tab 總數才會減少,而不是先關閉 tab 再移動。

雖然沒想出不同角度的解法,但以下 function 也算達到目的:

function LastTab(act)

    let lt = g:lasttab
    let tabClosed = tabpagenr('$') < lt.knownLength ? 1 : 0

    if a:act ==# 'TabLeave'
        if ! tabClosed
            let lt.prevLeave = lt.leave
        elseif lt.prevLeave > tabpagenr()
            let lt.prevLeave -= 1
        endif
        let lt.leave = tabpagenr()
    elseif a:act ==# 'switch'
        if tabClosed
            let lastTab = lt.prevLeave > tabpagenr() ? lt.prevLeave -1  : lt.prevLeave
        else
            let lastTab = lt.leave
        endif
        if lastTab == tabpagenr()
            echo 'Already on last tab.'
        else
            exe "tabn " . lastTab
        endif
    endif

    let lt.knownLength = tabpagenr('$')

endfunction

if ! exists('g:lasttab')
    let g:lasttab = {'leave':1, 'prevLeave':1, 'knownLength':1}
endif
au TabLeave * :call LastTab('TabLeave')
au TabEnter * :call LastTab('TabEnter')
nnoremap <LocalLeader>t :call LastTab('switch')<CR>
inoremap <LocalLeader>t <C-\><C-N>:call LastTab('switch')<CR>

這段放到 .vimrc,之後按 <LocalLeader>t 就能切換至最後選取的分頁了。
不過剛進入 Vim 時,第一次切換會跳到第一個 tab。

我認為眼鏡行只要別破壞市容就謝天謝地了,所以傾向支持外觀比較乾淨的某連鎖店。
可惜專業程度的落差,堅持之餘還是失守了……生活品質啊,一團瘴氣。

  大眼鏡 創眼鏡
服務態度 不錯,隨店員而異 禮尚往來,細心解說
驗光 每次驗都不一樣,且度數都超過實際值(要配新的囉) 一次就發現配太重
重新驗光 有點不耐 沒驗幾次,也不需要
我懷疑多焦點鏡片有角度差異 不會呀,沒有這種問題 有問題(拿出鏡片講解),且不是人人能適應
舊的鏡片 沒問我怎麼處理。
後來要找,從垃圾桶翻出來……
包裝好歸還
新鏡片度數不適應 請我跑診所檢查,事後在店裡發現是鏡架調整問題 商量之下,讓我換了一副,不過有貼錢
價格 沒感覺 似乎有點貴,不出名的品牌
爛藝人海報、看板 這間還算窗明几淨 大量噁心海報。
經老闆說明現狀、成因,雙方溝通後,已成功徹下指標性的嫌惡廣告

週末讀到 par 這個小巧指令,用於文字段落(paragraph)的格式化,例如在 Vim 裡面用 :'<,'>!par 把選取區內特定格式的段落重排。

看看 Descripton 裡的範例 Par 1.52 - paragraph reformatter 便能大概瞭解其用途。
有趣的是說明文件 par.doc,以我水平實在難懂,好像不屬這世界的寫法;如做智力測驗題目的兒時記憶,讓我半夜對著螢幕靜靜傻笑。

以上樂趣分享,以下簡易筆記(私用性質,請小心誤導


par 轉換大略過程 (how it works)

  1. Text

  2. 根據換行字元拆成 lines

                    line
    Text     =>     line
                    line
                      ⋮
  3. 將 lines 分為幾組 segment

    會找出 protected line(開頭字元符合 protected 字,具體哪些字可另外設定)或 blank line(僅含空白、Tab 等字的行)——這兩種行將直接輸出而不會轉換;而剩下來一段段的 lines 就是 segment。
    line (protected)
    line                        segment 1
    line                        segment 1
    line                 =>     segment 1 (3 lines)
    line (blank)
    line                        segment 2
    line                        segment 2 (n lines)
      ⋮                               ⋮
  4. 將 segment 內容(lines)分為幾組 block

    會找出 bodiless line(內容不是真的本文,例如 /*** ... ****/)和 superfluous line(冗行:整行空白、或既 bodiless 且中間重複字元全為空白)。
    bodiless line - 根據選項(repeat)調整輸出方式,
    superfluous line - 根據選項(expel)可能不輸出。
    segment line                         (只有一行應該不算 block?但仍會在後續步驟處理)
    segment line (bodiless)               
    segment line                 =>       block 1
    segment line                          block 1
    segment line (superfluous)
    segment line                          block 2
          ⋮
  5. 將 block 分為幾組 IP (Input paragraph)

    若選項(div)沒開,一個 block 就是一個 IP
  6. 對每個 IP 中的 lines,拿掉 <prefix>, <suffix>,剩下的字重排

    /* PREFIX WORD, WORD SUFFIX */
    /* PREFIX WORD SUFFIX */
    /* PREFIX WORD. WORD WORD SUFFIX */
                  ↓
    WORD, WORD
    WORD
    WORD. WORD WORD
                  ↓
    WORD, WORD WORD 
    WORD. WORD WORD
  7. 把 <prefix>, <suffix> 插回去

實際上根據選項,過程中各種細節都會影響輸出,不過這邊就沒研究了。


par 使用小抄 (how-to-use-it)

此為上課順手記,不是實戰用的。

par 59f         f (fit) 盡可能保持原寬度(59 字)
par 59l         l (last) 盡可能讓最後一行與其它行同寬
par 59t0        t0 (touch 0) 輸出寬固定為 59
par 59j         j (just) text justified
par 59h3        h (hang) 分開前 3 行與之後行的 prefix, suffix 計算
par 59p         p (prefix) 保證每行的前 59 字和 Input 相同
par 59d         d (div) 每行依照 comprelen+1 個字是否為空格,切割為不同 IP
par 59r         r (repeat, r 不給參數則為 3) 變更 bodiless line 定義,repeat 字元 3 次以上也算為 bodiless(原本只有空白才算)
                r 不為 0 時,會使 repeat 字元最多調整到 59
par 59e         e (expel) 不輸出 superfluous line
par 59q         q (quote) 於不同級的引用插入 vacant line
par 59i         i (invis) 不輸出 quote 插入的 vacant line
par 59g         g (guess) 判斷相連的 curious word, capitalized word 如何連接(例:Dr. Jon 分行時被當作一個字)
par 59gc        c (cap) 所有的詞皆視為 capitalized word
par Q+:+ q      將 "+ " 也視為引用字元(加入 PARQUOTE)再執行 q

MooTools 備有不少 plugin 在所謂 MooTools More,官網亦提供 More Builder 方便自訂下載。

不過若要下載 1)整套 More 所有元件,或 2)最新開發版本 有沒有簡單辦法呢?

寫這篇時才發現,原來 download 頁就有「Edge Build」可以抓最新 core build ,把網址 core 換成 more 應該就是最新的 More 了(其實也不確定,沒看到說明)

似乎已解決,只好把本文放分隔線後面……


另一個作法是到 More 的 GitHub Repository 把檔案複製下來,裡面就有 build.rb,會把各元件的原始碼合併起成一份 .js(預設包含 Core + More),附帶好處是要包哪些東西可以寫設定檔存起來。

記具體過程:

  1. cd some_directory
  2. git clone git://github.com/mootools/mootools-more.git
    cd mootools-more
  3. 這裡 Core 是以 submodule 方式管理,也要更新一下:
    git submodule init
    git submodule update
  4. 有興趣的話,看看 build.yml 的設定:
    • dependency_paths - 用來尋找相依檔案的路徑,預設為 Core 和 More 的 Source 目錄
    • build_path - 最後輸出的檔名
    • dependency_file -(原檔案中沒有這個項目)預設是 scripts.json,即每個 dependency_paths 下的 scripts.json 檔;內容描述具體會用到的檔案,以及各檔案的相依性
  5. 若想定義要排除哪些檔案,可以把每個 scripts.json 複製一份,改成自己的設定。
    當然 build.yml 裡的 dependency_file 也要跟著改名。
  6. 不確定是我的問題還是怎樣,開動前要修改一下 build.rb:
    1. def build
        @string ||= full_build
        @string.sub!('%build%', build_number)
        @string  # 加入這行才會傳回 @string,否則會輸出空檔案
      end
    2. builder = MooTools::Build.new({
        :dependency_paths => conf[:dependency_paths],
        :build_path => conf[:build_path],
        :dependency_file => conf[:dependency_file]   # 原本沒吃到這個設定
      })
  7. ruby build.rb

註:寫這文章的時候 Source/Native/URI.js 檔案編碼有問題,若輸出檔案不是 utf-8,可以檢查這個檔案。