Vim 下使用 Slimv(相似Slime) 輕鬆調試 Common Lisp

Vim 下使用 Slimv(相似Slime) 輕鬆調試 Common Lisp

目錄

前情回顧

在上一篇文章想法驗證:超輕量級全功能純文本界面 REPL 類語言 IDE: Vim+Tmux+Slimv中咱們對於在 tmux 中新建窗口運行 swank 服務端的想法通過了手工驗證, 證實了咱們的想法是可行的, 本文則完成實際的配置, 把全部的配置信息都寫入到配置文件中.html

安裝slimv

若是你看過上一篇文章而且照着作了, 那你的 slimv 已經安裝好了. 若是沒有安裝的話能夠按照下面的操做來安裝:git

咱們繼續經過 pathogen 來管理 slimv 插件, 也就是說, 只要進入 .vim/bundle/ 目錄下, 把 slimvgit 克隆進去, 暫時不作配置, 由於咱們要手動進行試驗, 安裝命令以下:express

cd ~/.vim/bundle/
git clone

安裝就這麼簡單, 若是對於 pathogen 的安裝有不清楚的地方能夠看看本系列第一篇文章裏的描述超輕量級純文本界面 REPL 類語言 IDE.小程序

接下來就是數據配置了.vim

數據配置

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 的配置分別爲:sass

" 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 安裝目錄bash

  • 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 如今返回了做爲參數輸入的字符的莫爾斯代碼串.

截圖不完整, 後面補

相關文章
相關標籤/搜索