Vim 下使用 Slimv(类似Slime) 轻松调试 Common Lisp

Vim 下使用 Slimv(类似Slime) 轻松调试 Common Lisp

目录

前情回顾

在上一篇文章想法验证:超轻量级全功能纯文本界面 REPL 类语言 IDE: Vim+Tmux+Slimv中我们对于在 tmux 中新建窗口运行 swank 服务端的想法经过了手工验证, 证明了我们的想法是可行的, 本文则完成实际的配置, 把所有的配置信息都写入到配置文件中.

安装slimv

如果你看过上一篇文章并且照着做了, 那你的 slimv 已经安装好了. 如果没有安装的话可以按照下面的操作来安装:

我们继续通过 pathogen 来管理 slimv 插件, 也就是说, 只要进入 .vim/bundle/ 目录下, 把 slimvgit 克隆进去, 暂时不做配置, 因为我们要手动进行试验, 安装命令如下:

cd ~/.vim/bundle/
git clone 

安装就这么简单, 如果对于 pathogen 的安装有不清楚的地方可以看看本系列第一篇文章里的描述超轻量级纯文本界面 REPL 类语言 IDE.

接下来就是数据配置了.

数据配置

Linux 和 OSX 平台下的配置

主要是对 vim 的配置, 因为我们要从vim 中启动 slimvswank, 只要在你原来的 .vimrc 配置文件中加入如下内容即可:

"Set mapleader
let mapleader = ","

" slimv for clisp
let g:slimv_swank_cmd = '! tmux new-window -d -n REPL-CLISP "clisp -i ~/.vim/bundle/slimv/slime/start-swank.lisp"'

上面的配置调用了 clisp, sbclccl 的配置分别为:

" slimv for sbcl 
let g:slimv_swank_cmd = '! tmux new-window -d -n REPL-SBCL "sbcl --load ~/.vim/bundle/slimv/slime/start-swank.lisp"'
" slimv for ccl
let g:slimv_swank_cmd = '! tmux new-window -d -n REPL-CCL "ccl -l ~/.vim/bundle/slimv/slime/start-swank.lisp"'

Win32 平台下的配置

Win32 下不使用 tmux, 直接按照官方配置文档的配置方法配置就可以了, 需要正确指定两个目录, 一个是 Common Lisp 安装目录, 一个是 slimv 安装目录

  • Common Lisp 安装目录: c:/Program Files/Lisp Cabinet/bin/ccl/
  • Slimv 安装目录: c:/Program Files/Lisp Cabinet/site/lisp/slime

配置命令如下:

let g:slimv_swank_cmd = '!start "c:/Program Files/Lisp Cabinet/bin/ccl/wx86cl.exe" -l "c:/Program Files/Lisp Cabinet/site/lisp/slime/start-swank.lisp"'

还有一个好消息就是我们对 slimv 的数据配置跟原来的 vim-slime 的配置不冲突, 所以这两个插件的功能你可以同时使用, 只要通过不同的快捷键调用即可.

默认快捷键

官网的文档里给出了如下快捷键:

----    
                                                *slimv-keyboard*
有两套快捷键可供选择, 默认快捷键绑定第一套, 设置变量为:

g:slimv_keybindings=1

第二套设置变量为: 

g:slimv_keybindings=2

注意前导键 <leader> 默认设置为逗号(,), 当然你可以改为其他键, 设置全局变量为:

g:slimv_leader

在图形界面下每个菜单项都会列出对应的快捷键, 不过我们是文本界面, 就没有这个福利了.

vim 定义了快捷键序列的超时值, 如果你觉得自己手速慢, 来不及在规定的超时时间内输完长长的快捷键序列, 那么你也可以自己设置一下相关的超时时间, 直接设置 vim 里对应的这几个参数好了:

timeout
ttimeout
timeoutlen
ttimeoutlen

下面就是具体的快捷键了, Set#1 代表第一套, Set#2 代表第二套, Command 代表该快捷键对应的命令.

    Set#1   Set#2    Command
    ---------------------------------------------------
    ,,      ,,       Slimv 菜单
    
    编辑命令 (Insert 模式):
    <C-X>0           关闭形式
    <Tab>            自动补全输入的符号
    <Space>          函数参数列表
    
    编辑命令 (Normal 模式):
    ,)      ,tc      关闭形式
    ,(      ,(t      括号自动成对开关
    
    求值命令:
["x],d  ["x],ed      求值 Defun (当前顶层) [放到寄存器 register x]
["x],e  ["x],ee      求值当前表达式 (当前子形式) [放到寄存器 reg. x]
["x],r  ["x],er      求值区域 (visual 选择) [或者来自 register x 的文本]
    ,b      ,eb      求值缓冲区内所有内容
    ,v      ,ei      交互求值 (evaluates in frame when in SLDB)
    ,u      ,eu      未定义函数求值
    
    调试命令:
    ,1      ,m1      Macroexpand-1
    ,m      ,ma      Macroexpand All
    ,t      ,dt      Toggle Trace
    ,T      ,du      Untrace All
    ,B      ,db      Set Breakpoint
    ,l      ,dd      Disassemble
    ,i      ,di      Inspect (inspects in frame when in SLDB)
    ,a      ,da      Abort
    ,q      ,dq      Quit to Toplevel
    ,n      ,dc      Continue
    ,H      ,dl      List Threads
    ,K      ,dk      Kill Thread
    ,G      ,dg      Debug Thread
    
    编译命令:
    ,D      ,cd      Compile Defun
    ,L      ,cl      Compile and Load File
    ,F      ,cf      Compile File
["x],R  ["x],cr      Compile Region [or text from register x]

    交叉引用命令:
    ,xc     ,xc      Who Calls
    ,xr     ,xr      Who References
    ,xs     ,xs      Who Sets
    ,xb     ,xb      Who Binds
    ,xm     ,xm      Who Macroexpands
    ,xp     ,xp      Who Specializes
    ,xl     ,xl      List Callers
    ,xe     ,xe      List Callees
    
    性能测量命令:
    ,p      ,pp      Toggle Profile
    ,B      ,pb      Profile by Substring
    ,U      ,pa      Unprofile All
    ,?      ,ps      Show Profiled
    ,o      ,pr      Profile Report
    ,x      ,px      Profile Reset
    
    文档命令:
    ,s      ,ds      Describe Symbol
    ,A      ,da      Apropos
    ,h      ,dh      Hyperspec
    ,]      ,dt      Generate Tags
    
    Repl 命令:
    ,c      ,rc      Connect to Server
    ,y      ,ri      Interrupt Lisp Process
    
    Set#1   Set#2    Command
    ---------------------------------------------------
    ,\      ,\       REPL 菜单 (独立菜单, 仅在 REPL 缓冲区有效)
    
    REPL 菜单命令:
    ,.      ,rs      Send Input
    ,/      ,ro      Close and Send Input
    ,g      ,rp      Set Package
    <C-C>   <C-C>    Interrupt Lisp Process
    ,<Up>   ,rp      Previous Input
    ,<Down> ,rn      Next Input
    ,-      ,-       Clear REPL

这些命令确实丰富, 基本上调试程序是够用了, 实在不够用的话也可以在 REPL 区自己手动输入相关的调试命令, 稍微麻烦一些而已.

剩下的部分我们用一个实例研演示一下这个超轻量级全功能纯文本界面的 REPL 开发环境有哪些功能, 主要参考自 slimv 的官方教程一, 二, 三:

Slimv Tutorial - Part One Slimv Tutorial - Part Two Slimv Tutorial - Part Three

实战演练

REPL 区基本操作

首先要说说 paredit 这个插件的效果, 它默认是打开的, 最简单就是自动为你补全括号, 比如你输入一个左括号 (, 它会自动补全一个右括号 ), 控制开关在这里, .vimrc 中增加:

let g:paredit_mode=0

它还有个功能叫 electric return, 当你输入回车时, 会插入新行, 控制开关如下:

let g:paredit_electric_return=0

其次是 Common Lisp 中的几个快捷键, 在 REPL 区使用:

*, **, ***:

可以得到 REPL 区上一次对象求值结果

+, ++, +++:

可以得到 REPL 区上一次求值的形式(表达式)

效果如下:

 90 CL-USER> (+ 123 345)
 91 468
 92 CL-USER> *
 93 468
 94 CL-USER> **
 95 468
 96 CL-USER> ***
 97 468
 98 CL-USER> +
 99 ***
100 CL-USER> ++
101 ***
102 CL-USER> (+ 123 345)
103 468
104 CL-USER> +
105 (+ 123 345)
106 CL-USER> ++
107 (+ 123 345)
108 CL-USER>
REPL  =============        

另外也支持历史命令查看: 可以在 Insert 模式下使用上下键

编辑源文件

开启彩虹括号

先在 ~/.vimrc 配置文件中打开 rainbow-parentheses, 用下面这条语句:

let g:lisp_rainbow=1

这样你在编辑 lisp 文件时就会发现你的不同层次的括号对会呈现不同的颜色, 同一层次的括号对会使用相同的颜色, 效果是这样:

Lisp彩虹括号示意图

开始编辑代码

vim 建立一个新文件, 命令如下:

vi ~/code-staff/morse.lisp

下面这是一个典型的显示界面, 上半部分是 REPL 区, 下半部分是编辑区:

  3 CL-USER>
~                                                                                                                                               
~                                                                                                                                               
~ 
REPL                                                                                                                                            
  1 (defpackage :morse
  2   (:use :common-lisp)
  3   )
  4 
  5 (in-package :)
~        
~
~/code-staff/morse.lisp [+]
(in-package PACKAGE-NAME)
[0] 1:bash  2:vim* 3:bash  4:bash  5:bash- 6:REPL-CLISP                                                               "Air.local" 19:45 31- 8-15

最下面一行[0] 1:bash 2:vim* 3:bash 4:bash 5:bash- 6:REPL-CLISP "Air.local" 19:45 31- 8-15tmux 的显示信息; 倒数第二行 (in-package PACKAGE-NAME) 是把光标放在编辑区的 in-package 上时 vim 给出的函数参数信息提示; 倒数第三行 ~/code-staff/morse.lisp [+]vim 状态栏显示的编辑区信息.

截图如下:

典型调试界面截图典型调试界面截图

文档命令

把光标移动到 defpackage 上, 进入命令模式:

  • 输入 ,s, 就可以查看 defpackage 的详细的信息;

  • 输入 ,h 打开默认浏览器, 查看 HyperSpec 中对 defpackage 的定义;

  • 输入 ,AREPL 区调用 (apropos "defpackage");

  • 输入 ,] 增加标签(需要事先安装好 ctags 插件)

执行 ,A 的结果:

 10 CL-USER> (apropos "defpackage")
 11 DEFPACKAGE                                 macro
 12 COMMON-LISP::DEFPACKAGE-MODERNIZE
 13 COMMON-LISP::DEFPACKAGE-RECORD-SYMNAME
 14 ; No value
 15 CL-USER>

执行 ,s 的结果:

~/code-staff/morse.lisp [+]                                                                                                                     
DEFPACKAGE is the symbol DEFPACKAGE, lies in #<PACKAGE COMMON-LISP>, is accessible in 19 packages CLOS, COMMON-LISP, COMMON-LISP-USER,
EXPORTING, EXT, POSIX, PXREF, REGEXP, SCREEN, SWANK, SWANK-LOADER, SWANK-MONITOR, SWANK-REPL, SWANK/BACKEND, SWANK/CLISP, SWANK/GRAY,
SWANK/MATCH, SWANK/RPC, SYSTEM, names a macro, has 1 property SYSTEM::DOCHTTP/1.1 404 Not FoundHTTP/1.1 200 OK
.
ANSI-CL Documentation is at
"http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/HyperSpec/Body/mac_defpackage.html"HTTP/1.1 301 Moved PermanentlyHTTP/1.1 200 OK

CLISP Documentation is at
"http://clisp.cons.org/impnotes/pack-intro.html#defpack"
For more information, evaluate (SYMBOL-PLIST 'DEFPACKAGE).

 #<PACKAGE COMMON-LISP> is the package named COMMON-LISP. It has 2 nicknames LISP, CL.
 It imports the external symbols of 1 package CLOS and exports 978 symbols to 18 packages SWANK-REPL, SWANK, SWANK/RPC, SWANK/MATCH,
 SWANK/GRAY, SWANK/CLISP, SWANK-MONITOR, PXREF, SWANK/BACKEND, SWANK-LOADER, REGEXP, POSIX, EXPORTING, SCREEN, CLOS, COMMON-LISP-USER, EXT,
 SYSTEM.

 #<MACRO #<COMPILED-FUNCTION DEFPACKAGE> (&WHOLE SYSTEM::WHOLE-FORM SYSTEM::PACKNAME &REST SYSTEM::OPTIONS)> is a macro expander.
 Argument list: (&WHOLE SYSTEM::WHOLE-FORM SYSTEM::PACKNAME &REST SYSTEM::OPTIONS)
 For more information, evaluate (DISASSEMBLE (MACRO-FUNCTION 'DEFPACKAGE)).

Documentation:
SYSTEM::IMPNOTES:
"pack-intro.html#defpack"
CLHS:
"Body/mac_defpackage.html"
SYSTEM::FILE:
((SYSTEM::DEFUN/DEFMACRO
   #P"/opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_release_tarballs_ports_lang_clisp/clisp/work/clisp-2.49/s
rc/defpackage.fas"
   11 202))
Press ENTER or type command to continue
求值命令

现在开始调试我们的小程序, 先求值第一个形式, 把光标放在 (defpackage ...) 形式体内任何地方, 然后输入 ,d, 我们的这个形式体就被发送到 REPL 并且完成求值, 这个命令会求值顶层的形式, 结果如下:

 15 CL-USER> (defpackage :morse
 16   (:use :common-lisp)
 17   )
 18 #<PACKAGE MORSE>
 19 CL-USER> 

现在再把光标移到 (in-package ...) 内并且输入 ,e, 这个命令将会在 REPL 缓冲区求值当前的 S-表达式, 结果如下:

 19 CL-USER> (in-package :morse)
 20 #<PACKAGE MORSE>
 21 CL-USER>

我们可以在 REPL 输入 (find-package :morse) 来检查是否生效, 还可以输入 ,g 把当前包设置为 :morse 并且进入该包, 这个改变会通过 REPL 提示符的变化(变为 morse)以及变量 *package* 反映出来:

 30 #<PACKAGE MORSE>
 31 CL-USER> 
 32 MORSE>*package* 
 33 #<PACKAGE MORSE>
 34 MORSE> 

现在让我们加入莫尔斯码映射函数, 在我们输入 defparameter空格 后, 函数参数列表会出现在状态栏. 这个功能对于所有用户定义的函数也有效, 不仅仅是那些内建函数.

  5 (in-package :morse)
  6 (defparameter )
~
~                                                                                                                                               
~/code-staff/morse.lisp [+]                                                                                                                     
(defparameter &WHOLE WHOLE-FORM SYMBOL INITIAL-VALUE &OPTIONAL DOCSTRING)
[0]  1:bash  2:vim* 3:bash  4:bash  5:bash- 6:REPL-CLISP 

现在开始填充莫尔斯映射表, 可以从网络上搜索到, 这里可以对代码进行自动缩进, 选择好区域后按 = (貌似这里我的表现跟教程的不太一致,先写, 后面再找原因)

  5 (in-package :morse)
  6 (defparameter *morse-mapping*
  7   '((#\A ".-")
  8     (#\B "-...")
  9     (#\C "-.-.")
 10     (#\, "--..--")
 11     (#\? "..--..")
 12     )
 13   )
 14 
 15 (defun character-to-morse (character)
 16   (assoc character *morse-mapping* :test #'char-equal)
 17   )
 18 
~      
输入单独的左括号

然后我们意识到我们只需要返回值的第二部分, 所以我们需要把 cdr 放在 (assoc ...) 前面,但是因为有 paredit 的缘故,我们无法输入一个单独的左括号 (, 因为它会自动输入成对的括号 (), 把光标移到最前面的左括号, 也就是这个 (assoc, 然后按下 ,w,W(在我们的环境下大写 W 有效, 小写无效), 它会在 S-表达式 外面用一对新括号把表达式括起来(paredit wrap), 现在我们就可以输入 cdr 了:

 13 (defun character-to-morse (character)
 14   (cdr (assoc character *morse-mapping* :test #'char-equal)))

paredit wrap 的相反操作是 splice, 通过按下 ,s, 它会删除掉最外层的括号, 还有一些类似的命令:

  • 输入 ,o 切分S-表达式 Split S-expression
  • 输入 ,J 加入S-表达式 Join S-expression
  • 输入 ,I 提升子形式 Raise subform
  • 输入 ,< 括号左移 Move left
  • 输入 ,> 括号右移 Move right
编译

现在编译我们的代码, 输入 ,D, 我们还可以编译和加载整个代码源文件:

  • ,F 编译整个文件
  • ,L 编译并且加载整个文件

结果如下:

 42 MORSE>
 43 
 44 Compilation finished. (No warnings)  [0.003131000092253089 secs]
 45 
 46 MORSE>
 47 
 48 Compilation finished. (No warnings)  [0.002271000063046813 secs]
 49 
 50 MORSE> ;; Compiling file /Users/admin/code-staff/morse.lisp ...
 51 ;; Wrote file /Users/admin/code-staff/morse.fas
 52 0 errors, 0 warnings
 53 MORSE>
 54 
 55 Compilation finished. (No warnings)  [0.03273700177669525 secs]
 56 
 57 MORSE>
 
自动补全

现在是时候测试一下我们的 character-to-morse 函数了, 输入 C-w w从代码编辑区切换到 REPL 缓冲区, 输入 char 接着按下 Tab 键, 你将会看到弹出一个可能的自动完成列表, 截图如下:

首次自动补全

如果你继续输入更多字符, 弹出菜单中的选项会自动缩小范围匹配, 截图如下:

二次自动补全

默认调用的方法是模糊补全 fuzzy completion, 因此你甚至可以输入 ctm 再按 Tab (ctm 是 character-to-morse 的首字母)

这种补全方法在 REPL 缓冲区也不受限制, 在代码编辑区它以同样的方式工作, 直到 slimv 连接到 swank 服务器上.

顺便说一句, 还有另一种 vim 的自动补全, 通过按 C-pC-n, 这种补全方式会在当前缓冲区查找相同的单词前缀, 这种方式对于补全那些不是符号名的单词比较有用, 比如注释或者字符串里的某些文本.

  • 输入 Ctrl p 向前查找
  • 输入 Ctrl n 向后查找

我们选择了正确的补全完成函数调用:

 98 MORSE> (character-to-morse #\a)
 99 (".-")
100 MORSE> (character-to-morse #\b)
101 ("-...")
102 MORSE> (character-to-morse #\c)
103 ("-.-.")
104 MORSE> 

我们得到的是一个列表, 里面包含一个字符串, 但是我们需要的结果是一个字符串, 我们意识到我们应该用 second 来代替函数中的 cdr, 因此我们相应地修改代码并且按下 ,d 重新求值这个 defun

 13 (defun character-to-morse (character)
 14   (second (assoc character *morse-mapping* :test #'char-equal)))

切换回 REPL 缓冲区, 在 Insert 模式下按下 向上 箭头来重新调用最后一条命令, 然后输入回车求值:

104 MORSE> (defun character-to-morse (character)
105   (second (assoc character *morse-mapping* :test #'char-equal)))
106 CHARACTER-TO-MORSE
107 MORSE> (character-to-morse #\c)
108 "-.-."
109 MORSE> 

好极了, character-to-morse 现在返回了作为参数输入的字符的莫尔斯代码串.

截图不完整, 后面补

转载于:https://my.oschina.net/freeblues/blog/500069

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值