ciel/repl-utils.lisp
2021-02-07 20:09:51 +01:00

95 lines
3.4 KiB
Common Lisp

(in-package :sbcli)
(defun last-nested-expr (s/sexp)
"From an input with nested parens (or none), return the most nested
function call (or the first thing at the prompt).
(hello (foo (bar:qux *zz* ?
=>
bar:qux
"
(let* ((input (str:trim s/sexp))
(last-parens-token (first (last (str:split #\( input)))))
(first (str:words last-parens-token))))
#+or(nil)
(progn
(assert (string= "baz:qux"
(last-nested-expr "(hello (foo bar (baz:qux zz ?")))
(assert (string= "baz:qux"
(last-nested-expr "(baz:qux zz ?")))
(assert (string= "qux"
(last-nested-expr "(baz (qux ?")))
(assert (string= "sym"
(last-nested-expr "sym ?"))))
;;;;
;;;; Syntax highlighting if pygments is installed.
;;;;
(defun maybe-highlight (str)
(if *syntax-highlighting*
(let ((pygmentize (or *pygmentize*
(which:which "pygmentize"))))
(when pygmentize
(with-input-from-string (s str)
(let ((proc (uiop:launch-program (alexandria:flatten
(list pygmentize *pygmentize-options*))
:input s
:output :stream)))
(read-line (uiop:process-info-output proc) nil "")))))
str))
(defun syntax-hl ()
(rl:redisplay)
(let ((res (maybe-highlight rl:*line-buffer*)))
(format t "~c[2K~c~a~a~c[~aD" #\esc #\return rl:*display-prompt* res #\esc (- rl:+end+ rl:*point*))
(when (= rl:+end+ rl:*point*)
(format t "~c[1C" #\esc))
(finish-output)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Run visual / interactive / ncurses commands in their terminal window.
;;;
;;; How to guess a program is interactive?
;;; We currently look from a hand-made list (à la Eshell).
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defparameter *visual-commands*
'(;; "emacs -nw" ;; in eshell, concept of visual-subcommands.
"vim" "vi"
"nano"
"htop" "top"
"man" "less" "more"
"screen" "tmux"
"lynx" "links" "mutt" "pine" "tin" "elm" "ncftp" "ncdu"
"ranger"
;; last but not least
"ciel-repl")
"List of visual/interactive/ncurses-based programs that will be run in their own terminal window.")
(defparameter *visual-terminal-emulator-choices*
'("terminator" "x-terminal-emulator" "xterm" "gnome-terminal"))
(defparameter *visual-terminal-switches* '("-e")
"Default options to the terminal. `-e' aka `--command'.")
(defun find-terminal ()
"Return the first terminal emulator found on the system from the `*visual-terminal-emulator-choices*' list."
(loop for program in *visual-terminal-emulator-choices*
when (which:which program)
return program))
(defun visual-command-p (text)
"The command TEXT starts by a known visual command, listed in `*visual-commands*'."
(let* ((cmd (string-left-trim "!" text)) ;; strip clesh syntax.
(first-word (first (str:words cmd))))
;; This will be smarter. https://github.com/ruricolist/cmd/issues/10
(find first-word *visual-commands* :test #'equalp)))
(defun run-visual-command (text)
"Run this text command into another terminal window."
(let ((cmd (string-left-trim "!" text)))
(uiop:launch-program `( ,(find-terminal)
;; quick way to flatten the list of switches:
,@*visual-terminal-switches*
,cmd))))