Sublime Text 2是一个高度可定制的文本编辑器,一直以来对希望有一个快速强大现代的编辑工具的的程序员保持着持续的吸引力。现在,我们将创建自己的一个Sublime plugin,实现用Nettuts+ Prefixr API处理CSS实现跨浏览器CSS的目的。

  当完成时,你会深入的明了如何创建一个 Sublime Prefixr plugin,并且有能力去写你自己的编辑器插件。

 前言:术语和参考资料

The extension model for Sublime Text 2 is fairly full-featured.

  Sublime Text 2的扩展模型是相当的功能全面。你可以改变语法高亮,实际的编辑器外观,以及所有的菜单项。此外,还可以创建新的build环境,自动补全,语言定义,代码区段,宏,键绑定,鼠标绑定以及插件。所有这些不同形式的改装都是用组织在package中的文件来实现的。

  所谓pacakage就是一个存储在你的Packages目录中的文件夹。你可以点击Preferences > Browse Packages… 菜单进入你的Packages目录。也可以通过创建一个zip文件并且把扩展名改为.sublime-package来实现把pacakage打包成一个单独文件。我们将在本教程中讨论一点怎么打包。

  Sublime绑定了很多不同的package。大不多数绑定的都是和特定语言相关的package,包括语言定义,自动补全以及build环境。除了语言相关的package,还有两个Default和User package。Defaultpackage包含了所有的标准键绑定,菜单定义,文件设置和一大堆用python写的插件。

During the process of writing a plugin, the   Sublime Text 2 API reference will be essential.

  要写一个插件, Sublime Text 2 API reference是根本。此外,Defaultpackage对于怎么做我们的工作也是一个很好的参考。编辑器的大部分功能都是通过commans命令来实现,除了敲入字符之外的所有操作都可以通过commans完成。查看Preferences > Key Bindings – Defaultmenu ,你可以找到很多有用的内建的功能。

  现在,pacakge和产检的区别已经清楚了,可以开始写我们的插件了。

 第一步 - 起步

  Sublime有一个功能可以产生一个简单插件所需要的Python代码框架。选择Tools > New Plugin…菜单,可以打开一个新的文件,带有下面的样式:

1
2
3
4
5
import sublime, sublime_plugin
 
class ExampleCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        self.view.insert(edit, 0, "Hello, World!")

  可以看到,引入了两个Sublime Python的模块,使得我们可以访问其API并且创建一个新的类。在开始编辑创建我们自己的插件之前,请先保存这个文件。

  要保存这个文件我们需要创建一个package来保存它。 按下 ctrl+s(Windows/Linux) or cmd+s (OS X) 来保存文件。保存对话框默认打开Userpackage,不要把我们的文件存在那里,而是创建一个新的文件夹,命名为Prefixr。

1
2
3
4
5
6
7
8
9
10
Packages/
- OCaml/
- Perl/
- PHP/
- Prefixr/
- Python/
- R/
- Rails/

  现在,把我们的文件保存在Prefixr文件夹中,命名为Prefixr.py。其实文件名并不重要,只要以.py为扩展名就可以。但方便起见,还是用我们的插件的名字吧。

  现在,插件已经做了保存。我们可以试着运行了。输入 ctrl+`打开Sublime的控制台,这是一个可以访问API的Python控制台。输入下面的Python代码来测试我们的新插件:

1
view.run_command('example')

  你将看到Hello World被插入到了我们的插件文件的开头。接下来继续之前先Undo掉这个新的插入。

 第二步 - Comman的类型和名字

  对于一个插件,Sublime提供了三种类型的command。

  • Text commands 提供通过一个View对象访问被选定的文件或者buffer的内的能力
  • Window commands 提供一个Window对象,可引用当前的窗口
  • Application commands 没有引用任何特定的窗口,文件或者buffer,很少使用。

  因为我们要用我们的插件来操作CSS文件或者buffer里面的内容,所以我们要使用 sublime_plugin.TextCommand 类作为我们定制的Prefixr命令的基类。这时,我们就需要命名我们命令的类名了。

  在我们的代码框架中,你可以看到下面的类:

1
class ExampleCommand(sublime_plugin.TextCommand):

  在我们运行命令时,在控制台中执行的是下面的代码:

1
view.run_command('example')

  Sublime将把继承自任意一个 sublime_plugin类(TextCommand,WindowCommand or ApplicationCommand)的类的名字的Command后缀去掉,并且用下划线符号命名替换驼峰式命名。

  这样一来,为创建一个名字是prefixr的command,类名就必须是PrefixrCommand。

1
class PrefixrCommand(sublime_plugin.TextCommand):

 第三步 - 选定文本

  Sublime最有用的功能之一就是具备多行选定的功能

  现在,我们已经正式命名了我们的插件,可以开始从当前的buffer中获取CSS并且发送到Prefixr API上了。Sublime最有用的功能之一就是具备多行选定的功能。由于要获取选定的文件,我们需要把所有选定的行放入我们的插件中处理,而不仅仅是第一个选定的。

  由于我们写的是一个文本命令,所以可以通过self.view访问当前view。view对象的self()方法将返回一个当前选定内容的iterable Region集合,我们可以通过花括号扫描到这些内容。若找不到花括号,可以扩大选定内容到周围的括号,以保证整个块有一个括号前缀。选定内容中是否包含花括号还将有利于我们后面对Prefixr API返回的内容作空白调整和格式调整。

1
2
3
4
5
braces = False
sels = self.view.sel()
for sel in sels:
    if self.view.substr(sel).find('{') != -1:
        braces = True

  用这几行代码替换框架中的run()方法中的代码。

  若未找到任何的花括号,我们需要循环检测每一个选定区段,把每一个区段和后括弧关联起来。之后,用带有to参数设置为 brackets的内建命令 expand_selectionl来确保获取了每个CSS块的完整内容。

1
2
3
4
5
6
7
8
if not braces:
    new_sels = []
    for sel in sels:
        new_sels.append(self.view.find('\}', sel.end()))
    sels.clear()
    for sel in new_sels:
        sels.add(sel)
    self.view.run_command("expand_selection", {"to": "brackets"})

  若果你想再检查一次你的代码,你可以和源代码zip文件中的Prefixr1.py文件对比一下。

 第四步 - 线程

为防止糟糕的连接破坏其他正常工作,我们需要确保在后台完成Prefixr API调用。

  此时,选定的文本已经扩展到了能抓取每个CSS块的完整内容。现在,我们需要把他们发送打牌Prefixr API上。这只需要一个简单的HTTP请求,用urllib和urllib2模块就可以实现。但是,在发起请求之前,需要想想一个潜在的web请求延迟会对编辑器性能造成的影响。如因为某些原因,用户处在一个很慢的连接环境下,对Prefixr API的请求很可能需要好几秒钟乃至更多。

  为防止糟糕的连接破坏其他正常工作,我们需要确保在后台完成Prefixr API调用。若你不了解线程,很基础的一种解释就是,线程是使你的程序的多个代码块在同一时间运行的机制。这在我们的插件环境中是很重要的,因为这样做可以避免在向Prefixr发送数据和等待响应的过程中Sublime处于不可用状态。

 第五步 - 创建线程

  我们将使用Python threading 模块来创建线程。要使用该模块,需要创建一个继承threading的新类。Thread包含一个所有线程代码都在其中执行的run()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class PrefixrApiCall(threading.Thread):
    def __init__(self, sel, string, timeout):
        self.sel = sel
        self.original = string
        self.timeout = timeout
        self.result = None
        threading.Thread.__init__(self)
 
    def run(self):
        try:
            data = urllib.urlencode({'css': self.original})
            request = urllib2.Request('http://prefixr.com/api/index.php', data,
                headers={"User-Agent": "Sublime Prefixr"})
            http_file = urllib2.urlopen(request, timeout=self.timeout)
            self.result = http_file.read()
            return
 
        except (urllib2.HTTPError) as (e):
            err = '%s: HTTP error %s contacting API' % (__name__, str(e.code))
        except (urllib2.URLError) as (e):
            err = '%s: URL error %s contacting API' % (__name__, str(e.reason))
 
        sublime.error_message(err)
        self.result = False

  这里,我们使用thread的__init__()方法来设置所有的在web请求中需要的数据变量。run()方法包含所有的设置代码和执行向Prefixr API 的http请求的代码。由于线程并发的执行,所以直接返回值是不可行的,取而代之的我们设置self.result作为调用的结果。

  鉴于我们在我们的插件中开始使用其他一些模块,我们必须在程序顶端增加import语句。

1
2
3
import urllib
import urllib2
import threading

  现在我们有了一个线程类来执行HTTP请求,我们需要为每一个selection块创建一个线程。为此,回到我们的PrefixrCommand类的run方法中,使用下面的循环:

1
2
3
4
5
6
threads = []
for sel in sels:
    string = self.view.substr(sel)
    thread = PrefixrApiCall(sel, string, 5)
    threads.append(thread)
    thread.start()

  我们记录了每一个我们创建的线程,然后调用start()方法开启每一个线程。如果想再次检查你的工作,对比源代码文件zip文件中的filePrefixr1.py文件。

 第六步 - 准备结果

  现在,我们已经开始了实际的Prefixr API请求,在处理HTTP相应之前,我们需要处理最后几个问题。

  首先,我们清楚所有的selection,因为之前我们修改了他们。稍后将把他们设置成一个更合理的状态。

1
self.view.sel().clear()

  此外,我们开启一个新的Edit对象,把undo和redo操作组织在一起。我们指定我们在为prefix命令创建一个这样的组。

1
edit = self.view.begin_edit('prefixr')

  作为最后一步,我们调用下面的方法来处理API的相应结果。

 第七步 - 处理线程

  此时,我们的线程已经在运行了,认知已经运行结束了。下一次,我们需要完成刚刚调用的handle_threads()方法。这一方法将循环处理线程列表并且寻找不再运行的线程。

1
2
3
4
5
6
7
8
9
10
def handle_threads(self, edit, threads, braces, offset=0, i=0, dir=1):
    next_threads = []
    for thread in threads:
        if thread.is_alive():
            next_threads.append(thread)
            continue
        if thread.result == False:
            continue
        offset = self.replace(edit, thread, braces, offset)
    threads = next_threads

  如果线程仍然运行着,我们将其加入线程列表一遍后面再检测。如果result是False,则忽略它。然后,对于返回正确的结果,再调用一个马上就要说到的replace()方法。

  原文地址:http://net.tutsplus.com/tutorials/python-tutorials/how-to-create-a-sublime-text-2-plugin/

如何开发 Sublime Text 2 的插件的更多相关文章

  1. Sublime text追踪函数插件:ctags 和php代码格式化

    转自:http://blog.csdn.net/zm2714/article/details/8076077 这两天一直纠结两款编辑器——eclipse和sublime Text. eclipse的p ...

  2. Sublime text追踪函数插件

    Sublime Text2/3怎样在Ubuntu中配置CTags插件 | 浏览:1278 | 更新:2014-03-05 10:34 1 2 3 4 5 6 7 分步阅读 本文详解在Ubuntu Li ...

  3. Ubuntu安装 Sublime Text 及常用插件推荐

    之前一直在用 Code Blocks 这个IDE工具,可用着还是感觉不怎么好,于是在网上找到了一篇文章,上面介绍了不少IDE,我找到了Sublime Text 感觉挺不错的. 帖子地址: http:/ ...

  4. Sublime Text 3 + phpfmt 插件

    Sublime Text 3 + phpfmt 插件 phpfmt 插件参数 ```json{ "autocomplete": true, "enable_auto_al ...

  5. sublime Text不能安装插件的解决办法

    我的sublime Text不能安装插件,提示如下错误 解决办法如下: 1.点击Preferences > Browse Packages菜单 2.进入打开的目录的“上层目录”,然后再进入Ins ...

  6. ATOM & Sublime Text 下MarkDown插件功能比较

    ATOM & Sublime Text 下MarkDown插件功能比较 作者:net66 更新日期:2016-6-14 10:50 [一] 编辑器 Sublime Text3 vs Atom ...

  7. Sublime Text 3常用插件安装

    Sublime Text 3常用插件安装 PS:sublime是笔者用过的最好用的编辑器,也是最轻量级,功能最强大的编辑器.好东西应该被分享! 1.直接安装 --下载安装包解压缩到Packages目录 ...

  8. 解决sublime text无法安装插件问题

    解决sublime text无法安装插件问题最近在sublime text3中使用命令ctrl+shift+p命令安装插件发现不能安装了,一会儿报错 这个错误表示没有可用的安装包,经过一番探索发现是配 ...

  9. quick-cocos2d-x开发工具sublime text及其强力插件QuickXDev

    更新:如今QuickXDev已经能够通过Package Control下载了,全部QuickXDev相关的请看这里:http://my.oschina.net/lonewolf/blog?catalo ...

随机推荐

  1. VirtualBox中安装CentOS-6.6虚拟机(转载)

    1. 下载 可以到官网下载,http://mirror.centos.org/centos/ 如果下载速度太慢的话,也可以到163镜像下载: http://mirrors.163.com/centos ...

  2. Android 计算器界面

    高仿魅族魅蓝NOTE 2风格 <?xml version="1.0" encoding="utf-8"?> <TableLayout xmln ...

  3. The Pilots Brothers' refrigerator

    2965 he Pilots Brothers' refrigerator Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 1 ...

  4. 3d中的坐标系的概念

    世界坐标系 世界坐标系是一个特殊的坐标系,它建立了描述其它坐标系所需要的参考框架.从另一方面说能够用世界坐标描述其它坐标系的位置,而不能用更大的.外部的坐标系来描述世界坐标系. 物体坐标系 物体坐标系 ...

  5. 自动编号维护SNRO

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  6. Python入门-引号

    Python 接收单引号(' ),双引号(" ),三引号(''' """) 来表示字符串,引号的开始与结束必须的相同类型的. 其中三引号可以由多行组成,编写多行 ...

  7. log4j的使用(1) —— 简单入门篇

    这里会介绍三种打印日志的方法:控制台Console,文件File,数据库DataBase 1.下载lo4j的jar包并导入project 2.因为要在数据库添加日志,所以先新建一个库,并新建打印日志的 ...

  8. FLASH CC 2015 CANVAS (七)总结

    FLASH CC 2015 CANVAS (一至七)确切来说是自己在摸索学习过程中而写.所以定为“开荒教程”. 去年年底转战H5,半年中一直非常忙也不敢用CC来做项目,担心有BUG或者无法实现需求,所 ...

  9. C#开发Activex控件(1)

    项目结构 创建Activex步骤: 1.选择创建类别(Windows 控件库或类库) 2.设置对应的com属性 AssemblyInfo.cs中须做以下设置:a.引入命名空间:using System ...

  10. JAVA中Response的几种用法(设定时间调整到指定页面 ....... )

    <%@ page language="java" import="java.util.*" pageEncoding="gbk"%&g ...