sublime text 3(以下简称ST3)是一个非常性感的文本编辑器,很多前端开发者用她作为IDE,经过配置也能将她打造成一个包括c、php等其他语言的IDE。ST3是用C++开发的,她的插件系统是由python3编写,所以只要你熟悉python,就可以很轻松的为ST3开发插件。下面是我记录的一个简单插件(GithubMarkdown)的开发过程,希望给入门的同学提供一定的参考。
准备工作
一个简单插件示例
在菜单中依次选择Tools
->Developer
->New Plugin
这是一个最简单的插件的示例,保存该文件,默认会保存到Sublime Text 3/Packages/User目录下,文件名为untitled。
import sublime
import sublime_plugin
class ExampleCommand(sublime_plugin.TextCommand):
def run(self, edit):
self.view.insert(edit, 0, "Hello, World!")
ctrl+~
打开控制台,看到reloading plugin User.untitled
,说明插件已经被自动加载了。
然后在控制台的输入框里输入view.run_command('example')
,可以看到当前编辑的文件开头插入了Hello, World!
,说明这个简单插件生效了。是不是超级简单?
view.run_command的参数是下划线风格的命令名,ST3在执行的时候将首字母大写并去掉下划线,如执行view.run_command('my_example')
则ST3会执行MyExampleCommand
类里的run方法
插件保存位置
选择preferences
->Broswer pakages
打开插件保存的目录,在我的mac os上是/Users/yxr/Library/Application Support/Sublime Text 3/Packages
。通过package control安装的插件有的以源码的形式存放在该目录下,有的则以.sublime-package
结尾的压缩包存放在上层目录的Installed Packages
文件夹下。ST3将会从3个地方加载插件:
- Installed Packages (only .sublime-package files)
- Packages
- Packages/
/
根据约定,不会加载更深层次嵌套的python文件,也就是说如果将前面的untitled.py
放到Packages
目录下会生效,而放到User/New/
下面将不会生效。
下面我要开发的插件直接保存在Packages/GithubMarkdown/
插件开发文档和源码示例
- 官方api文档
- 非官方文档 由于ST3并没有提供详细的插件开发教程,因此更好的方式是直接阅读现有的插件源码。
- a tutorial: how to create a sublime text 2 plugin
- 官方api文档说
Packages/Default
目录下有很多示例插件,但我没有找到Default文件夹。不过我在ST3的安装目录下找到了:/Applications/Sublime Text.app/Contents/MacOS/Packages/Default.sublime-package
,将该压缩包解压到桌面,就可以看到里面的内容。py文件就是插件,对于插件开发,这些示例就是最好的学习资源。
GithubMarkdown的开发过程
GithubMarkdown的功能是调用github提供的api渲染markdown。主要是解决两个需求:
- 我很喜欢GFM这种风格的markdown
- 添加或修改github项目的README时在本地预览效果
有关GFM请参考Mastering Markdown
插件工作原理
- 获取当前文件的路径(第5步会用到)
- 获取当前编辑的文件内容(包含没有写到磁盘的改动)
- 调用github api渲染
文档说github上的README.md使用markdown mode,而issues里的评论使用gfm mode。但是奇怪的是以
markdown
的模式并不能渲染task list,这在github的README里都能渲染。所以,为了支持高级功能这里使用gfm
模式。 - 将返回作为body部分插入到html文件模版中,由于markdown渲染是不带样式的,所以需要添加额外的样式表来获得和github readme一致的样式,我使用的是sindresorhus/github-markdown-css项目提供的样式
- 将带有样式的html写入到原文件所在文件夹下,文件名相同,后缀为.html 如果每次转换后在默认浏览器中打开,那么多次渲染将会打开很多的标签页,这种方式并不友好,所以采用这种简单的方式,直接写到文件中,用户可以在浏览器中打开这个html文件,每次更新后刷新一下。
核心功能的实现
克隆代码到本地并切换到特定的tag
git clone https://github.com/yanxurui/GithubMarkdown
git checkout v1
- 该插件使用的是TextCommand,每一个TextCommand对应一个view,可以通过
self.view
访问view对象。同理WindowCommand对应一个window对象,应该根据需要选择相应的command类型。 sublime.load_settings
加载插件的配置文件,配置文件是json对象,分User和Default两种,分别位于Packages/User/<setting-name>
和Packages/<package-name>/<setting-name>
中,前者覆盖后者。该方法返回Settings
对象,类似dict
self.view.file_name
获取当前文件的完整路径self.view.substr(sublime.Region(0, self.view.size()))
获取当前view的内容sublime.status_message
在sublime的状态栏上显示提示信息,第一条信息sublime.status_message('GithubMarkdown: request github api...')
看不到,这是个bug有待解决sublime.error_message
弹窗提示信息sublime.load_resource
加载给定的资源,返回str
,名字格式应该类似Packages/Default/Main.sublime-menu
耗时任务阻塞主线程
上面的插件在使用体验上很不好,网络请求是耗时任务,会把主线程阻塞,这是桌面应用开发的大忌。因此要把耗时任务放到子线程中执行,事实上ST3已经提供了非常方便的异步执行耗时任务的方法set_timeout_async
。修改后的版本可以通过如下命令获取:
git checkout v2
GithubMarkdown: request github api...
,当请求返回的时候该提示消失。
命令面板
运行某个命令除了在console里执行view.run_command('<command_name>')
还可以将命令加入到Command Pallete
(命令面板)中,每次从命令面板搜索来执行,而且ST3会记住上次执行的命名,这就方便多了。新增Default.sublime-commands
:
[
{
"caption": "Github Markdown: render a markdown document by github",
"command": "github_markdown"
}
]
shift+cmd+p
打开命令面板,输入github即可看到我们添加的命令,回车执行。下次打开命令面板还能看到该命令处于选中状态。
快捷键
很多时候通过Command Pallete来调用命令还是不够方便,幸好ST3能让我们轻松的设置快捷键。在不通的OS上快捷键是不同的,因此需要针对每种OS配置相应的快捷键。以OS X为例,在我们的插件目录下创建Default (OSX).sublime-keymap
:
[
{
"keys": ["ctrl+alt+g"],
"command": "github_markdown"
}
]
keys
和command
两个键,以及可选的args
。
同理,为其他系统绑定快捷键只需要增加相应的配置文件即可,参考目录下的Default (Windows).sublime-keymap
和Default (Linux).sublime-keymap
。windows和linux的快捷键几本一致。
菜单项
能够定制菜单是ST3提供的另一项非常酷的事情,方法是创建.sublime-menu
文件,最常用的文件名有三种:
Main.sublime-menu
controls the main program menuSide Bar.sublime-menu
controls the right-click menu on a file or folder in the sidebarContext.sublime-menu
controls the right-click menu on a file being edited 本插件目录下的Main.sublime-menu“Side Bar” is when you right-click a document or folder on the left side of the application (not shown when opening individual files), and “Context” is the right-click menu over the text area.
上下文菜单
首先为这个插件增加一个上下文菜单,也就是在编辑器右键出现的菜单项。在该目录下创建Context.sublime-menu
[
{ "command": "github_markdown", "caption": "Render this markdown file" }
]
open in
browser
菜单项一样,需要在类里增加一个方法:
def is_visible(self):
return self.view.file_name() is not None and (
self.view.file_name()[-3:] == ".md" or
self.view.file_name()[-3:] == ".MD")
主菜单
然后再添加一个插件设置菜单,方便用户修改参数配置和重新绑定快捷键,新增Main.sublime-menu
文件
[
{
"caption": "Preferences",
"mnemonic": "n",
"id": "preferences",
"children": [
{
"caption": "Package Settings",
"mnemonic": "P",
"id": "package-settings",
"children": [
{
"caption": "GithubMarkdown",
"children": [
{
"command": "open_file",
"args": {
"file": "${packages}/GithubMarkdown/GithubMarkdown.sublime-settings"
},
"caption": "Settings – Default"
},
{
"command": "open_file",
"args": {
"file": "${packages}/User/GithubMarkdown.sublime-settings"
},
"caption": "Settings – User"
},
{
"caption": "-"
},
{
"command": "open_file",
"args": {
"file": "${packages}/GithubMarkdown/Default (OSX).sublime-keymap",
"platform": "OSX"
},
"caption": "Key Bindings – Default"
},
{
"command": "open_file",
"args": {
"file": "${packages}/User/Default (OSX).sublime-keymap",
"platform": "OSX"
},
"caption": "Key Bindings – User"
},
{
"caption": "-"
},
{
"command": "open_file",
"args": {
"file": "${packages}/GithubMarkdown/README.md"
},
"caption": "README"
}
]
}
]
}
]
}
]
mnemonic
表示助记符,它的作用是在windows和linux系统上作为一种选择菜单的快捷方式。Preferences
和Package Settings
是已有的菜单项,可以不加caption和mnemonic,但是其他插件在主菜单的配置中都加上了,这也许是一种惯例。- 快捷键配置需要根据操作系统打开相应的文件,使用platform参数选择。
其他
调试技巧
ST3没有设置代理的功能,也不支持系统代理(在我的OS X上如此)。有时候为了调试需要抓取http请求的包就成了难题,简单的将charles设置成系统代理是抓不到ST3的包。还好,ST3支持http_proxy
和https_proxy
这两个环境变量,因此调试的时候可以这样启动ST3
http_proxy=http://localhost:8888 https_proxy=http://localhost:8888 "/Applications/Sublime Text.app/Contents/MacOS/Sublime Text"
http://localhost:8888
是chalse监听的地址。由于github的api都是通过https访问的,因此还需要在chalse中打开api.github.com的https解析。
另外,如果要通过Package control下载被墙的插件是可以通过package control的配置文件设置代理(在package control的默认配置文件中搜索proxy)。
提交到package control
ST官方没有提供插件中心,这一点我觉得做得不好。好在package control这个社区维护的插件给我们带来了福音,它支持我们检索、安装和更新插件。提交插件到package control的流程参见文档。由于该平台的作者不欢迎我们提交功能和已有插件类似的插件,因此我这个插件便没有提交。
开源协议&README
和其他github项目一样,加上开源协议和REAME,README很重要!!!