Closed ralt closed 6 years ago
You caught me! I haven't really focused too much on the interactive parts of SHCL. I've been more focused on the core parts of SHCL. At any rate, yeah, loading a file seems like the right solution to me. Funcalling a prompt function also sounds right to me.
As for calling shell commands, there is a way! Check out shcl/core/lisp-interpolation
. I think you want evaluate-constant-shell-string
and (if you like reader macros) enable-reader-syntax
. For example,
(eval-when (:compile-toplevel :load-toplevel :execute)
(shcl/core/lisp-interpolation:enable-reader-syntax))
(let ((x 123))
;; These should expand to the exact same form
(shcl/core/lisp-interpolation:evaluate-constant-shell-string "echo ,(incf x)")
#$ echo ,(incf x) #$)
The ,
splice syntax for shell commands is available by default when you use either of the above methods. You can also specify that the shell command should be read with a different shell readtable if you want. Unlike normal shell variable expansion (e.g. $FOO
), the result of a lisp splice is always treated as quoted. For example, these shell commands should be equivalent.
(let ((path "some path with shell *special* $(characters)"))
#$ rm ,path #$
#$ rm "some path with shell *special* $(characters)" #$
#$ VAR=,path ; rm "$VAR" #$) ;; Need to quote $VAR so that special characters aren't expanded
Alrighty. I threw something together. Is 0eb7bc314dc5727db92df4119fc46023d03eca0a what you were looking for?
Yes! Thank you!
The filename is debatable, especially when there are standards like XDG_CONFIG_HOME
(you can use (uiop:xdg-config-home)
), but that is good enough for me :)
Cheers
FYI I exported those to make life easier:
modified shell/main.lisp
@@ -42,7 +42,7 @@
(defpackage :shcl-user
(:use :common-lisp :shcl/shell/lisp-repl)
- (:import-from :shcl/core/environment #:env)
+ (:import-from :shcl/core/environment #:env #:$pwd #:$oldpwd #:$home)
(:import-from :shcl/core/command #:define-builtin)
(:import-from :shcl/shell/main
#:default-prompt #:*prompt-function* #:*fresh-prompt*))
I guess at least package aliases would make life easier.
Oh, and if you're interested:
(defun my-prompt ()
(if *fresh-prompt*
(format nil "~a@~a:~a$ "
(uiop:getenv "USER")
(uiop:getenv "HOSTNAME")
(if (and (>= (length $pwd) (length $home))
(string= (subseq $pwd 0 (length $home)) $home))
(format nil "~~~a" (subseq $pwd (length $home)))
$pwd))
"> "))
(setf *prompt-function* 'my-prompt)
That diff for shcl-user looks good to me. The prompt looks nice, too. Wanna make a pull request?
By the way, you probably want to use shcl/core/environment:env
rather than uiop:getenv
. SHCL doesn't actually store its shell environment variables in the process environment. env
will check SHCL's environment before falling back to the actual process environment.
Do you want the PR for both the new exports and the prompt? Or just the new exports?
Hello,
Instead of
shcl>
, it'd be nice to have something else, like current directory or w/e. Looking at the source, it seems to be hardcoded right now: https://github.com/bradleyjensen/shcl/blob/master/shell/main.lisp#L65Relatedly, I haven't seen anywhere where it loads a user-provided file to execute it (e.g.
LOAD
it).I guess the 2 birds could be killed with one stone; make the prompt funcall a function that can be provided by the user in an RC file, and that should be good. (Related: do you have an easy way to call a shell command from Lisp? That could be used in the RC file.)
What do you think?