沒穿方服

索引

顯示╱隱藏內文

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,可以檢查這個檔案。