sublime插件gist源码分析(1)

缘由

最近学习sublime插件开发,准备给leanote写一个插件。
这样可以发挥sublime编辑器的优势,也可以让leanote把笔记管理起来。
学习插件开发,最快的就是看别人怎么做的。
于是找了一个类似的插件,学习下里面的源码。
可以快速了解下api的使用,也顺便巩固下python

学习源码的注意事项

  • 要有目地性。
  • 先从跟踪一个简单功能来,边看边查边实现,查的过程就是学习的过程。
  • 看代码只看关注的功能主线,扩展功能或异常分支都暂时可以略过。
  • 能调试的话,可以把中间结果打印出来,可以更加了解其中机制
  • 对一点有些疑问,不用死磕,可以先记录下来,跳过,回头再来看。
  • 对复杂的逻辑要多看几次,理顺关系。

准备工作

看代码之前,应该是要用这过个插件,了解它的功能和配置。
要在sublime上安装gist插件就不用说了。
再则对插件开发有一定了解,起码要写过hello world。
然后再是下载源代码:
git clone https://github.com/condemil/Gist

文件概览

文件不多,可以把每个文件每简单查看下,确定每个文件都是干嘛的。

  • sublime-menu 菜单配置文件
  • sublime-keymap 快捷键文件
  • gist.py 功能实现的主体,主要关注的
  • helpers.py 辅助函数
  • request.py 网络操作相关的内容
  • setting.py 配置文件

先从快捷键入手,在文件Default (Windows).sublime-keymap
{ "keys": ["ctrl+k", "ctrl+o"], "command": "gist_list" }
分析源码先找一条主线分析,这里选先看gist_list逻辑。
主要功能:

  • 列出你的gist上有哪些代码片段
  • 选择之后,会在sublime中打开。

列出代码片段功能分析

先是发现 GistListCommandBase, GistListCommand这两个类。
GistListCommandBase中有run方法,插件入口,但和example中的run不一样。
先向下看,在 run 的结尾有self.get_window().show_quick_panel(gist_names, on_gist_num)
通过查sublime 插件的 api知道这就是弹选择框的操作,说明找对地方了。
on_gist_num是回调函数,就是等用户选完,会调用这个函数。
再看他是怎么生成gist_names的。
向上面找,gist_name由 filtered生成,filtered由gists_filter(api_request(settings.GISTS_URL))拿到
然后去找api_request的代码,发现在 request.py中
经过解读,就是从配置中拿到token,然后用 gist的 api拿到数据。
(api 说明https://developer.github.com/v3/gists/)

run后面include_users与include_orgs是分支代码可以先不看。
on_gist_num这个回调中,offOrgs与offUsers都是0,所以直接走最后的分支,就是self.handle_gist(self.gists[num - offUsers])
这个handle_gist就是子类的实现
GistListCommand的实现就是open_gist

综上分析,主要流程为

  • GistListCommandBase.run入口
  • api_request拿到gist的列表json,进行过滤,生成gist_names
  • self.get_window().show_quick_panel 显示选择框
  • on_gist_num处理结果,并由子类去调用open_gist

然后自己写了一个简化版。
没有做参数验查,异常处理,只处理简单的情况
做了从gist的api拿到gist的列表,并显示在sublime上,让用户选哪个。
token为gist的 参考 https://github.com/condemil/Gist#generating-access-token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import sublime
import sublime_plugin
import contextlib
import json
import urllib.request as urllib

class ExampleCommand(sublime_plugin.TextCommand):
def run(self, edit):
token ='xxx'
rq = urllib.Request('https://api.github.com/gists')
rq.add_header('Authorization', 'token ' + token)
with contextlib.closing(urllib.urlopen(rq)) as response:
gistjson = json.loads(response.read().decode('utf8', 'ignore'))
descriptions = []
for i in gistjson:
descriptions.append(i['description'])
def callbackfun(index):
sublime.status_message('select: '+descriptions[index])
self.view.window().show_quick_panel(descriptions, callbackfun)

示显代码片段功能

现在拿到了gist相关信息,怎么何把它显示在sublime上呢
继续读open_gist代码。
因为这里只对api的调用。
没有做过多的解读

于是写了一个简化版
实现新建文件,在文件中插入内容,存在临时目录中,并保存

1
2
3
4
5
6
7
8
9
10
11
12
13
import sublime
import sublime_plugin
import tempfile

class ExampleCommand(sublime_plugin.TextCommand):
def run(self, edit):
view = sublime.active_window().new_file()
filename = "Main.java"
view.set_name(filename)
view.run_command('append', {'characters':'public void main'})
view.retarget(tempfile.gettempdir() + '/' + filename)
# view.set_scratch(False)
view.run_command('save')

上面用到的组件总结

sublime plugin api

  • sublime.status_message
  • view.window().show_quick_panel
  • sublime.active_window().new_file()
  • view.set_name()
  • view.run_command(‘append’, {‘characters’:’xx’})
  • view.retarget()
  • view.run_command(‘save’)

  • json
  • urllib
  • contextlib

参考

https://developer.github.com/v3/gists/
https://www.sublimetext.com/docs/3/api_reference.htm