SquircleSpace / shcl

SHell in Common Lisp
Apache License 2.0
318 stars 17 forks source link

Prompt customization and others #3

Closed ralt closed 6 years ago

ralt commented 6 years ago

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#L65

Relatedly, 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?

SquircleSpace commented 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
SquircleSpace commented 6 years ago

Alrighty. I threw something together. Is 0eb7bc314dc5727db92df4119fc46023d03eca0a what you were looking for?

ralt commented 6 years ago

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

ralt commented 6 years ago

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.

ralt commented 6 years ago

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)
SquircleSpace commented 6 years ago

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.

ralt commented 6 years ago

Do you want the PR for both the new exports and the prompt? Or just the new exports?