自己动手开发sublime插件(1)

最近对sublime的亲密度不断增加,以前只是用它来写WEB,现在发现用它来写C++也很方便,特别是它的免打扰模式太好用了,于是我干脆弄了一个C++的sublime build system,这样在sublime中也能编译C++了,除了不能调试,已经算是一个开发环境。

 

但是,昨天发现,我习惯用的Visual Assist在sublime中找不到相似的插件。尤其是 alt + up, alt+ down 快捷键,在VC中是跳转到类或函数定义,是我最常用的快捷键之一。好吧,既然找不到,咱就自己做一个。

 

其实为sublime做插件并不难,sublime是用python做为脚本驱动的,插件通常也是用python。在网上看了点资料,再看看sublime3\package\default目录里面几个py,就大概明白插件的思路了。

 

首先,在 sublime3安装目录 \Data\Packages目录下新建一个目录,名字可以随意,我取名为JumpTag。

 

然后,因为我要捕捉快捷键,就要有.sublime-keymap文件;要用python,必须有.py文件。所以在这个JumpTag目录下新建几个文件:Default (Linux).sublime-keymap、Default (OSX).sublime-keymap、Default (Windows).sublime-keymap 和 JumpTag.py

 

其中,三个.sublime-keymap文件都是一样:

[
  { "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文件里啦:

 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,按正则表达式找出所有函数定义和类定义的位置,我这正则表达式也不知道是不是最优,总之功能是实现了。

现在有了找出定义位置的函数了,那么,我们什么时候才需要去找这些定义的位置呢?应该知道全文查找是个相对耗时的操作,不应该在每次按快捷键时调用。我们可以把定义的位置保存起来,在文档发生改变时再重新查找。因此,就需要监听文档的修改事件了。定义一个监听器也是很简单的:

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中立即重新加载,这真的很方便。

posted @ 2016-06-06 18:35  小小西  阅读(1733)  评论(0编辑  收藏  举报