Motivation
自從習慣在 Vim 裡面各種 Fuzzy Search 之後,發現記憶力越來越差 QQ:開檔案、找 Function Tag、查某一行 Code、乃至於找目前檔案裡的某個關鍵字,通通只需要一點點零散的關鍵字就能夠快速找到並前往(個人推薦 LeaderF 及 fzf.vim)。回到 Sublime Text,找檔名、Tag 還算方便,但唯獨缺少一個「模糊」搜尋 Code 本身的功能,搜尋功能是有,但是需要輸入正確的關鍵字或者用正規表達式來模擬模糊搜尋。
於是乎,只好自己開發一個簡單的 Sublime Text 外掛了。
偷偷抱怨一下 VS Code 的 C/C++ Extension by Microsoft,查找 Tag 的功能竟然沒有實作模糊匹配。有人在 VS Code 的 Github 上發 Issue,結果開發團隊表示這是 Extension 的問題跟他們無關⋯⋯
Make Our Own Plugin
查了一下 API 發現,Selection Prompt 其實就有內建的 Fuzzy Search 演算法,也就是說只要餵給 API 資料自動就能幫我們做到簡易的 Fuzzy Search 功能。這樣問題就簡化了不少,只要把需要的行全部餵給 Selection Prompt 做為 Input 就搞定了。
Start a Blank Plugin
在開始之前需要新增一個空白 Plugin 在自己的電腦,在選單依序按:Tool、Developer、 New Plugin…,就會跑出一個 Template。
import sublime
import sublime_plugin
class ExampleCommand(sublime_plugin.TextCommand):
def run(self, edit):
self.view.insert(edit, 0, "Hello, World!")
將此 Sample 儲存至預設的目錄即可開始開發自製的 Plugin,檔名可以隨意設定例如 Sample.py
,Sublime Text 會自動去搜尋目錄底下的所有 Python 檔案去執行。
Test the Example Command
現在你可以在 Console Ctrl-`
裡面輸入以下指令測試一下範例 Command。
view.run_command('example')
你會發現目前文件的游標所在行,最前面插入了一個 Hello, World!
字串,就代表外掛載入成功。Command 名稱的轉換方式就是去掉尾巴的 Command
之後將 CamelCase 轉成 snake_case 即是。
Make It an Independent Plugin
User 資料夾裡面的 Python 檔案雖然會被 Sublime Text 載入使用,但卻不會被視為一個單獨的 Plugin。為了方便管理並且讓 Package Control 抓到我們自製的 Plugin,需要往上一層並新增一個 Plugin 資料夾,然後把檔案移到這邊。
例如我想要製作一個名為 Simple Fuzzy 的外掛,於是新增了 /Users/<USERNAME>/Library/Application Support/Sublime Text 3/Packages/SimpleFuzzy/
目錄並且在裡面加入了剛剛的 Sample 檔案,然後重新命名為 SimpleFuzzy.py
。
現在在 Command Palette 中找到 Package Control: List Packages
並輸入 SimpleFuzzy
,就可以找到我們剛剛新建的 Plugin。
Add a Sublime Text Command
為了呼叫功能需要定義一個自製的 Command。
Sublime Text API 根據不同的使用時機提供了 3 種 Command 介面,三者的主要差異是可以在 self
拿到的 Attribute 不同:
- 跟目前文件互動的 Text Command
- 跟目前視窗互動的 Window Command
- 跟應用程式互動的 Application Command
以我的需求選擇使用了 Text Command(用於搜尋目前文件)及 Window Command (用於搜尋 Project 資料夾)。
加入第一個簡單的 fuzzy_current_file
Command:
class FuzzyCurrentFileCommand(sublime_plugin.TextCommand):
def run(self, edit, pos):
self.view.sel().clear()
self.view.sel().add(sublime.Region(pos))
self.view.show_at_center(sublime.Region(pos))
def input(self, args):
if "pos" not in args:
return EditorLineInputHandler(self.view)
else:
return None
做的事情很簡單,就是讓使用者輸入他想找的行然後跳過去。這裡只有跳過去的程式碼,彈出的搜尋框是用 InputHandler
實作。
執行的順序是 input
(如果有的話)然後 run
,run
方法可以取得 InputHandler
回傳的資料(此例中的 pos
),而 edit
參數是Text Command 內建,若有修改文件內容的需要會用到此參數。
Adapt List-Item Selection Prompt
由於彈出的輸入框已經內建 Fuzzy Search 功能,因此只要將文件內容逐行全部餵給 InputHandler
即可。
class EditorLineInputHandler(sublime_plugin.ListInputHandler):
def __init__(self, view):
self.view = view
def name(self):
return "pos"
def placeholder(self):
return "Search content line..."
def list_items(self):
regions = self.view.find_all('.+\n')
lines = [self.view.substr(region).strip().replace('\t', '') for region in regions]
positions = [r.begin() for r in regions]
return [
sublime.ListInputItem(
text=line_str,
value=pos,
) for pos, line_str in zip(positions, lines)
if len(line_str)
]
這裡解釋一下幾個重要的 Method:
name()
:返回此InputHandler
處理的參數命名list_itmes()
:返回一個字串 List 或者更客製化的ListInputItem
List
簡單的把空白列移除之後回傳給 InputHandler
即可。使用者找到所需要的行之後按下 Enter 會回傳 pos
給 FuzzyCurrentFileCommand
,然後就可以跳到指定的位置。
咦,無法叫出 Input Prompt?因為欲使用 InputHandler
還必須多做一件事,就是在 .sublime-commands
檔案中為我們的 Command 新增一個 Entry。在同一層資料夾新增一個 Default.sublime-commands
檔案然後輸入:
[
{ "caption": "SimpleFuzzy: Current File…", "command": "fuzzy_current_file" },
]
就可以在 Command Palette 中找到自定義 Command:SimpleFuzzy: Current File…
。按下 Enter 沒意外的話會彈出一個視窗並且可以模糊尋找目前文件的任意行,並且透過再次按下 Enter 跳轉。
更多的使用方法請參考使用手冊 [1],其他使用教學則可以參考 [2]、[3]。
此小工具外掛的開發到此告一個段落之後,就可以準備部署到 Sublime Text 官方 Package Control 的目錄中,如此一來就能讓全世界的使用者下載來使用。
Deploy for Package Control
部署流程大致如下:
- 首先必須開一個自己的雲端空間維護這個 Project,可以使用 Github 或 BitBucket 的 Public Repository
- Fork 官方 Package Control 的 Repository 並且在 Repository 列表中新增一個自己的 Plugin 條目
- 發 Pull Request 請求將自己的 Plugin 加入官方的
master
主 Branch
更詳細的步驟可以參考官方公布的一份準則文件,裡面還額外要求一些每個 Plugin 開發者都必須遵守的規則,例如:
- 必須先到 Package Control 搜尋頁檢視是否已經有類似或者重複的功能
- 挑選一個簡單明瞭、易於辨識的名稱
- 移除所有非必要檔案
- ⋯⋯等
如果是用雲端版本控制服務,在想要發佈新版本的時候需要新增特定格式的 Tag 以標示版號,例如 1.0.0
之類。
最後,經過一番討論及修改之後,就可以等到 Merged 啦!撒花🎉 自製的 SimpleFuzzy 出現在 Package Control 首頁啦~
歡迎大家下載來玩~
Installation
在 Command Palette 中找到 Package Control: Install Package
並輸入 SimpleFuzzy
,就可以找到這個 Plugin 下載來用啦~
Source Code
目前功能實作的方法頗陽春,所以遇到很肥的 Project Folder 的時候會卡一下 XD 之後有空再看要如何改善~
ukyouz/SublimeText-SimpleFuzzy
References
- Documentation API Reference | Sublime Text
- Sublime Text Community Documentation
- How to Create a Sublime Text 2 Plugin | envato-tuts+