想法驗證:超輕量級全功能純文字介面 REPL 類語言 IDE: Vim+Tmux+Slimv

自由布魯斯發表於2016-05-30

想法驗證:超輕量級全功能純文字介面 REPL 類語言 IDE: Vim+Tmux+Slimv

前言

之前寫過一篇文章超輕量級純文字介面 REPL 類語言 IDE, 介紹了在純粹文字介面下如何用 Vim + Tmux + vim-slime 搭建一個超輕量級的 REPL 類語言開發環境, 配置步驟很簡單, 初學者使用起來也很容易上手, 不過用過幾次後發現缺憾: 這個開發環境是不是太簡陋了點? 也就是比語言自帶的 REPL 稍微多了個編輯區的功能, 尤其是使用過 Emacs + Slime 環境的朋友, 多麼希望能在 vi 下用到類似的功能啊!

好訊息是, 現在 slimv 出現了--(其實它早就出現了,只不過剛剛進入本文作者視野), 它整合了 slime 的大多數功能, 可以用於 vim 環境. 不過初步閱讀文件後發現, 幫助文件裡介紹的 Linux 環境下的例子都要用到 XWindows 下的 XTerm 或者 KDE 下的 konsole, 這有點背離我們希望使用純文字介面的初衷, 畢竟我們的目標是打造一款在流暢執行在樹莓派上的超輕量級開發環境, 並不希望啟動笨重龐大的視窗系統來浪費資源.

怎麼辦? 先進行理論分析, 看看 slimv 的原理, 它其實跟 Emaca 下的 slime 一樣, 使用 XTerm 的目的是為了啟動一個 swank 服務端, 但是 swank 服務端是不需要視窗系統的, 可以直接在純文字介面下用指令碼來啟動, 只要保持 slimvswank 版本一致, 埠號使用 slimv 預設值, 那麼我們在純文字介面下啟動的 swank 服務端照樣可以為 slimv 提供連線服務. 而且, Emacs + slime + Common Lisp 是可以執行於純文字介面的, 說明從理論上講, slimv 也應該具備這個能力.

試驗1: 純手工啟動 swank.lisp 並連線

先試驗一下, 因為 slimv 中服務端的程式是這個 ~/.vim/bundle/slimv/slime/start-swank.lisp 所以我們可以用任意一個 Common Lisp 實現來執行這個指令碼, 這裡我們先試試 CLISP, 命令為:

clisp -i ~/.vim/bundle/slimv/slime/start-swank.lisp

刷過一堆資訊, 最後顯示:

;; Swank started at port: 4005.

很好,說明我們通過命令列成功地啟動了 swank 服務端

這個服務埠可以通過 slimv 或者 telnet 登入上去, 先說說 telnet 怎麼連, 另外開一個終端視窗, 輸入

telnet 127.0.0.1 4005

就會顯示連線成功:

Air:slime admin$ telnet 127.0.0.1 4005
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.

然後第一個啟動 CLISP 的終端視窗就會出現一個 REPL 提示符 CL-USER, 變為如下介面:

;; Swank started at port: 4005.
CL-USER>

這裡其實就是經過 swank 包裝的 REPL 介面, 是可以直接輸入表示式求值的

CL-USER> (+ 12 34)
(+ 12 34)
46
CL-USER>

好玩吧, :)

好了, 言歸正傳, 繼續我們的試驗.

試驗2: 試驗在 tmux 不同的視窗啟動 swank

slimv 的文件中, 預設使用了 XTerm 來啟動 swank, 但是經過上面的理論分析和動手試驗, 我們清楚地瞭解到 swank 服務端並不需要一個圖形介面下的程式來啟動, 那麼我們希望把 swank 服務端啟動在一個 tmux 的視窗中, 這樣我們使用 vim + tmux 的組合時可以很方便地通過快捷鍵來切換, 當然, 也可以啟動在同一個 tmux 視窗的不同皮膚內, 但是鑑於我們螢幕面積有限, 而且一般情況下不需要太多關注這個 swank 服務端的狀態, 所以還是另外啟動一個新視窗好了.

這裡因為之前對 tmux 主要是使用一些快捷鍵, 具體通過指令碼如何呼叫的知識基本沒查過, 所以稍微花了點時間去研究, 最終通過網路學習到了如何在 tmux 的視窗裡啟動程式, 其實就是一句指令碼的正確格式,

tmux new-window -d -n REPL9 "clisp -i ~/.vim/bundle/slimv/slime/start-swank.lisp"

這行命令告訴 tmux 新建一個視窗(引數 new-window), 後臺執行(引數 -d), 名字叫REPL9(引數 -n REPL9), 然後在這個視窗內執行雙引號裡的命令(引數 "clisp -i ~/.vim/bundle/slimv/slime/start-swank.lisp")

很好, 在顯示一大堆資訊後 swank 服務端成功啟動了, 執行結果

;; Swank started at port: 4005.

那我們試著從 vim 中用 slimv 連線一下, 用 vim 開啟一個測試用的 test.lisp 檔案, 進入命令狀態, 按下快捷鍵 逗號 小寫英文字母c

,c

於是在 vim 的編輯視窗上方新開了一個 REPL 視窗, 顯示如下:

24 CLISP 2.49 (2010-07-07) (built on air.local [192.168.0.2])  Port: 4005  Pid: 68955
25 ; SWANK 2014-10-10
26 CL-USER> 

再去看看 swank 服務端, 顯示如下:

;; Swank started at port: 4005.
CL-USER>
;;  Loading file /Users/admin/.vim/bundle/slimv/slime/contrib/swank-asdf.lisp ...
;;   Loading file /Users/admin/.slime/fasl/2014-10-10/clisp-2.49-unix-unknown/contrib/swank-repl.fas ...
WARNING: The generic function #<STANDARD-GENERIC-FUNCTION THREAD-FOR-EVALUATION> is being modified, but has already been called.
WARNING: Replacing method #<STANDARD-METHOD (#<STRUCTURE-CLASS MULTITHREADED-CONNECTION> (EQL :FIND-EXISTING))> in
     #<STANDARD-GENERIC-FUNCTION THREAD-FOR-EVALUATION>
;;   Loaded file /Users/admin/.slime/fasl/2014-10-10/clisp-2.49-unix-unknown/contrib/swank-repl.fas

很顯然, 連線成功了! 試著在 REPL 區對錶達式求值:

注意, 因為 vim 區分命令模式和編輯模式, 而我們的 REPL 區其實就是一個 vim 編輯緩衝區(類似於 Emacs 下的 buffer 的概念), 因此, 如果我們想要在 REPL 區輸入什麼表示式, 需要先切換到插入模式(按 i ), 然後再輸入要求值的表示式,顯示如下:

26 CL-USER> (+ 123 345)
27 468
28 CL-USER>

很好, 成功了,非常漂亮, 先切換回命令狀態(快捷鍵 Esc), 再切換回程式碼編輯區(C-w w), 試試看能不能把程式碼編輯區的程式碼片段通過快捷鍵傳送到 REPL 區求值, 把游標挪到一個表示式上, 然後按下快捷鍵: 逗號 小寫英文字母e (,e), 很好, 傳送過去了,求值成功了!

 28 CL-USER> (print "hh")
 29 "hh"
 30 CL-USER>
REPL   -----------------------                                                                                                                                        
 1 (+ 1 2)
 2
 3 (format t "hello")
 4
 5 (print "hh")
 6
 7 (make-hash-table)
 8
 9 (defun hello ()
10   (print "hello,world")
11   )

上半部分是 REPL 區,下半部分是程式碼編輯區

再試試對函式定義的求值, 快捷鍵 逗號 小寫英文字母d(,d), 繼續成功,如下:

 30 CL-USER> (defun hello ()
 31   (print "hello,world")
 32   )
 33 HELLO
 34 CL-USER>
REPL   -------------------------                                                                                                                                        
 1 (+ 1 2)
 2
 3 (format t "hello")
 4
 5 (print "hh")
 6
 7 (make-hash-table)
 8 (defun hello ()
 9   (print "hello,world")
10   )

slimv 支援的 slime 函式以及預設快捷鍵

slimv 號稱是世界上對 slime 函式支援最全面的 vim 實現, 沒有之一, 那麼我們看看它究竟支援哪些 slime 的函式:

注意: slimv 預設設定 vim<leader> 鍵為逗號(,), 所以你的 .vimrc 中就不要對 <leader> 另外定義了.

---- slimv-keyboard The default keybindings (|g:slimv_keybindings|=1) and another easy to remember built-in keybinding set (|g:slimv_keybindings|=2) for Slimv are the following. Please note that the leading ',' key below refers to , which is set by Slimv to ',' by default (see |g:slimv_leader|).

In the graphical menu the currently active keyboard shortcuts are displayed beside the menu item names, so one can refer to the GUI menu as a quick reference for the keymappings.

Vim defines timeout values for mapped key sequences. If you find that Vim does not allow you enough time between pressing ',' and the last key(s) of the sequence, then you may want to fine tune these Vim options: |timeout|, |ttimeout|, |timeoutlen|, |ttimeoutlen|.

Set#1   Set#2    Command
---------------------------------------------------
,,      ,,       Slimv Menu
Edit commands (Insert mode):
<C-X>0           Close Form
<Tab>            Complete Symbol
<Space>          Function Arglist
Edit commands (Normal mode):
,)      ,tc      Close Form
,(      ,(t      Paredit Toggle
Evaluation commands:
["x],d  ["x],ed      Eval Defun (current top level form) [put in register x]
["x],e  ["x],ee      Eval Current Expression (current subform) [put in reg. x]
["x],r  ["x],er      Eval Region (visual selection) [or text from register x]
,b      ,eb      Eval Buffer
,v      ,ei      Interactive Eval (evaluates in frame when in SLDB)
,u      ,eu      Undefine Function
Debug commands:
,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
Compile commands:
,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]
Cross Reference commands
,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
Profile commands:
,p      ,pp      Toggle Profile
,B      ,pb      Profile by Substring
,U      ,pa      Unprofile All
,?      ,ps      Show Profiled
,o      ,pr      Profile Report
,x      ,px      Profile Reset
Documentation commands:
,s      ,ds      Describe Symbol
,A      ,da      Apropos
,h      ,dh      Hyperspec
,]      ,dt      Generate Tags
Repl commands:
,c      ,rc      Connect to Server
,y      ,ri      Interrupt Lisp Process
Set#1   Set#2    Command
---------------------------------------------------
,\      ,\       REPL Menu (separate menu, valid only for the REPL buffer)
REPL menu commands:
,.      ,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

Note:

Some mappings accept an optional "x prefix (where x is a register name) similarly to Vim's p (put) and y (yank) commands. These commands may additionally use the given Vim register to store or retrieve text.

Commands "Eval Defun" and "Eval Current Expression" also store the form being

evaluated in the given register. When using uppercase register name, the current form is appended to the contents of the register.

Commands "Eval Region" and "Compile Region" use the contents of the given register (instead of the selected region) for evaluation or compilation.

This feature may be used for remembering and recalling a test form used for testing parts of the code.

洋洋灑灑真是不少, 稍微試驗幾個, 發現確實支援, 簡直太好了, 看來我們想在純文字介面的 vim 下使用全功能的 slime 大有希望!

目前為止, 我們已經完美驗證了原先的想法: 在純文字介面下用 tmux 啟動 swank 服務端, 並通過 slimv 連線到 swank, 然後對新建的 REPL 區進行求值試驗, 一切都很完美, 剩下的就工作是把我們的驗證成果轉化為配置檔案, 讓它們應用起來!

明天繼續...

相關文章