Vim error highlight
2013/08/12 更新[[[ 自從 YouCompleteMe 這個 plugin 出來之後,一堆 plugin 都變成垃圾了,包含這篇文章的所有內容,也變成垃圾了,請直接參照 https://github.com/Valloric/YouCompleteMe 然後不用再往下讀了。 ]]]一直想要一種功能,就是 vim 可以自動顯示錯誤的程式碼,像是 visual studio 或是 eclipse 那樣。最近摸索了一下終於讓我實現了這個功能 :P
事先準備
-
可以在背景執行動作,不會妨礙現在進行的編輯作業。
-
可以把 quick fix list 裡面的錯誤,轉而使用 syntex highlight 直接在編輯視窗內標示出來,看一下官方網站的 screensho:
進行修改
理論上這兩個東西都有安裝的話,好像就成功了,不但可以在背景編譯,還可以在編輯畫面表示錯誤。當然,實際上這兩個都安裝了以後,還是沒辦法直接達成目的的,不然我也不用寫這篇文了 XD
經過 trace 兩者的 script 以後,發現原因在於 errormarker 是透過 hook QuickFixCmdPost event 來呼叫 SetErrorMarkers() 函數來把 quick fix list 裡面的資料轉標到編輯畫面。當平常使用 :make 的時候,QuickFixCmdPost 會被觸發,但是當透過 AsyncCommand 進行 make 的時候,似乎不會觸發 QuickFixCmdPost 事件,於是 SetErrorMarkers() 也不會被呼叫,然後一切都完啦。所以我們的目標就是讓 AsyncCommand 「正確」的呼叫 SetErrorMarker() 就好。
先講一下 AsyncCommand 的機制,AsyncMake 其實是一個 wrapper,包住了 asynccommand#run(make, asynchandler#qf) 這個動作,後面那個 asynchandler#qf 是等到 make 結束以後,用來處理結果的 handler,而這個 handler 會在 asynccommand#done() 的時候被呼叫,所以我們只要在 asynccommand#done() 裡面呼叫 SetErrorMarkers() 就可以了。
asynccommand#done() 在 .vim/bundle/asynccommand/autoload/asynccommand.vim 這個檔案裡面,找到他,
2 " Called on completion of the task
3 let r = s:receivers[a:temp_file_name] "handlers are registered in s:receivers
4 if type(r.dict) == type({})
5 call call(r.func, [a:temp_file_name], r.dict)
6 else
7 call call(r.func, [a:temp_file_name])
8 endif
9 unlet s:receivers[a:temp_file_name]
10 delete a:temp_file_name
16 endfunction
在函數結束之前加上呼叫 SetErrorMarkers() 的程式碼,變成這樣:
2 " Called on completion of the task
3 let r = s:receivers[a:temp_file_name] "handlers are registered in s:receivers
4 if type(r.dict) == type({})
5 call call(r.func, [a:temp_file_name], r.dict)
6 else
7 call call(r.func, [a:temp_file_name])
8 endif
9 unlet s:receivers[a:temp_file_name]
10 delete a:temp_file_name
11
12 if exists("*ExposeSetErrorMarkers") " Check and
13 call ExposeSetErrorMarkers() " Call ExposeSetErrorMarkers()
14 endif " here
15
16 endfunction
注意到我們呼叫的不是 SetErrorMarkers() 而是 ExposeSetErrorMarkers(),這是因為 SetErrorMarkers() 是一個 script local 函數,只能在 script 內部被呼叫,所以我在 errormarker.vim 裡面加了一個函數來曝光 SetErrorMarkers():
2 call s:SetErrorMarkers()
3 endfunction
這樣以後只要輸入 AsyncMake 就可以在背景編譯,編譯完成以後自動 highlight 錯誤了。為了方便使用,我還加上了幾個 autocommand:
2 au BufWritePost *.c AsyncMake
3 au BufWritePost *.cc AsyncMake
4 au BufWritePost *.cpp AsyncMake
這樣編輯 c/c++ 的時候每次存檔都會自動編譯然後 highlight 錯誤。
其實我知道我的改法很醜啦,但是他 work XD 如果有人知道比較優雅的改法請跟我分享阿~
小記:我發現我 ubuntu 安裝 vim 的話,是沒辦法作這件事的,執行 script 的時候會發生錯誤,但是安裝 vim-gtk 的話就可以了,不知道是因為設定的時候哪個選項沒開,但是總之如果有人遇到一樣的問題的話請試試看安裝 vim-gtk。