sublime优化之路
Emacs的忠粉竟然开始写sublime的优化之路,所谓世事难料。Emacs的可配置化,可玩性真是无与伦比,里面的插件丰富,质量高,更新频繁。但有一个致命的缺点,就是在公司有安全扫描的时候,启动和运行非常缓慢。因为在自己的电脑上面运行非常快,之前以为只是个别公司的安全控件导致极度缓慢,时不时会僵死失去响应,很烦人,但经历过几个公司都一样,就想要换一换了。本来是绝世宝刀屠龙刀,但挥舞不动也只能属于鸡肋应用了。
而Sublime Text基本上各个公司的软件库里面都有,下载下来主要当做notepad记事本的替换品,体验之后,发现界面很漂亮,快,真的是快,并且相对比较稳定,毕竟商业化产品。后来就想要不用Sublime替换Emacs,了解之后,发现Sublime使用python做为插件语言,比较好上手,使用范围也广,做为第二主要语言保持熟悉也挺好的,这也是放弃VIM的原因。
粗略使用Sublime Text安装插件,还是挺惊喜的。安装速度很快,Emacs的安装之前有自己的elpa,Malpa,国内速度很慢,时不时不能使用,但可以切换国内源。后来的包管理基本上切换到github上去了,那更糟糕了。github基本上很难把包下载完,更新一下就挂了。这里也要吐槽一下,一个Emacs怎么需要那么多的包,安装完起码4、500MB了,这还是个编辑器吗?VS code也就400多MB。
顺便说一下VS code,本来以为是轻量化的编辑器,实际用下来也不怎么轻量化了,体积不小,启动速度和sublime也没得比。里面的包安装体验还是很不错的,整体感觉也是中规中矩。它基于electron开发,扩展语言应该用的node-js,这个写vue什么的也还熟悉,只是更新起来也是一大坨。
最后说一下,Sublime和VS code都是TextMate那边模仿过来的,TextMate是Mac下的著名的文本编辑器软件,与BBEdit一起并称苹果机上的Emacs和Vim,之前是收费软件,首创的Snippet、多鼠标操作等也被其他编辑器吸收,开发二代耗时几年,再回过头来,市场也丢失大部分,干脆就开源了。所以,可以这么理解,TextMate是Mac下比肩Emacs的编辑器,而Sublime将TextMate扩展到各个平台。
推荐的插件
Package
- Package control
这个必须安装,安装这个之后才方便安装其他的插件。安装完成之后可以通过Command Palette
进行调用。Command Palette
中可以支持所有注册的命令的fuzzy模糊搜索并调用。所有的操作都要定义为Command
,执行Command
有三种方式,一是刚刚讲的Command Palette
,先通过.sublime-command
注册命令,然后搜索过滤出命令;二是通过.sublime-keymaps
绑定快捷键进行执行;三是通过.sublime-menu
注册到菜单栏里面去。这三种方式可以共存。当然,严格来讲也还可以通过Console
进行命令的调用,但这种就不方便了,除非调试开发,一般很少使用。
- PackageResourceViewer
默认安装的插件是.sublime-package
后缀的,这个实际上是一个zip包,可以用压缩软件直接打开。这个插件可以用来预览或者是解压这个压缩包里面的单个源码,或者解压几个或全部的插件包。
安装 PackageResourcesViewer,通过 PackageResourcesViewer:Open Resource修改想要调整的代码,保存就会在 packages 文件夹下面创建原有插件的目录结构和python源码文件,然后,在这个文件里面进行修改和覆盖。 - OverrideAudit
这个插件和前面的PackageResourceViewer
类似,也可以打开和解压插件包里面的单个文件,但没有批量解压这个插件包的功能,否则就可以不用前面那个插件了。这个插件是有菜单栏进行使用的,功能如菜单,也是比较好理解的。可以查看插件内容,也可以汇总插件的安装情况,可以查看哪些插件有解压。因为我们有时需要对安装的插件进行一些扩展和调整,这个插件也可以发现原插件是否有更新,哪些文件被覆盖了,等等。如果喜欢自己修改插件,个人觉得这个也是必须要安装的。具体的功能建议安装之后自己好好体验一下。
Emacs
- Emacs Pro Essentials
这个作为一个重度Emacs使用者,必须要安装的,装完之后,大部分都文本操作快捷键之类的,还是可以保持一样的。分屏pane(emacs的window)操作,窗口window(emacs的frame)跳转,view(emacs的buffer)切换等基本还是一致的。但要说到里面的文件操作,那可太难用了。Emacs的交互panel比Sublime的可以高级灵活多了。Sublime的quick_panel只能给定待选列表,然后从中进行选择,对于输入框的内容无法获取,也没有监听输入框的变化的回调函数,那就没法输入的时候,动态的调整待选择的内容。所以,后面讲到的文件的操作,要废很大的劲去绕过去,通过input_panel和quick_panel来回倒腾,或者只能在input_panel里面做简单的提示,而不能自动补全,相当别扭,这个和Emacs比起来还是差了好多。更不用说,根据拼音首字母进行模糊搜索和过滤的能力了,sublime看起来很难实现了。
Markdown
sublime的markdown原生就有还不错的支持,安装下面几个插件就更好了。
- MarkdownEditing
这个提供一些编辑和跳转的增强。 - MarkdownImages(废弃)
可以在编辑页面里面保存的时候自动显示图片内容,还是比较实用的。只是会导致sublime闪动,鼠标乱跳。
解决图片预览的方案 - MarkdownPreview
可以和LiveReload
配合在浏览器中实现实时预览功能。用得比较少,一般情况,也知道自己的文章最后会呈现成什么样,当然,看一看也没有什么不好的。 - LiveReload
可以在保存文档的时候动态的刷新浏览器中的预览页面。需要在浏览器里面安装插件,比如chrome里面要安装livereload.zip插件,国内的插件市场应该打不开,可以离线安装,具体的安装方法可以参考 http://www.xitongzhijia.net/soft/196180.html 。
KeybindingHelper
这个作为插件开发,可以用来了解快捷键绑定了什么命令,通过 Ctrl+super+` 将按键记录窗口打开,可以看到调用了什么命令。
TrailingSpaces(废弃)
可以显示和删除行尾的空白空格,对于一个强迫症者而言,这个是必须的。但看了一下sublime自带的配置,发现sublime自己的功能就能很好的满足要求,应该性能也会更好:
"draw_white_space": ["leading_mixed_tabs", "trailing_all", "selection"],
"trim_trailing_white_space_on_save": "not_on_caret",
AutomaticPackageReloader(废弃)
可以自动加载修改的package,但实际效果不太好,使用简单的 Package reloader
。
Package Reloader
可以通过.reload.json
文件设置包的加载文件和顺序,比较简单可靠。但实际上,对于不同版本的python不支持。默认是3.3,如果里面有.python-version
设置为3.8,则结果是不对的。因为还是加载到3.3里面去了。
Debugger
采用最新的DAP(debug adapter protocal),和LSP类似,可以支持多种debugger的后端,然后用统一的转换协议和client进行交互,可以自由的切换后端。
打开文件
- Open (修改)
将这个替换为ctrl+x, ctrl+f
的默认命令,可以在quick_panel
里面进行模糊过滤,但不能创建新的文件,也不能进行拼音首字母的过滤。不能进行项目文件的搜索。
show_quick_panel
不能监听键盘事件,所以,不能做实时的输入判断和文件补全刷新。也不能监听enter或者tab的输入来判断是否创建文件。
import sublime
import sublime_plugin
import re
import os
from os.path import join, dirname, abspath, isdir, basename, expanduser, exists
class OpenBrowseCommand(sublime_plugin.TextCommand):
settings_file = 'Open.sublime-settings'
def show_panel(self):
func = self.open
elements = self.display
sublime.set_timeout(lambda: self.view.window().show_quick_panel(elements, func), 10)
def open(self, index):
"""
If file is a directory will list the files and directories
If file is a file will open that file
"""
if index != -1:
fname = self.items[index]
if isdir(fname):
self.display = []
self.items = []
self.list_files(fname)
self.show_panel()
elif exists(fname):
sublime.set_timeout(lambda: self.view.window().open_file(fname, sublime.ENCODED_POSITION), 0)
def run(self, cmd):
self.settings = sublime.load_settings(self.settings_file)
self.display = []
self.items = []
# List current file (tab) directory
fname = self.view.window().active_view().file_name()
if self.settings.get('list_current_dir', True) and fname is not None and exists(fname):
self.list_files(dirname(fname))
# List bookmarks
self.list_bookmarks()
self.show_panel()
def list_bookmarks(self):
bookmarks = self.settings.get('bookmarks', list())
bookmark_icon = self.settings.get('bookmark_prefix', '»')
if bookmark_icon == '%d':
self.display += ['%d: %s ' % (i, f) for i, f in enumerate(bookmarks)]
else:
self.display += [bookmark_icon + ' ' + f for f in bookmarks]
bookmarks = [abspath(expanduser(f)) for f in bookmarks]
self.items += [f for f in bookmarks]
def list_files(self, fname):
self.currentdir = fname
# Parent dir
self.display += ['..']
self.items += [abspath(join(self.currentdir, os.pardir))]
# List files and dirs
self.display += [join(f, '') if isdir(join(fname, f)) else f for f in os.listdir(fname) if self.filter_files(f)]
self.items += [join(fname, f) for f in os.listdir(fname) if self.filter_files(f)]
def filter_files(self, fname):
"""
Returns False if a file should be ignored: If the file matched any of the regular expressions
on the settings file
"""
fname = basename(fname)
for regex in self.settings.get('filter_regex', list()):
regex = regex.replace('\\\\', '\\') # Fix backslash escaping on json
p = re.compile(regex)
if p.match(fname) is not None:
return False
return True
- OpenPath
用于使用系统的文件管理器,如finder或者explorer打开文件夹,默认有当前文件夹和项目文件夹,我们有时想打开sublime的插件安装包所在的目录,也叫配置目录,那就可以模仿写一个方法,然后注册到Command Palette
中。
class OpenConfigFolder(sublime_plugin.WindowCommand):
def run(self):
if self.window.active_view() is None:
return
open_path(os.path.dirname(sublime.packages_path()))
{
"caption": "OpenPath: Open config folder",
"command": "open_config_folder"
}
-
ProjectFiles(废弃)
自带project管理,但不方便在文件间跳转、模糊搜索和跳转.ProjectFiles
已经很久没有维护了,有bug,结果经常很诡异。需要找到替代品。 -
Restart
在开发插件的过程中,有时需要重启,直接关闭再打开,会稍显麻烦。所以,一键重启就很方便了。但是这个插件下载下来之后,在Macos中并不生效。不work就改:
核心点在于pkill在sublime里面运行的时候不工作,替换为更底层的kill命令就可以,通过ps acx 配合awk把进程的pid找出来,关掉之后,再重新启动。
[ IU] Restart
restart.py
--- Installed Packages/Restart/restart.py 2022-04-30 19:24:32
+++ Packages/Restart/restart.py 2022-04-30 20:05:22
@@ -14,8 +14,8 @@
os.execl(sys.executable,' ')
elif sys.platform == 'darwin':
#Restarting ST3 on mac
- if sublime.version()[:1]=='3':
- subprocess.call("pkill subl && "+ os.path.join(os.getcwd(), 'subl'), shell=True)
+ if sublime.version()[:1] >= '3':
+ subprocess.call("kill $(ps acx | awk '/sublime_text/ {print $1}') && '/Applications/Sublime Text.app/Contents/MacOS/sublime_text'", shell=True)
else:
os.execl(os.path.join(os.getcwd(), 'subl'))
else:
自定义插件
sublime中的几个概念
- window(emacs中的frame)
对应WindowCommand,可以通过sublime.active_window()获取当前的窗口,可以通过self.window.active_view()获取到view - pane (emacs中的window)
- group
- view (emacs中的buffer)
对应TextCommand,主要的editor操作是这个这个对象里面完成的。 - sublime 和 sublime_plugin
sublime对外暴露的接口,主要是通过这两个模块来提供的。
打开交互
按键 ctrl+` ,然后可以在里面执行命令view或者window的命令: window.run_command("open_project_folder")
如果要注册command,需要创建一个 .sublime-command
的文件,并在里面添加命令:
常用插件编写命令
self.view.sel() # 获取光标的位置,因为sublime默认是有多鼠标操作的,所以说一个list,用[0]取第一个光标,每个光标包含起始位置,分别为a,b。是选择的顺序的起始位置,所以,a,b的大小是不确定的,和选取的顺序有关。是从前往后,还是从后往前
self.view.size() # 获取字符的最大数量
self.view.rowcol(self.view.size()) # 获取point的行号和列号,可以获取最大行号
self.view.substr(sublime.Region(0, view.size())) # 可以获取所有的文档内容
self.view.show_popup('hello'); # 这个和补全代码的弹出窗口一样,在鼠标指针那里弹出。
sublime.message_dialog("hello") # 弹出消息窗口
sublime.status_message('ssss') # 在左下角的status bar里面显示消息
sublime.error_message('ssss') # 弹出错误消息窗口
优化配置
MacOS显示全路径
在preference->settings里面添加:
{
"ignored_packages":
[
"Markdown",
"Vintage",
],
"font_size": 13,
"show_full_path": true,
"translate_tabs_to_spaces": true,
"auto_complete_cycle": true,
"save_on_focus_lost": true,
}
项目级别的build system
全局的python build不能添加项目的目录,可以添加项目级别的build system,这样,可以把项目的目录添加到PATHONPATH里面去。
{
"folders":
[
{
"path": "."
}
],
"build_systems": [
{
"name": "StockPython",
"cmd": ["/usr/local/bin/python3", "-u", "$file"],
"file_regex": "^[ ]*File \"(...*?)\", line ([0-9])*",
"env": {"PYTHONIOENCODING": "utf8", "PYTHONPATH": "/data/stock/"},
"selector": "source.python",
"working_dir": "$project_path"
}
]
}
在sublime中编写插件,使用LSP-pyright进行python补全
默认插件中import sublime
无法识别,可以通过讲开发环境设置为 "pyright.dev_environment": "sublime_text_38"
来引入stub,这样,import sublime
不会报错,可以看到函数的定义和源码。
{
"folders":
[
{
"path": ".",
}
],
"settings":
{
"LSP":
{
"LSP-pyright":
{
"enabled": true,
"settings": {
"pyright.dev_environment": "sublime_text_38",
}
},
},
},
}
当有些错误提示是非必要的,可以忽略时,可以在行尾添加 # type: ignore
进行忽略。