Skip to content

Instantly share code, notes, and snippets.

@rswofxd
Created October 13, 2012 04:31
Show Gist options
  • Save rswofxd/3883230 to your computer and use it in GitHub Desktop.
Save rswofxd/3883230 to your computer and use it in GitHub Desktop.
Python://sublim text 2插件开发实例.py
######################################
开发步骤
Sublime text 2 的插件开发使用的是 Python 。具体接口可以参考 API Reference。而 How to Create a Sublime Text 2 Plugin 提供了一个很好插件开发例子。
使用插件模板
使用 Sublime 菜单 Tools->New Plugin... ,即可创建新的插件:
import sublime, sublime_plugin
class ExampleCommand(sublime_plugin.TextCommand):
def run(self, edit):
self.view.insert(edit, 0, "Hello, World!")
Sublime的插件中含有一个或多个命令(Command)。每个命令的具体实现在 run 函数中。 现在此插件功能为在当前视图开始处插入 "Hello, World!" 字符串。
保存并修改
在 Sublime 的 Packages 文件夹中, 创建新的文件夹 eval_sel 。 Packages 文件夹可以通过菜单 Preferences->Browse Packages 打开。
将已生成的文件保存在 eval_sel 文件中,命名为 eval_sel.py 。 将
class ExampleCommand(sublime_plugin.TextCommand):
重命名为:
class evalselCommand(sublime_plugin.TextCommand):
快捷键配置
在 eval_sel 文件夹下面建立文件:
Default (Linux).sublime-keymap
Default (OSX).sublime-keymap
Default (Windows).sublime-keymap
可以在这些文件中以json格式定义快捷键映射:
[ { "keys": ["ctrl+alt+e"], "command": "evalsel" } ]
这样每次按 ctrl + alt + e 时,就会执行 evalsel 命令。
具体实现
获取选中文本
view.sel() 能够返回选中的区域集合(有可能存在多个选中区域)。获取第一个选中区域。
sel = self.view.sel()[0]
view.substr 则能获取区域所包含的文本
expression = self.view.substr(sel)
传递给解释器
在 Sublime 中,可以使用 Python 的大部分标准类库。这种情况可以使用 subprocess 启动一个子进程,传递字符串到子进程的 stdin ,并从子进程的 stdout 获取结果。
1. 启动子进程
self.process = subprocess.Popen(evaluator, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
evaluator 是用来启动解释器的参数列表,一般是解释器的路径,比如使用 Racket 时为 ["racket"] (racket.exe 已经加入系统路径)。 但这里需要注意一点,使用某些解释器的时候,需要加入额外的参数,让其采用交互模式,比如使用 Python 时为 ["python", "-u", "-i"]。
将 stdin/stdout 设置为 subprocess.PIPE ,则表明打开指向 stdin/stdout 管道。
2. 将表达式传递给子进程
子进程的 stdin/stdout 可以当做标准的文件对象进行读写。 采用交互模式时,在写入后,要注意调用 flush 刷新缓存。
self.process.stdin.write(expression + "\n") self.process.stdin.flush()
3. 从子进程获取结果
从 stdout 读取内容时,需注意读取时会被阻塞,所以要在另一个线程中读取。这里实现了简单的读取线程。
class readThread(threading.Thread):
def __init__(self, process, file_io, output):
self.file_io = file_io
self.output = output
self.process = process
threading.Thread.__init__(self)
def run(self):
if not self.file_io:
return
while True:
line = self.file_io.readline()
if len(line) == 0:
break;
sublime.set_timeout(functools.partial(self.output, "%s" % (line)), 0)
输出结果
Sublime 为插件提供了 output_panel ,相当一个特殊的 view 对象。 需要通过 window 对象的接口,获取 output_panel 对象。
def show_output_view(self):
if not self.output_view:
self.output_view = self.view.window().get_output_panel("evalsel")
self.view.window().run_command('show_panel', {'panel': 'output.evalsel'})
插入文本:
def output(self, info):
self.output_view.set_read_only(False)
edit = self.output_view.begin_edit()
self.output_view.insert(edit, self.output_view.size(), info)
self.scroll_to_view_end() self.output_view.end_edit(edit)
self.output_view.set_read_only(True)
发布
最简单的方法是将插件发布到网上,让用户自己下载到 Packages 文件夹中。
但有更方便的方法就是,通过 Packages control 发布。参考 Submitting a Package 介绍,只需要简单地几步即可。
##########################################
#coding:utf-8
import sublime, sublime_plugin
import os, subprocess, threading, functools
SETTINGS_FILE = "eval_sel.sublime-settings"
class readThread(threading.Thread):
def __init__(self, process, file_io, output):
self.file_io = file_io
self.output = output
self.process = process
threading.Thread.__init__(self)
def run(self):
if not self.file_io:
return
while True:
line = self.file_io.readline()
if len(line) == 0:
break;
sublime.set_timeout(functools.partial(self.output,
"%s" % (line)), 0)
class evalselCommand(sublime_plugin.TextCommand):
def __init__(self, view):
self.view = view
self.output_view = None
self.process = None
self.out_thread = None
def __del__(self):
self.close_process()
def getLang(self):
scopes = self.view.settings().get('syntax')
lang = scopes.split("/")[1]
return lang
def open_process(self):
if self.process and (not self.process.poll()):
return True
lang = self.getLang()
evaluator = sublime.load_settings(SETTINGS_FILE).get(lang, "").split()
print evaluator
if len(evaluator) == 0:
return False
self.process = subprocess.Popen(evaluator,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
# re-start new thread when open new subprocess
self.run_read_thread()
return True
def close_process(self):
if self.process:
self.process.kill()
def run_read_thread(self):
self.out_thread = readThread(self.process,
self.process.stdout,
self.output)
self.out_thread.start()
def run(self, edit):
# always show output panel
self.show_output_view()
if self.open_process():
sel = self.view.sel()[0]
expression = self.view.substr(sel)
self.eval(expression)
def scroll_to_view_end(self):
(cur_row, _) = self.output_view.rowcol(self.output_view.size())
self.output_view.show(self.output_view.text_point(cur_row, 0))
def output(self, info):
self.output_view.set_read_only(False)
edit = self.output_view.begin_edit()
self.output_view.insert(edit, self.output_view.size(), info)
self.scroll_to_view_end()
self.output_view.end_edit(edit)
self.output_view.set_read_only(True)
def show_output_view(self):
if not self.output_view:
self.output_view = self.view.window().get_output_panel("evalsel")
self.view.window().run_command('show_panel', {'panel': 'output.evalsel'})
def eval(self, expression):
self.process.stdin.write(expression + "\n")
self.process.stdin.flush()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment