branch: elpa/racket-mode commit b8ff3bdcb1430a0c6e960e1cd25ba6dc634098de Author: Björn Lindström <b...@elektrubadur.se> Commit: Greg Hendershott <g...@greghendershott.com>
Handle command line args to racket-program/backend The documentation already had examples of configurations like (racket-add-back-end "/ssh:headless:~/gui-project/" :racket-program "xvfb-run racket") This would (accidentally?) work for remote backends, because the way the SSH command is invoked ends up applying shell word splitting on the program. However, when I wanted to use this locally something like this: (racket-add-backend "/project/" :racket-program "podman run --rm --interactive --volume=/project/:/project/:z --workdir=/project/ […] docker.io/racket/racket/8.9-full" "racket") This wouldn't work because the `executable-find' call would try to find the path to an executable with that full string as a name. This commit adds the option of defining `racket-program` (or the back-end `:racket-program` option) as a list rather than a string, in which case the first element will be used as the command to call, and the rest of the elements as command line arguments. --- doc/racket-mode.texi | 17 ++++++++++++--- racket-back-end.el | 61 ++++++++++++++++++++++++++++++---------------------- racket-custom.el | 12 +++++++++-- racket-doc.el | 14 +++++++----- racket-shell.el | 17 ++++++++++----- 5 files changed, 80 insertions(+), 41 deletions(-) diff --git a/doc/racket-mode.texi b/doc/racket-mode.texi index 108515c7d8..604518120b 100644 --- a/doc/racket-mode.texi +++ b/doc/racket-mode.texi @@ -3147,7 +3147,18 @@ Delete the ``compiled'' directories made by @ref{racket-mode-start-faster}. @node racket-program @subsection racket-program -Pathname of the Racket executable. +Pathname of the Racket executable or command line to launch it. + +@itemize +@item +If the value of this variable is a string, it will be interpreted as a +simple command without arguments. + +@item +If it is a list of strings, the first element will be taken to be the +executable, and the rest of the list command line arguments to pass to +it before any other arguments. +@end itemize Note that a back end configuration can override this with a non-nil @code{racket-program} property list value. See @@ -4195,9 +4206,9 @@ are a few examples. ;; 4. For example's sake, assume for buffers visiting ;; /ssh:headless:~/gui-project/ we want :racket-program instead - ;; to be "xvfb-run racket". + ;; to be '("xvfb-run" "racket"). (racket-add-back-end "/ssh:headless:~/gui-project/" - :racket-program "xvfb-run racket") + :racket-program '("xvfb-run" "racket")) @end lisp If you use various versions of Racket by setting PATH values via diff --git a/racket-back-end.el b/racket-back-end.el index abad379f0c..3b44c7a423 100644 --- a/racket-back-end.el +++ b/racket-back-end.el @@ -196,9 +196,9 @@ are a few examples. ;; 4. For example's sake, assume for buffers visiting ;; /ssh:headless:~/gui-project/ we want :racket-program instead - ;; to be \"xvfb-run racket\". + ;; to be \\='(\"xvfb-run\" \"racket\"). (racket-add-back-end \"/ssh:headless:~/gui-project/\" - :racket-program \"xvfb-run racket\") + :racket-program \\='(\"xvfb-run\" \"racket\")) #+END_SRC If you use various versions of Racket by setting PATH values via @@ -249,7 +249,12 @@ alongside each .envrc file: (signal 'wrong-type-argument (list type key v))))) (number-or-null-p (n) (or (not n) (numberp n)))) (check #'stringp :directory) - (check #'string-or-null-p :racket-program) + (let ((racket-program (plist-get plist :racket-program))) + (if (listp racket-program) + (dolist (s racket-program) + (unless (stringp s) + (signal 'wrong-type-argument (list #'stringp :racket-program s)))) + (check #'stringp :racket-program))) (when (file-remote-p (plist-get plist :directory)) (check #'stringp :remote-source-dir) (check #'file-name-absolute-p :remote-source-dir)) @@ -453,29 +458,33 @@ a possibly slow remote connection." (defun racket--back-end-args->command (back-end racket-command-args) "Given RACKET-COMMAND-ARGS, prepend path to racket for BACK-END." - (if (racket--back-end-local-p back-end) - (cons (let ((racket-program (or (plist-get back-end :racket-program) - racket-program))) - (or (executable-find racket-program) - (error - "Cannot executable-find Racket:\n racket-program: %S\n exec-path: %S" - racket-program - exec-path))) - racket-command-args) - (pcase-let ((`(,host ,user ,port ,_name) - (racket--file-name->host+user+port+name - (plist-get back-end :directory)))) - `("ssh" - ,@(when port - `("-p" ,(format "%s" port))) - ,(if user - (format "%s@%s" - user - host) - host) - ,(or (plist-get back-end :racket-program) - racket-program) ;can't use `executable-find' remotely - ,@racket-command-args)))) + (let* ((racket-program (or (plist-get back-end :racket-program) + racket-program)) + (command (if (stringp racket-program) + (list racket-program) + racket-program)) + (program (car command)) + (flags (cdr command))) + (if (racket--back-end-local-p back-end) + (let ((program (or (executable-find program) + (error + "Cannot executable-find Racket:\n racket-program: %S\n exec-path: %S" + program + exec-path)))) + (cons program (append flags racket-command-args))) + (pcase-let ((`(,host ,user ,port ,_name) + (racket--file-name->host+user+port+name + (plist-get back-end :directory)))) + `("ssh" + ,@(when port + `("-p" ,(format "%s" port))) + ,(if user + (format "%s@%s" + user + host) + host) + ,@command ;can't use `executable-find' remotely + ,@racket-command-args))))) ;;; File system watches diff --git a/racket-custom.el b/racket-custom.el index 1e57fcc818..f4fe09a4ba 100644 --- a/racket-custom.el +++ b/racket-custom.el @@ -40,12 +40,20 @@ (defvar racket--winp (eq 'windows-nt system-type)) (defcustom racket-program (if racket--winp "Racket.exe" "racket") - "Pathname of the Racket executable. + "Pathname of the Racket executable or command line to launch it. + +- If the value of this variable is a string, it will be interpreted as a + simple command without arguments. + +- If it is a list of strings, the first element will be taken to be the + executable, and the rest of the list command line arguments to pass to + it before any other arguments. Note that a back end configuration can override this with a non-nil `racket-program` property list value. See `racket-add-back-end'." - :type '(file :must-match t) + :type '(choice (string) + (repeat string)) :risky t) (make-obsolete-variable 'racket-command-port nil "2020-04-25") diff --git a/racket-doc.el b/racket-doc.el index 574be59136..4f066d9a23 100644 --- a/racket-doc.el +++ b/racket-doc.el @@ -60,11 +60,15 @@ Centralizes how to issue doc command and handle response correctly." (defun racket--search-doc-locally (str) (racket--doc-assert-local-back-end) - (call-process racket-program - nil ;INFILE: none - 0 ;DESTINATION: discard/don't wait - nil ;DISPLAY: none - "-l" "raco" "docs" str)) + (let ((command (if (stringp racket-program) + (list racket-program) + racket-program))) + (apply #'call-process `(,(car command) + nil ;INFILE: none + 0 ;DESTINATION: discard/don't wait + nil ;DISPLAY: none + ,@(cdr command) + "-l" "raco" "docs" ,str)))) (provide 'racket-doc) diff --git a/racket-shell.el b/racket-shell.el index 711a2323ec..016c1b30cc 100644 --- a/racket-shell.el +++ b/racket-shell.el @@ -11,6 +11,7 @@ (require 'racket-custom) (require 'racket-util) (require 'shell) +(require 'subr-x) (require 'term) (defun racket-racket () @@ -34,11 +35,17 @@ variable `racket-shell-or-terminal-function'." (defun racket--shell-or-terminal (args) (racket--save-if-changed) - (let* ((exe (shell-quote-argument - (if (file-name-absolute-p racket-program) - (expand-file-name racket-program) ;handle e.g. ~/ - racket-program))) - (cmd (concat exe " " args)) + (let* ((command (if (stringp racket-program) + (list racket-program) + racket-program)) + (program (car command)) + (exe (shell-quote-argument + (if (file-name-absolute-p program) + (expand-file-name program) ;handle e.g. ~/ + program))) + (flags (mapcar (lambda (x) (shell-quote-argument x)) + (cdr command))) + (cmd (concat exe " " (string-join flags " ") args)) (win (selected-window))) (funcall racket-shell-or-terminal-function cmd) (select-window win)))