最近對sublime的親密度不斷增長,之前只是用它來寫WEB,如今發現用它來寫C++也很方便,特別是它的免打擾模式太好用了,因而我乾脆弄了一個C++的sublime build system,這樣在sublime中也能編譯C++了,除了不能調試,已經算是一個開發環境。html
可是,昨天發現,我習慣用的Visual Assist在sublime中找不到類似的插件。尤爲是 alt + up, alt+ down 快捷鍵,在VC中是跳轉到類或函數定義,是我最經常使用的快捷鍵之一。好吧,既然找不到,咱就本身作一個。python
其實爲sublime作插件並不難,sublime是用python作爲腳本驅動的,插件一般也是用python。在網上看了點資料,再看看sublime3\package\default目錄裏面幾個py,就大概明白插件的思路了。正則表達式
首先,在 sublime3安裝目錄 \Data\Packages目錄下新建一個目錄,名字能夠隨意,我取名爲JumpTag。json
而後,由於我要捕捉快捷鍵,就要有.sublime-keymap文件;要用python,必須有.py文件。因此在這個JumpTag目錄下新建幾個文件:Default (Linux).sublime-keymap、Default (OSX).sublime-keymap、Default (Windows).sublime-keymap 和 JumpTag.pyapi
其中,三個.sublime-keymap文件都是同樣:async
[ { "keys": ["alt+up"], "command": "jump_tag_prev"}, { "keys": ["alt+down"], "command": "jump_tag_next"} ]
實際上是json格式,意思是說按下 alt+up 就調用命令 jump_tag_prev,alt+down 調用 jump_tag_next函數
那麼,在哪裏實現jump_tag_prev、jump_tag_next 命令呢?固然是在.py文件裏啦:ui
1 import sublime, sublime_plugin 2 import re 3 4 5 _datas = {"view_id":0,"recorder":None} 6 7 class JumpTagRecorder(sublime_plugin.EventListener): 8 def on_modified_async(self, view): 9 _datas["view_id"] = view.id(); 10 _datas["recorder"]= None; 11 12 def on_activated_async(self, view): 13 _datas["view_id"] = 0; 14 _datas["recorder"]= None; 15 16 17 def gen_recorder(view): 18 if (_datas["recorder"] is None or _datas["view_id"] != view.id()): 19 _datas["view_id"] = view.id() 20 _datas["recorder"]= view.find_all(r"(^(?![ \t]*?\b(if|while|for|try)\b)[\w \t*&]*?\([^()]*?\)(?=[\w\s]*?\{))|(\bclass\b|\bstruct\b)[\w \t:,<>]+?(?=[\s]*?\{)") 21 22 return _datas["recorder"] 23 24 25 class JumpTagPrevCommand(sublime_plugin.TextCommand): #注意看類的名字,對應了 jump_tag_prev_command 命令 26 def run(self, edit): 27 reg_list = gen_recorder(self.view) 28 29 if reg_list is None: 30 return 31 32 for region in self.view.sel(): 33 pt = region 34 break 35 36 closest_num = 999999999 37 closest_region = sublime.Region(0,0) 38 39 for region in reg_list: 40 if region.end() < pt.begin(): 41 n = pt.begin() - region.end() 42 if n < closest_num: 43 closest_num = n 44 closest_region = region 45 46 if closest_region.empty(): 47 return 48 49 self.view.sel().clear() 50 self.view.sel().add(closest_region) 51 52 self.view.show(closest_region.begin()) 53 54 class JumpTagNextCommand(sublime_plugin.TextCommand): #這個類的名字對應了 jump_tag_next_command 命令 55 def run(self, edit): 56 57 reg_list = gen_recorder(self.view) 58 59 if reg_list is None: 60 return 61 62 for region in self.view.sel(): 63 pt = region 64 break 65 66 closest_num = 999999999 67 closest_region = sublime.Region(0,0) 68 69 for region in reg_list: 70 if region.begin() > pt.end(): 71 n = region.begin() - pt.end() 72 if n < closest_num: 73 closest_num = n 74 closest_region = region 75 76 if closest_region.empty(): 77 return 78 79 self.view.sel().clear() 80 self.view.sel().add(closest_region) 81 82 self.view.show(closest_region.begin())
稍微講一下這代碼:由於咱們要在函數定義、類定義之間跳轉,因此就得先把這些定義的位置找出來,函數gen_recorder就是幹這工做的,我認可函數名起的不咋滴。它調用了sublime的一個API:find_all,按正則表達式找出全部函數定義和類定義的位置,我這正則表達式也不知道是否是最優,總之功能是實現了。spa
如今有了找出定義位置的函數了,那麼,咱們何時才須要去找這些定義的位置呢?應該知道全文查找是個相對耗時的操做,不該該在每次按快捷鍵時調用。咱們能夠把定義的位置保存起來,在文檔發生改變時再從新查找。所以,就須要監聽文檔的修改事件了。定義一個監聽器也是很簡單的:插件
class JumpTagRecorder(sublime_plugin.EventListener): #注意括號裏必須是sublime_plugin.EventListener def on_modified_async(self, view): #注意函數的名字 _datas["view_id"] = view.id(); _datas["recorder"]= None; def on_activated_async(self, view): _datas["view_id"] = 0; _datas["recorder"]= None;
上面這個類的名字是隨意的,可是要注意括號裏面,必須是 sublime_plugin.EventListener,類的方法必須按照api文檔http://www.sublimetext.com/docs/3/api_reference.html
最後,就是實現 jump_tag_prev、jump_tag_next命令了,代碼十分的簡單,相信沒有人看不懂的。要注意的是命令對應的類,不能隨意取名,必定要把命令名改爲「駝峯式」,還得加上Command,並且必需要有run方法。
簡簡單單,就實現一個小插件啦!對了,讓我影響比較深的是sublime是熱加載插件代碼的,也就是說插件代碼一旦被修改,sublime中當即從新加載,這真的很方便。