沒穿方服

索引

顯示╱隱藏內文

Gblame 運用

Gblame 是什麼

tpope/vim-fugitive 提供的指令,把 git blame 結果顯示在分割視窗。
裡面按 -~P 可以對其它版本再 blame(詳見 doc),比直接在 command line 操作方便多了。

Gblame 畫面


開啟後自動 resize 到作者名

:Gblame 打開後,預設會顯示到 Date,很佔畫面,按 A 可以縮到 Author。

煩的是沒有選項能自動這件事,只好另設一個 command,在 :Gblame 後多送一個 A

command! -nargs=0 GBlameA Gblame | call <SID>fugitiveblame_after()
function! s:fugitiveblame_after() "{{{
  call feedkeys('A')
endfunction "}}}

然後用 tyru/vim-altercmd 方便輸入……

AlterCommand gb[lame] GBlameA

bling/vim-airline 狀態列

很微小的修改,原本狀態列會顯示 fugitive tmp file 的名稱,我把它改成 "Gblame"。

function! AirlineGblame(...)
  if &filetype == 'fugitiveblame'
    let w:airline_section_a = ''
    let w:airline_section_b = ''
    let w:airline_section_c = 'Gblame'
    let w:airline_section_y = ''
  endif
endfunction
let g:airline_statusline_funcrefs = []
call airline#add_statusline_func('AirlineGblame')

Git compare

配合 bootleq/vim-gitdiffall 使用, 按 gf 將檔案變更以 vim diff 開在新 tab,看那個 revision 改了什麼:

gitdiffall 開在新 tab

<LocalLeader>gf 則是開啟新的 tmux window,把該版本變更的檔案全部打開:

gitdiffall 開在新的 tmux window

function! s:fugitiveblame_gitdiffall(all) "{{{
  let rev = matchstr(getline('.'), '\v^\w{7}')
  let buffer = fugitive#buffer(bufname(b:fugitive_blamed_bufnr))
  if a:all
    call TmuxNewWindow({
          \   "text": "gitdiffall @" . rev,
          \   "title": '⎇',
          \   "directory": buffer.repo().tree()
          \ })
  else
    execute printf("tabnew %s | silent GitDiff @%s", buffer.path(), rev)
  endif
endfunction "}}}

autocmd! my_vimrc FileType fugitiveblame
      \ nnoremap <buffer> gf :call <SID>fugitiveblame_gitdiffall(0)<CR> |
      \ nnoremap <buffer> <LocalLeader>gf :call <SID>fugitiveblame_gitdiffall(1)<CR>
function! TmuxNewWindow(...) "{{{
  let options = a:0 ? a:1 : {}
  let text = get(options, 'text', '')
  let title = get(options, 'title', '')
  let directory = get(options, 'directory', getcwd())
  let cmd = 'tmux new-window -a '
        \ . (empty(title) ? '' : printf('-n %s', shellescape(title)))
        \ . printf(' -c %s', shellescape(directory))
  call system(cmd)
  if !empty(text)
    let cmd = printf('tmux set-buffer %s \; paste-buffer -d \; send-keys Enter', shellescape(text))
    call system(cmd)
  endif
endfunction "}}}

這裡比較悶的是 Gblame 的 mapping 都寫死死,最後只好選 gf 來用。
看 issue 感覺是 tpope 不大想理 mapping 的彈性。

一、tpope/vim-rails

我最常用的是 :RT:Tmodel foo:A


二、basyura/unite-rails

可設定按下 ,fR 就打開 Unite 介面,選擇 Rails 相關的 source。

nnoremap [unite] <Nop>
nmap <LocalLeader>f [unite]
nnoremap <silent> [unite]R :<C-U>Unite -start-insert -input=rails/ source<CR>

於是按下 ,fR 就顯示
Unite rails/xxx

接著選擇 initializer,就能進入 rails/initializer 這個 source 的畫面了。
※下圖我按了 ke 示範一下 filter 功能
搜尋 initializers


這兩個 plugin 的功能都不限於「尋找檔案」,這篇就不多說了。

沒有 LiveReload 也過得很好

「雙螢幕 + FireGestures 搖擺手勢(按住滑鼠右鍵再按左鍵)重新載入網頁」 —— 至少比按 F5 快多了。

目前環境

Windows 7 的 Firefox 21 beta + LiveReload Firefox extension,
Ubuntu VM 的 Rails + guard-livereload

我的 LiveReload 大概已經一兩年沒辦法用了,I Don't Know Why,最近被問到才重啟調查,發現在瀏覽器直接測試 WebSocket(可以用 WebSocket.org | Echo Test 測)是可以和 VM 裡的 LiveReload server 來往的,那八成是瀏覽器的擴充套件出狀況。

惱人的 LiveReload Firefox 擴充套件

LiveReload 官網提供的 2.0.8 版 xpi 因為還沒包含 Pull Request #8,所以應該是不堪用的,此外還有按鈕無法移除、連線錯誤沒有警告(有時甚至毫無反應)等煩人問題。

另一關鍵是沒有選項可以調整連線的 hostport,預設是 127.0.0.1 和 35729,我 VM 裡的 server 就不是用這兩個,當然連不上。

註:LiveReload 現在的設計是 server 在頁面插入 livereload.js,讓 client 從中取得 host 和 port。 所以也許是我的 guard-livereload 版本太舊(0.3.1),或者頁面需要另做修改。

最後對策是改 xpi 內容,讓它讀取 Firefox 自訂選項 extensions.livereload.fc.hostportfc 只是隨便定的名字),具體是在 global.js 的 beforeEnablingFirst 加入:

// Use custom preferences
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
  .getService(Components.interfaces.nsIPrefService);
prefs = prefs.getBranch("extensions.livereload.fc.");
if (prefs.getCharPref('host')) {
  this.host = prefs.getCharPref('host');
}
if (prefs.getCharPref('port')) {
  this.port = prefs.getCharPref('port');
}

第一次嘗試 Responsive Design 和 Grid System

公司的活動頁,明明趕得要死,我還是忍不住把 Susy 用上了,為什麼?

因為前陣子在規劃新版面,我認為像樣的 grid system 能幫助我們,減少設計師、上稿人員和我,煩惱或溝通一些不必要的問題,例如某元素 margin 多少、某版位我做了多寬、……,不如先定個標準出來,要改再說,不改就我處理。

RWD 也是早就想做的事情,目前網站拆為桌面版和手機版(m.網址),結果因為 m 的功能不完整,造成 tablet 使用者處在不上不下的局面,想想 RWD 還是最輕鬆的解法。

找了不少解決方案,感覺都很難搞,最後只有 Susy 給我嘗試的勇氣。

Susy 可愛之處

  • 不用自己算寬度。 比如 960px 分成 16 欄,每欄是多寬? 我不可能算對啊!
  • 不用在 HTML 加入像 .row4 .span3 的噁心 class,那根本無法維護嘛。

Susy 實際做的事情

剛開始什麼 gutter、columns 的名詞都看不懂,但實際做一次,認識一下產生的 CSS,會發現 Susy 做的事其實不多。

※目前版本是 1.0.8,但以下會混用 2.0 alpha 未定的介面說明,我覺得更清楚。

  1. 建立 grid(set-grid(960px 12 1/4)

    主要是維護 grid 設定的工作,也就是定義「容器寬、欄數、欄間隔、留邊」等等變數; 一般會寫下幾組設定,用在不同的 media query 條件下。

    具體例子,v1.0.8 的語法

    $container-width: 970px;
    @include container;

    產生的 CSS 會包含 max-width: 970px; 以及 :after :before 的 clearfix。

  2. 安排元素的佔有欄數與定位(span(last 3 of 5 isolate)

    Susy 做掉兩個麻煩的算數問題,第一是元素本身的 width; 只要指定橫跨(span)寬度是 N 欄,Susy 就會根據當時的 grid 設定,把元素 width 設為應有的百分比

    第二是為了將元素放在正確的水平位置上,所需要設定的左右 margin (理由:Susy 採用 float 加上負 margin 實作 grid,避免 % 除不盡時,瀏覽器處理最後一個 pixel 的問題,參考 Responsive Design’s Dirty Little Secret)。

    v1.0.8 使用 @include span-columns(3 omega, 5); 指定元素佔 5 欄中的 3 欄; omega 表示是該列的最後一個元素,預設會產生反方向的 float,倒過來算,否則那幾 % 有時還是會被擠到第二列。

    另有 isolate(3) 用來將元素定位在第 3 欄; 針對列表也能以 isolate-grid(4) 讓各 item 寬 4 欄、自動對齊,且 nth-child(4n+1) 會自動 clear,產生 CSS 如:

    #AllWorks ul.gallery li {
      width: 23.07692%;
      float: left;
      margin-right: 2.5641%;
      margin-right: -100%;
    }
    #AllWorks ul.gallery li:nth-child(4n + 1) {
      margin-left: 0%;
      clear: left;
    }
    #AllWorks ul.gallery li:nth-child(4n + 2) {
      margin-left: 25.64103%;
    } ... (後略)

  3. Media query

    提供方便設定 media query 的 Mixin。 v1.0.8 用法

    $media-tablet:  640px  8 969px;
    @include at-breakpoint($media-tablet) {  // ... (block 後略)

    會產生 @media (min-width: 640px) and (max-width: 969px) { ... 的 CSS,且其中寬度會以 8 欄為標準計算。


遇到的問題

  1. 容器已經有 max-width,但某個 header 想要背景佔滿畫面寬

    要讓 content 置中,header、footer 又要橫向佔滿整個畫面,自然的作法就是將 header、content、footer 放在同一層,各自設定寬度(分別為 100%、container width、100%)。

    如果想讓 header 的「內容」也遵守 grid 設定,讓它跟 content 對齊,就要在寬 100% 的 header 下,再加一層 wrapper 且設為 grid container 才行。

    若 container 寬未佔滿全畫面,如何實現全畫面寬的 header?

    結論:這個問題跟 Susy 沒有關係。終究無法只用一個 container 就達成需求。

    註:有個用 pseudo element 的做法,參考 css - How to handle full-width element outside of a 960 compass susy grid? - Stack Overflow

  2. 預設的垂直對齊,只能靠頂(top)對齊

    因為 CSS vertical-align 只對 layout 為 inline 或 table-cell 的元素有用,而 float 會把 layout 變成 block,正好 Susy 是使用 float 排版,結果就是「垂直對齊」變得不易做到。

    預設的 grid 定位,總是垂直置頂對齊。

    可能的解法是改用 display: inline-block;,就能使用 vertical-align 了,也是我們平常實作 gallery 的方式。 (Susy 有一個相關 feature request inline-block version · Issue #69,不過被拒絕掉了)

    這次我也沒用 vertical-align,設計師可能也比較中意 vertical rhythm…… 但總之想換 inline-block 的話,要注意的應該只有:

    1. Haml tag 要用 > 和 < 把多餘的空白去掉,因為空白會佔寬度:
      %li><
        %div.image
    2. 不使用 Susy isolate,改為自己指定 width 和 margin:
      li {
        width: columns(4, 16);
        margin-right: gutter(16);
        &:nth-child(4n) {
          margin-right: 0;
        }
      }

    項目改用 inline-block 就能 vertical-align 了。

我的 FuzzyFinder 壞掉了

忘了何時開始,本機 FuzzyFinder 初次打開時,如果前幾個字輸入太快,就會「過早自動完成」補成一條無效候選。

例如 FufFile 的 mapping 是 ,f 的話,輸入 ,ff 就會出現下圖狀況,非常惱人。

過早自動完成是萬惡的根源

調查不出原因,只好放棄,反正沒人維護了,就跟 FuzzyFinder 說拜拜吧。

最後一張照片,親愛的 fuf

用了三年的 FuzzyFinder 介面

替代品當然是 unite.vim

之前寫過一篇簡介「試試新的萬能尋找器 unite.vim」,那時就它用來輔助 FuzzyFinder 了,選 action 的介面有時候真的比較方便,現成的實用 source 又多。

而且 2011 至今,幾乎每次跑 BundleInstall 都看到它在更新,相當驚人。 回報 bug 很快就會被處理,這點相當酷。

豪放的大視窗

Unite buffer 開出來整個外觀如下圖,分割位置、寬或高、buffer 名稱、訊息(兩行 [file_rec] 的部分)都可以調整。

Unite file_rec 整體外觀

輸入方式(narrowing 語法)的變更

需要重新習慣的不多:

  1. FuzzyFinder 以 ; 分隔多個關鍵字,在 unite.vim 改用空白。
  2. 離開 Insert Mode 不會直接退出 unite buffer,要用另外的 mapping 才行。

而關鍵字與候選範圍的比對關係,在 unite.vim 被抽出為 matcher,可以用 unite#filters#matcher_default#use()unite#custom_source()unite#set_substitute_pattern() 等方式作細部調整。

Matcher 可以換成 matcher_regexp、matcher_fuzzy、……,可惜文件不清,我只能邊改邊試。

set_substitute_pattern 可以做到輸入 $HOME 展開 $HOME,輸入 gem/ 展開 ruby gem path,理論上應該能自由調整 narrowing 用語。 ※我是參考這篇 unite.vim の設定を一部晒してみる - 永遠に未完成調的。

我的 mappings

註:我認為分享 key mapping 無聊透頂,尤其選的按鍵只是習慣、沒有語義,那你改個按鍵也要分享一篇嗎?

這裡只是形容一下操作的感覺,有些按鍵是預設的、沒改。

  • ,ff - 開啟 Unite file 介面,由目前 buffer 所在目錄開始搜尋。
  • ; - 在 unite buffer Insert Mode 向前刪除一個 word,反悔或打錯字時用。
  • <Ctrl-J><Ctrl-K> - 移動到下一個/上一個候選。如果在 Insert Mode,會自動進入 Normal Mode,然後只要按 j k 就能上下移動。
  • <Tab> - 對選中的項目,開始選擇 action。
  • <CR> - 對選中的項目執行 default action,例如檔案的預設 action 我調成在新分頁開啟。
  • ,<CR> - 對選中的項目執目第二 default action(目前 unite.vim 沒有所謂第二 action,要自己設 mapping),我設定成在目前分頁開啟檔案。
  • <F5> - 在 unite buffer 中,重新整理,清除候選快取。
  • ,q - 退出一層 unite buffer。
  • ,Q - 退出全部 unite buffer。

API 與設定感覺

其實設計蠻漂亮的…… sourcekind 是要處理的情境和候選物件類型, action 是要執行的動作, filter 包含 matcher、sorter、converter 的微調。 以上定義清楚,設定時的指令自然會乾淨,這部分做得不錯,比 FuzzyFinder 進步多了。

比較惱人的是文件趕不上開發速度,英文不太講究、repo 裡的 commit message 也敘述不清。 有點難搞。

目前常用的 source

※更多 source,參考 unite plugins · Shougo/unite.vim Wiki

  • file - 最常用的就是這個。
  • file_mru (most recent used files)
    Unite file_mru
  • register - 多行內容也能顯示喔。
  • bookmark - 目錄、檔案、位置都通,比 fuf 方便。
  • help - 但要用 tags-caching 這個 branch 才有快取,否則實在太慢。
    Unite help
  • session
  • tab
  • outline - 有了這個,幾乎就沒在開 Tagbar 了(雖然我本來就少用 tag)。

還不大會用的其他功能

  1. file_rec source

    Fuf 要找深層目錄下的檔案,會使用 **/ 查詢。 Unite.vim 也能使用 **/ 寫法,我一般用這個就 OK 了。

    而 unite.vim 還有專門做深層搜尋的 file_rec source,作者也經常磨亮這個功能。 不過目錄樹很大時,總是會跑到 too many candiates(預設 1000 項),加大快取數又要跑很久。 解法應該是用另外的 file_rec/async source,可以非同步發動更快的搜尋,但需要裝別的東西,我就沒試了。

  2. Quick Match

    類似 EasyMotion 的選擇法,打一個鍵就能對某個項目動作。 還沒想到要用在哪,也許適合用在 bookmark 吧。

    :Unite -quick-match alignta

總之一下子就能習慣,早就可以換了

我目前的設定放在 bootleq/vimrc_human/.vimrc#L808-923 at 500f59,沒什麼特別的,doc 裡也有範例,純擺設。