八、扩展包¶
在3.0版中,我们将一些功能移到了单独的包里。本章讲述如何加载扩展包的模块,并且描述了这些包的功能。
8.1 加载扩展包¶
默认情况下扩展包并没有被加载。你必须稍微设置一下,这样Emacs就可以知道在哪里找到这些扩展包、加载哪些扩展包。总的来说,你应该调用slime-setup函数,并将需要用的包的名字作为一个列表传给它。例如,加载slime-scratch和slime-editing-commands包的设置如下:
- (setq inferior-lisp-program "/opt/sbcl/bin/sbcl") ; your Lisp system
- (add-to-list 'load-path "~/hacking/lisp/slime/") ; your SLIME directory
- (require 'slime-autoloads)
- (slime-setup '(slime-scratch slime-editing-commands))
启动Slime后,这些扩展包的命令应该都可用了。
这里要特别提到REPL和slime-fancy扩展包。许多用户认为REPL(见 8.2 REPL)很必要,而slime-fancy(见 8.20 slime-fancy)加载了REPL包和其它几乎所有常用的包。所以,如果你不知道怎样启动,试试:
- (slime-setup '(slime-repl)) ; repl only
如果你喜欢你见到的,试试:
- (slime-setup '(slime-fancy)) ; almost everything
8.2 REPL:“顶层环境”¶
Slime使用定制的读-写-打印循环(REPL,也被称作“顶层环境”,或者是监听者)。REPL界面使用Emacs Lisp写成,因此与传统的基于comint的Lisp交互相比,它与Emacs更加契合:
- 可以通过SLDB调试REPL里表达式的状况信号
- 打印出的不同的返回值可以通过不同的Emacs faces(颜色)加以区分
Emacs通过不同的符号管理REPL提示符。这确保了Lisp的输出被插入到正确的位置,并且不会和其它的用户输入搞混。
加载REPL需要在你的.emacs文件里调用(slime-setup ‘(slime-repl))。C-c C-z 或 M-x slime-switch-to-output-buffer
选择输出缓冲区,一般是在另一个窗口里
- C-c C-y 或 M-x slime-call-defun
在REPL里调用在当前光标处定义的函数
8.2.1 REPL命令¶
- RET 或 M-x slime-repl-return
如果输入完成,则在Lisp里对当前输入求值。如果没有,则开启一个新行并缩进。如果给出了前缀参数,则不检查输入完整性而直接求值。
- C-RET 或 M-x slime-repl-closing-return
关闭所有未匹配的括号并对当前输入求值。也绑定到了M-RET。
- TAB 或 M-x slime-indent-and-complete-symbol
缩进当前行并进行符号补全。
- C-j 或 M-x slime-repl-newline-and-indent
新开一行并缩进。
- C-a 或 M-x slime-repl-bol
到行首,但是在REPL提示符之后。
- C-c C-c 或 M-x slime-interrupt
以SIGINT中断Lisp进程。
- C-c M-o 或 M-x slime-repl-clear-buffer
清空当前缓冲区,只留一个提示符。
- C-c C-o 或 M-x slime-repl-clear-output
从缓冲区里清空之前表达式的输出和结果
8.2.2 输入引导¶
输入导航(历史)命令是模仿coming-mode的。如果你习惯了类似Bash的键绑定,那么要注意了: M-p和M-n使用当前的输入作为搜索样本,并且类似Bash一样只有在当前行是空的情况下才工作。C-<up>和C-<down>像Bash里的up和down键效果一样。
- C-
或 M-x slime-repl-forward-input 和 C- 和 M-x slime-repl-backward-input
去到前一个/后一个历史输入
- M-n 或 M-x slime-repl-next-input 和 M-p 或 M-x slime-repl-previous-input
使用当前的输入作为搜索样本,在输入历史里向前/向后搜索相关输入。如果M-n/M-n被连续按两次,第二次调用会使用相同的搜索样本(即使当前输入已经改变)。
- M-s 或 M-x slime-repl-next-matching-input 和 M-r 或 M-x slime-repl-previous-matching-input
使用正则表达式在输入历史里向前/向后搜索
- C-c C-n 或 M-x slime-repl-next-prompt 和 C-c C-p 或 M-x slime-repl-previous-prompt
在REPL缓冲区的当前和前一个提示符之间移动。在有之前输入的一行里按RET会把那一行复制到最新的提示符处。
slime-repl-wrap-history变量控控制了环绕行为,即是如果到了末尾那么是否应该重新跳转到历史的最开头。
8.2.3 快捷命令¶
“快捷命令”是一组通过名称调用的REPL命令。要调用一个快捷命令,你需要先在REPL提示符后输入一个逗号,然后再输入命令的名称来执行。
快捷命令处理一些类似于切换目录和加载编译Lisp系统的事务。快捷命令在下面列出,或者你可以通过help快捷命令来交互式地列出来。
- change-directory (aka !d, cd)
改变当前目录
- change-package (aka !p, in, in-package)
改变当前包
- compile-and-load (aka cl)
编译(如果)并加载一个Lisp文件
- defparameter (aka !)
定义一个新的全局的特殊的变量
- disconnect
关闭所有连接
- help (aka ?)
显示帮助
- pop-directory (aka -d)
弹出当前目录
- pop-package (aka -p)
弹出包栈的顶端元素
- push-directory (aka +d, pushd)
将一个新的目录推到目录栈里
- push-package (aka +p)
将一个包推到包栈里
- pwd
显示当前目录
- quit
退出Lisp
- resend-form
再次发送最后的形式
- restart-inferior-lisp
重启inferior-lisp并重新连接Slime
- sayoonara
退出所有Lisp并关闭所有Slime缓冲区
8.3 多REPL¶
slime-mrepl扩展包为多监听者缓冲区提供了支持。M-x slime-open-listener命令创建一个新的缓冲区。在多线程Lisp里,每一个监听者都与一个单独的线程相连。在单线程Lisp里,创建多监听者缓冲区也是可以的,但是其命令都是在同一个进程里顺序执行的。
8.4 inferior-slime-mode¶
inferior-slime-mode是一个用来与inferior-lisp缓冲区一起使用的次模式。它提供了一些Slime命令,例如符号补全和文档查询。它也跟踪Lisp进程的当前目录。将以下代码加入.emacs配置来使用它:
- (slime-setup '(inferior-slime-mode))
- M-x inferior-slime-mode
打开或关闭inferior-slime-mode
inferior-slime-mode-map变量包含了额外的键绑定
8.5 混合补全¶
slime-c-p-c扩展包提供了不同的符号补全算法,它通过中划线分割的符号名 [1] 的单词子串来进行“并行”的补全。形式上来讲,“a-b-c”可以补全任何匹配“^a.-b.-c.*”正则表达式的符号(“圆点”匹配任何除了中划线之外的东西)。下面的例子会给你更直观的感觉:
- m-v-b补全为multiple-value-bind。
- w-open稍有歧义:它可以补全with-open-file或with-open-stream。它会扩展到最长的相同匹配(with-open-)然后光标会停留在有歧义的第一个字符处,在这里就是最后一个单词处。
- w–stream扩展为with-open-stream
slime-c-p-c-unambiguous-prefix-p变量定义了在补全符号后光标应该置于何处。例如f-o可能的补全是finish-output和force-output,默认情况下光标会移动到f后面,因为这里是明确的前缀。如果f-o are finish-output and force-output是nil,光标会到插入的文本的最后,在这里就是在o之后。
除此之外,slime-c-p-c也为字符名提供补全(对很多可以识别Unicode的Lisp实现来讲通常很有用):
- CL-USER> #\Sp<TAB>
在这里Slime会将其补全为#Space,但在一个可以识别Unicode的实现里,就可能会有以下的补全:
- Space Space
- Sparkle Spherical_Angle
- Spherical_Angle_Opening_Left Spherical_Angle_Opening_Up
slime-c-p-c扩展包也提供了对关键字的大小写敏感的补全。例如:
- CL-USER> (find 1 '(1 2 3) :s<TAB>
在这里Slime会补全为:start,而不是将所有以:s开头的关键字列出来。
- C-c C-s 或 M-x slime-complete-form
如果有的话,将当前光标处的函数的参数列表列出来并插入缓冲区。更加一般地,此命令给不完全的形式的缺失参数提供了一个模板。对于发现泛函数的额外参数,处理make-instance、defmethod和其它很多函数来说有特殊的代码,例如:
- (subseq "abc" <C-c C-s>
- --inserts--> start [end])
- (find 17 <C-c C-s>
- --inserts--> sequence :from-end from-end :test test
- :test-not test-not :start start :end end
- :key key)
- (find 17 '(17 18 19) :test #'= <C-c C-s>
- --inserts--> :from-end from-end
- :test-not test-not :start start :end end
- :key key)
- (defclass foo () ((bar :initarg :bar)))
- (defmethod print-object <C-c C-s>
- --inserts--> (object stream)
- body...)
- (defmethod initialize-instance :after ((object foo) &key blub))
- (make-instance 'foo <C-c C-s>
- --inserts--> :bar bar :blub blub initargs...)
8.6 模糊补全¶
slime-fuzzy扩展包提供了另一种符号补全方式。
[最好有人描述一下这种算法到底是做什么的]
它尝试一次性补全整个符号,而不是只补全一部分。例如,“mvb”会补全为“multiple-value-bind”,“norm-df”会补全为“least-positive-normalized-double-float”。
这种算法尝试以不同的方式扩展每一个字符,然后以下列的方式将所有可能的补全排序列出。
根据在字符串里的位置,字母会被赋予一个权值。字符串最开头,或者是前缀字母之后的字母的权值是最高的。分隔符之后的字符,例如#-,权值是次高的。字符串最后或者是后缀字母之前的字母有中等权值,其它地方的字母的权值最低。
如果一个字母在另一个匹配字母之后,它在此处的可能性就比之前字母的可能性低,所以就会使用之前的可能性。
最后,一个偏好因子会作用于一些常用的较短的匹配,其它的东西都是一样的。
- C-c M-i 或 M-x slime-fuzzy-complete-symbol
根据当前光标处的缩写列出所有可能的补全。如果你将变量slime-complete-symbol-function的值设为这个命令,则可以通过M-TAB使用模糊补全。
8.7 slimeautodocmode¶
Autodoc模式是一个用来自动显示光标附近符号的相关信息的minor-mode。对于函数名,参数列表会被显示,对于全局变量,则显示它的值。Autodoc是通过Emacs的eldoc-mode来实现的。
该模式可以通过你~/.emacs文件里的slime-setup调用来默认开启:
- (slime-setup '(slime-autodoc))
- M-x slime-arglist NAME
显示函数NAME的参数列表
- M-x slime-autodoc-mode
根据参数的值开启或关闭autodoc-mode。当没有参数时,触发该模式。
如果变量slime-use-autodoc-mode被设置(默认情况),Emacs会启动一个计时器,否则信息只会在按SPC之后显示。
8.8 ASDF¶
ASDF是一个流行的“系统构建工具”。slime-asdf扩展包提供了一些命令来从Emacs里加载和编译这些系统。ASDF本身没有被包含在Slime里,你必须自己把它加载到Lisp里。还有,你必须在连接之前加载ASDF,否则你会收到关于符号缺失的错误。
- M-x slime-load-system NAME
编译并加载ASDF系统。默认的系统名字是从当前目录下第一个符合*.asd的文件里获得的。
- M-x slime-open-system NAME &optional LOAD
打开系统里的所有文件,如果LOAD不是nil的话则加载进来。
- M-x slime-browse-system NAME
使用Dired浏览系统里的所有文件。
该扩展包也加载了一些新的REPL快捷命令(见 8.2.3 快捷命令);
- load-system
编译(根据需要)并加载一个ASDF系统
- compile-system
编译(但不加载)一个ASDF系统
- force-compile-system
重新编译(但不加载)一个ASDF系统
- force-load-system
重新编译并加载一个ASDF系统
- open-system
打开系统里的所有文件
- browse-system
使用Dired打开系统里的所有文件
8.9 导航条¶
slime-banner扩展包在当前REPL缓冲区安装一个位于窗口顶端的横条。开始的时候还会播放一段动画。
通过将slime-startup-animation设置为nil,你可以关闭动画,而slime-header-line-p可以设置横条。
8.10 编辑命令¶
slime-editing-commands扩展包提供了一些命令来编辑Lisp表达式。
- C-c M-q 或 M-x slime-reindent-defun
重新缩进当前的defun,或者重排当前段落。如果光标在一段注释里,那么光标附近的文本会被当做一个段落,然后用fill-paragraph重排。否则,它会被当做Lisp代码,当前defun会被重新缩进。如果当前defun有没匹配的括号,在重新缩进前会尝试修复。
- C-c C-] 或 M-x slime-close-all-parens-in-sexp
补全当前光标处未闭合的S表达式的括号。插入足够多的右括号,使得跟它的左括号数量匹配。删除多余的左括号,将结尾处的括号格式化为Lisp形式。
如果REGION是true,对该区域操作。否则对顶层环境光标前的表达式操作。
- M-x slime-insert-balanced-comments
在包含光标的表达式里插入对称的注释。如果该命令被重复调用(多次调用之间没有其它命令了),注释逐渐从里面的表达式向外扩展。如果调用的时候有前缀参数,S表达式的参数列表会有一个对称的注释。
M-C-a 或 M-x slime-beginning-of-defun
M-C-e 或 M-x slime-end-of-defun
8.11 更好的检查器¶
有一个默认检查器的替代物,由slime-fancy-inspector扩展包提供。该检查器更加了解CLOS对象和方法。它提供很多用来使Lisp代码检查对象的行为。例如,为了展示一个泛函数,检查器会以纯文本的形式显示其文档,而对于每个方法则会列出它的超链接和一个你可以调用的“除去该方法”行为。它的键绑定跟默认检查器是一样的。
8.12 对象描述¶
在Slime里,一个“对象描述” [2] 指的是跟一个Lisp对象有关的一块文本。右键点击文本会弹出操作该对象的一个菜单。有些操作,例如查看,对所有对象都适用,但对象也可以有自己特有的操作。例如,路径对象有Dired相关的操作。
更加重要的是,可以使用所有标准的Emacs命令来剪切和粘贴这些描述(也就是Lisp对象,而不仅仅是打印出来的样子)。通过这种方式,可以剪切和粘贴REPL里之前计算出来的结果。这对不可读对象来说十分重要。
slime-presentations扩展包在REPL里安装这种对象描述,也就是求值命令的结果会被显示出来。使用这种方法,相关描述会生成标准Common Lisp REPL历史变量的用法。例如:
- CL-USER> (find-class 'standard-class)
- #<STANDARD-CLASS STANDARD-CLASS>
- CL-USER>
在缓冲区里描述会以红色显示。使用标准的Emacs命令,描述可以被复制进REPL内的一个新的输入里:
- CL-USER> (eql '#<STANDARD-CLASS STANDARD-CLASS> '#<STANDARD-CLASS STANDARD-CLASS>)
- T
当你复制了一个不完整的描述,或者编辑描述里的文本,该描述会变为纯文本,丢失与Lisp对象之间的关联。在缓冲区里,这会通过其颜色从红色变回黑色来表示,而且不能撤销。
对象描述也可以在查看器(所有可以查看的部分都是对象描述)和调试器(所有的本地变量都是对象描述)里使用。这样就可以使用出现在调试窗口里的对象来在REPL里求值。这比使用M-x sldb-eval-in-frame更加方便。警告:从查看器和调试器而来的对象只在相关窗口打开的时候才是可用的。否则的话会引起错误或者混淆。
对于某些Lisp实现,你还可以安装slime-presentation-streams包,它让对象描述适用于standard-output流和其它流。这意味着不只是计算的结果,而是某些对象都可以通过与对象描述相关联来打印到标准输出(作为计算的副作用)。目前所有的不可读对象和路径都被作为对象描述打印出来。
- CL-USER> (describe (find-class 'standard-object))
- #<STANDARD-CLASS STANDARD-OBJECT> is an instance of
- #<STANDARD-CLASS STANDARD-CLASS>:
- The following slots have :INSTANCE allocation:
- PLIST NIL
- FLAGS 1
- DIRECT-METHODS ((#<STANDARD-METHOD
- SWANK::ALL-SLOTS-FOR-INSPECTOR
- (STANDARD-OBJECT T)>
这也使得可以复制粘贴、查看这些对象。
除了标准Emacs命令,还有一些键盘命令,一个menu-bar菜单,一个上下文菜单来操作对象描述。我们在下面解释了这些键盘命令,它们也可以通过menu-bar访问。
- C-c C-v SPC 或 M-x slime-mark-presentation
如果光标在描述内,将其移到描述的最前并标记其末尾。这样就可以复制该描述。
- C-c C-v w 或 M-x slime-copy-presentation-at-point-to-kill-ring
如果光标在描述内,将该描述复制到kill ring里。
- C-c C-v r 或 M-x slime-copy-presentation-at-point-to-repl
如果光标在描述内,将该描述复制到REPL里。
- C-c C-v d 或 M-x slime-describe-presentation-at-point
如果光标在描述内,显示相关对象的注释。
- C-c C-v i 或 M-x slime-inspect-presentation-at-point
如果光标在描述内,在Slime查看器里查看该对象。
- C-c C-v n 或 M-x slime-next-presentation
将光标移到缓冲区里的下一个描述处。
- C-c C-v p 或 M-x slime-previous-presentation
将光标移到缓冲区里的上一个描述处。
相关的操作也可以在每一个描述的上下文菜单里找到。在一个描述处单击mouse-3打开上下文菜单会,会显示可用的命令。对于某些对象,某些特别的命令也是可用的。用户可以通过给swank::menu-choices-for-presentation定义方法来定义特殊的命令。
警告:对于没有弱哈希表的Lisp实现,所有跟对象描述相关联的对象都被垃圾回收保护起来。如果你的Lisp进程因此变得太大,使用C-c C-v M-o(slime-clear-presentations)断开这些关联,这会清空REPL缓冲区,并且断开所有对象描述的关联。
警告:对象描述可能让新用户迷惑。
- CL-USER> (cons 1 2)
- (1 . 2)
- CL-USER> (eq '(1 . 2) '(1 . 2))
- T
可能有人会期望结果是nil,因为这看起来像是两个新创建的cons在相互比较,而忽视了它们的对象身份。但是在上例中,对象描述(1 . 2)是被两次复制到REPL里的,所以eq确实是作用在相同的对象上的,也就是之前输入到REPL里的cons对象。
8.13 打印窗口¶
打印窗口是一个特殊的Emacs窗口,用来代替显示区域(mini缓冲区)来显示Slime命令的信息。这是一个可选的特性。跟显示区域相比,打印窗口的优势是可以显示更多的文本,可以被滚动,而且当你按键时内容不会消失。所有可能的较长的信息都会被发送到打印窗口,例如参数列表、宏展开等等。
- M-x slime-ensure-typeout-frame
保证打印窗口存在,如果需要就新建一个。
如果打印窗口关闭那么会重新使用显示区域。
如果要在启动时自动创建一个打印窗口,需要加载slime-typeout-frame扩展包。(见 8.1 加载扩展包)
slime-typeout-frame-properties变量指定了打印窗口的长度和其它可能的特性。它的值会传给make-frame。
8.14 TRAMP¶
slime-tramp扩展包提供了一些为TRAMP进行文件名转换的函数。(见 7.1.3 设置路径名翻译)
8.15 文档链接¶
对于某些错误信息,SBCL包含了ANSI标准或者SBCL用户手册相关的参考。slime-references扩展包将这些参考变为可以点击的链接。这使得在HyperSpec里找到这些参考相关的章节更加容易。
8.16 交叉引用和类查看器¶
slime-xref-browser扩展包提供了一个基础的类查看器。
- M-x slime-browse-classes
该命令需要一个类的名字,它会显示出类的所有继承关系。
- M-x slime-browse-xrefs
该命令显示一个符号及其交叉引用,即它的调用者。以该符号为根的引用树会在之后显示出来。
8.17 高亮编辑¶
slime-highlight-edits是一个用来高亮显示Lisp源代码里被修改了的部分的minor模式。这对于快速找到那些需要重新编译(用C-c C-c)的函数十分有用。
- M-x slime-highlight-edits-mode
打开或关闭slime-highlight-edits-mode
8.18 空白缓冲区¶
由slime-scratch扩展包提供的Slime的空白缓冲区,模仿Emacs的scratch缓冲区。如果slime-scratch-file被设置,它被用来备份空白缓冲区,使其变得可持久。它跟其它的Lisp缓冲区是一样的,除了绑定到C-j的命令。
- C-j 或 M-x slime-eval-print-last-expression
对光标前的表达式求值,并将结果插入到当前缓冲区里。
- M-x slime-scratch
创建一个slime-scratch缓冲区。在此缓冲区里你可以输入Lisp表达式并用C-j来求值,类似于Emacs的scratch缓冲区。
8.19 slime**sprof¶
slime-sprof扩展包用来集成SBCL的静态分析器,sb-sprof。
slime-sprof-exclude-swank变量控制是否显示swank函数,默认值是nil。
- M-x slime-sprof-start
开始分析。
- M-x slime-sprof-stop
停止分析。
- M-x slime-sprof-browser
报告分析结果
下面的命令在slime-sprof-browser模式里定义:
- RET 或 M-x slime-sprof-browser-toggle
打开或折叠函数的详细信息(调用者、调用)
- v 或 M-x slime-sprof-browser-view-source
查看函数源码
- d 或 M-x slime-sprof-browser-disassemble-function
拆开该函数
- s 或 M-x slime-sprof-toggle-swank-exclusion
标记swank函数使其不在报告里
8.20 slime**fancy¶
slime-fancy包是一个用来加载那些最受欢迎的包的元包。
脚注
Footnotes
|[1]|这种类型的补全由Chris McConnell在completer.el里被构建。该包跟ILISP绑定在一起。
|[2]|对象描述是来自于Lisp机的特性。可以通过定义present方法来适用于不同的设备,例如将对象绘到位图显示屏或者将文本写到字符流。
原文: https://slime-user-manual-cn.readthedocs.io/en/latest/chapter-8.html