Sublime Text插件开发的高级例程

2015/01/16 07:43
阅读数 188

原文地址:http://code-tech.diandian.com/post/2012-11-16/40042306130

Sublime插件的开发,可以首先参考How to Create a Sublime Text 2 Plugin

如何开发Sublime插件 还有Sublime中文手册 ,还有个非官方的文档,没有翻译完,希望有志之士发扬开源精神,继续完成,地址https://github.com/yangweijie/sublime-text-unofficial-documentation--cn

虽然看了前面两篇文章,但是对于没有任何python基础的人还是有点困难,本人主攻语言php,看了2星期的python电子书。隔了几个月又基本还回去了,推荐一个网站可以将常用php函数找到对应python版,说起来还真的很像的: http://www.php2python.com     PS:Ruby和python都能跨界,什么时候出个php的就好了O(∩_∩)O~

废话不多说了,先说一下开发插件流程:

1.按照上面的文章开发出Example插件

2.在此插件上添加自己的功能

3.完善菜单、快捷键和配置文件等

注意点:善用调试面板 ctrl+~,多读其他插件的源码,他山之石可以攻玉,借助度娘和谷姐的力量  

先粘上本插件的源码

 

# -*- coding: utf-8 -*-
import sublime, sublime_plugin
import  os,httplib,urllib,urllib2,json,webbrowser,codecs
                                    
def fs_reader(path):
    return codecs.open(path, mode='r', encoding='utf8').read()
                                    
def fs_writer(path, raw):
    codecs.open(path, mode='w', encoding='utf8').write(raw)
                                    
def out_tpl(new,sub=''):
    if sub == '':
        return tpl.replace('{%s}',new)
    else:
        return tpl2.replace('{%s}',new)
                                    
def get_tpl_fullpath(filename,parent_dir=''):
    return packages_path + '\\manual\\' + parent_dir + filename+'.html'
                                    
def write_tpl(filename,content,parent_dir=''):
    if not os.path.isfile(get_tpl_fullpath(filename,parent_dir)):
        fs_writer(get_tpl_fullpath(filename,parent_dir),content)
                                    
def open_tab(url):
    webbrowser.open_new_tab(url)
                                    
def get_content(id,parent_dir=0):
    conn = httplib.HTTPConnection("doc.thinkphp.cn")
    conn.request("GET", "/api/view/"+id)
    r1 = conn.getresponse()
    data1 = r1.read()
    data1 = json.loads(data1)
    data = data1['data']
    content = ''
    for i in data:
        content= content + i['content']
    if parent_dir == 0:
        return out_tpl(content)
    else:
        return out_tpl(content,1)
settings = sublime.load_settings('Thinkphp.sublime-settings')
packages_path = sublime.packages_path() + '\\Thinkphp'
manual_dir = settings.get('manual_dir')
tpl = fs_reader(os.path.join(packages_path + '\\'+ manual_dir +'\\public', 'book.tpl'))
tpl2 = fs_reader(os.path.join(packages_path + '\\'+ manual_dir +'\\public', 'book_sub.tpl'))
                                    
class ThinkphpCommand(sublime_plugin.TextCommand):
                                    
    def see(self,id,name,parent_dir=''):
        #url = 'http://doc.thinkphp.cn/manual/' + self.data[arg]['name']
        content = get_content(id,parent_dir)
        write_tpl(name,content,parent_dir)
        url = 'file://'+get_tpl_fullpath(name,parent_dir)
        open_tab(url)
                                    
    def build(self,id,name,parent_dir=''):
        content = get_content(id,parent_dir)
        write_tpl(name,content,parent_dir)
                                    
    def run(self, edit):
        data = self._init()
        self.data = data
        chapter = []
        tree = []
        sort_data = []
        k = 0
        for i in data:
            chapter.insert(int(i['id']),i['title'])
            sort_data.insert(k,i['name'])
            state = i.get('_child', None)
            if state:
                tree.insert(k,i['_child'])
            else:
                tree.insert(k,None)
            k = k+1
        # print chapter
        self.chapter = chapter
        self.sort_data = sort_data
        self.tree = tree
        self.view.window().show_quick_panel(chapter, self.panel_done)
                                    
    def panel_done(self,arg):
        if arg == -1:
            pass
        else:
            self.tree_key = arg
            if self.tree[arg] == None:
                self.see(self.data[arg]['id'],self.data[arg]['name'])
            else:
                if not os.path.isdir(packages_path + '\\'+ manual_dir +'\\'+self.sort_data[arg]):
                    os.mkdir(packages_path + '\\'+ manual_dir +'\\'+self.sort_data[arg])
                child =[]
                k = 0
                for i in self.tree[arg]:
                    child.insert(k,i['title'])
                    k +=1
                                    
                self.view.window().show_quick_panel(child, self.child_done)
                                    
    def child_done(self,arg):
        if arg == -1:
            self.view.window().show_quick_panel(self.chapter, self.panel_done)
        else:
            self.see(self.tree[self.tree_key][arg]['id'],self.tree[self.tree_key][arg]['name'],self.sort_data[self.tree_key]+'\\')
                                    
    def _init(self):
        conn = httplib.HTTPConnection("doc.thinkphp.cn")
        conn.request("GET", "/api")
        r1 = conn.getresponse()
        data1 = r1.read()
        data1 = json.loads(data1)
        return data1['data']
                                    
    def update_manual(self):
        data = self._init()
        for j in data:
            if not j.get('_child', None):
                self.build(j['id'], j['name'])
            else:
                parent_dir = j['name']
                if not os.path.isdir(packages_path + '\\'+ manual_dir +'\\'+j['name']):
                    os.mkdir(packages_path + '\\'+ manual_dir +'\\'+j['name'])
                for t in j['_child']:
                    sublime.set_timeout(self.build(t['id'],t['name'],parent_dir+'\\'),100)
        sublime.status_message('the manual has been generated')
                                    
    def search_panel(self):
        self.view.window().show_input_panel('search in thinkphp manual?', '', self.search_done, self.search_change, self.search_cancel)
                                    
    def search_done(self,arg):
        data = {'keywords' : arg}
        f = urllib2.urlopen(url = 'http://doc.thinkphp.cn/api/search',data = urllib.urlencode(data))
        data = f.read()
        data = json.loads(data)
        if data['data'] == []:
            sublime.error_message('No Search result !')
        else:
            chapter = []
            data = data['data']
            self.search_list = data
            for i in data:
                chapter.insert(int(i['id']),i['title'])
            self.view.window().show_quick_panel(chapter, self.manual_search_done)
                                    
    def manual_search_done(self,arg):
        if arg == -1:
            pass
        else:
            choose = self.search_list[arg]
            data = self._init()
            for i in data:
                if i['id'] == choose['id']:
                    self.see(choose['id'], choose['name'])
                else:
                    state = i.get('_child', None)
                    if state:
                        for j in i['_child']:
                            if j['id'] == choose['id']:
                                if not os.path.isdir(packages_path + '\\'+ manual_dir +'\\'+i['name']):
                                    os.mkdir(packages_path + '\\'+ manual_dir +'\\'+i['name'])
                                self.see(choose['id'], choose['name'], i['name']+'\\')
                                    
    def search_change(self,arg):
        pass
                                    
    def search_cancel(self):
        pass       
                                    
class update_thinkphp_manual(ThinkphpCommand,sublime_plugin.TextCommand):
    def run(self, edit):
       self.update_manual()
                                    
class search_word_thinkphp_manual(ThinkphpCommand,sublime_plugin.TextCommand):
    def run(self, edit):
        region = self.view.sel()[0]
        if region.begin() != region.end():
            self.search_done(self.view.substr(region))
        else:
            self.search_panel()
                                    
class search_thinkphp_manual(ThinkphpCommand,sublime_plugin.TextCommand):
    def run(self, edit):
        self.search_panel()

# -*- coding: utf-8 -*-  

这行注释防止代码有中文导致的乱码显示

self.view.window().show_quick_panel

这行里的self.view.window()获得window类,之后就可以调用window类的所有方法了,比如show_quick_panel显示下拉列表

sublime.packages_path()

这个是获取当前插件包的路径,根据安装版和绿色版返回的地址不同,前者win的是在用户文档目录下,后者是在Sublime软件安装的Data\Packages里

classThinkphpCommand(sublime_plugin.TextCommand):

这个才是开始插件命令的定义,注意Thinkphp一定要和插件目录名一致,当我们保存插件py文件时,st会重新加载所有插件一次,这时在终端面板里可以通过view.runCommand('thinkphp')来运行它看效果,命令名要用小写了

sublime.error_message()

Sublime类的方法可以直接调用

class update_thinkphp_manual(ThinkphpCommand,sublime_plugin.TextCommand):

   defrun(self, edit):

      self.update_manual()

这个是前面2篇文档没讲的,我是在gist插件的源码里看到的,如何在一个插件里扩展多条命令,并且可以使用当前插件的方法

settings = sublime.load_settings('Thinkphp.sublime-settings')

manual_dir = settings.get('manual_dir')

这两句是读取配置和获取配置的值,当然还有自己去更新配置

region = self.view.sel()[0]
        if region.begin() != region.end():
            self.search_done(self.view.substr(region))

这个是最简单的获取选区的判断后使用的例子

接下来时菜单Main.sublime-menu:

[   
    {
        "id": "tools",
        "children":
        [
            {
                "caption": "ThinkPHP manual",
                "id": "ThinkPHP manual",
                "command": "thinkphp"
            },
            {
                "caption": "ThinkPHP manual: search",
                "id": "ThinkPHP manual: search",
                "command": "search_thinkphp_manual"
            },
            {
                "caption": "ThinkPHP manual: build book",
                "id": "ThinkPHP manual: build book",
                "command": "update_thinkphp_manual"
            }
        ]
    },
    {
        "caption": "Preferences",
        "mnemonic": "n",
        "id": "preferences",
        "children":
        [
            {
                "caption": "Package Settings",
                "mnemonic": "P",
                "id": "package-settings",
                "children":
                [
                    {
                        "caption": "ThinkPHP",
                        "children":
                        [
                            {
                                "command": "open_file",
                                "args": {"file": "${packages}/Gist/ThinkPHP.sublime-settings"},
                                "caption": "Settings – Default"
                            },
                            {
                                "command": "open_file",
                                "args": {"file": "${packages}/User/ThinkPHP.sublime-settings"},
                                "caption": "Settings – User"
                            },
                            {
                                "command": "open_file",
                                "args": {
                                    "file": "${packages}/ThinkPHP/Default (Windows).sublime-keymap",
                                    "platform": "Windows"
                                },
                                "caption": "Key Bindings – Default"
                            },
                            {
                                "command": "open_file",
                                "args": {
                                    "file": "${packages}/User/Default (Windows).sublime-keymap",
                                    "platform": "Windows"
                                },
                                "caption": "Key Bindings – User"
                            },
                            { "caption": "-" }
                        ]
                    }
                ]
            }
        ]
    }
                                  
]

这里注意的是"${packages}"代表当前插件目录,还有就是顺序,tools在preferences前面,放到后面可能不起作用了

Thinkphp.sublime-commands

[
    {
        "caption": "ThinkPHP manual: view list",
        "command": "thinkphp"
    },
    {
        "caption": "ThinkPHP manual: build book",
        "command": "update_thinkphp_manual"
    },
    {
        "caption": "ThinkPHP manual: search",
        "command": "search_thinkphp_manual"
    }
]

只有在命令文件里写过的命令才会在ctrl+shift+p的命令列表中显示

右键 Context.sublime-menu:

[
    { "command": "search_word_thinkphp_manual", "caption": "search_thinkphp_manual here" }
]

菜单文件配了选项在菜单中会出现,但是只有在对应命令存在可执行,方会显示可点击的状态,而不是灰色

插件命名最好使用首字母大写的驼峰法命名如Thinkphp,最好不要带_ 空格等。

希望大家能从这篇文章中得到一些帮助,然后投入到轰轰烈烈的Sublime插件开发队伍中去。。


展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部