]> git.eshelyaron.com Git - emacs.git/commitdiff
Use "/local:" prefix in Eshell to run local commands when cwd is remote
authorJim Porter <jporterbugs@gmail.com>
Fri, 10 May 2024 19:22:52 +0000 (12:22 -0700)
committerEshel Yaron <me@eshelyaron.com>
Thu, 16 May 2024 08:22:35 +0000 (10:22 +0200)
* lisp/eshell/esh-ext.el (eshell-explicit-remote-commands)
(eshell-explicit-command): Update docstrings.
(eshell--local-prefix): New constant.
(eshell-handle-remote-command): Remove.
(eshell-quoted-file-command): New function...
(eshell-ext-initialize): ... add it as a hook.
(eshell-remote-command): Support running commands on localhost.
(eshell-connection-local-command): Rename from
'eshell-external-command'.
(eshell-external-command): New implementation calling
'eshell-remote-command' or 'eshell-connection-local-command' as
appropriate.

* test/lisp/eshell/esh-ext-tests.el
(esh-ext-test/explicitly-local-command): Update test.

* doc/misc/eshell.texi (Remote Access): Update documentation.

* etc/NEWS: Update announcement.

(cherry picked from commit e260bf1be7b03c21988a2090cde31970bd6bbfc9)

doc/misc/eshell.texi
etc/NEWS
lisp/eshell/esh-ext.el
test/lisp/eshell/esh-ext-tests.el

index 30c85da795bedc080493a9d2a098ca46bd7c84d7..8cb73d4077b230013054355f14fb1dfd9061f053 100644 (file)
@@ -1535,13 +1535,18 @@ slash module (@pxref{Electric forward slash}).
 When running commands, you can also make them explicitly remote by
 prefixing the command name with a remote identifier, e.g.@:
 @samp{/ssh:user@@remote:whoami}.  This runs the command @code{whoami}
-over the SSH connection for @code{user@@remote}, no matter your
-current directory.  If you want to explicitly run a @emph{local}
-command even when in a remote directory, you can prefix the command
-name with @kbd{/:}, like @samp{/:whoami}.  In either case, you can
+over the SSH connection for @code{user@@remote}, no matter your current
+directory.  If you want to explicitly run a command on your @emph{local}
+machine even when in a remote directory, you can prefix the command name
+with @kbd{/local:}, like @samp{/local:whoami}.  In either case, you can
 also specify the absolute path to the program, e.g.@:
-@samp{/ssh:user@@remote:/usr/bin/whoami}.  To disable this syntax, set
-the option @code{eshell-explicit-remote-commands} to @code{nil}.
+@samp{/ssh:user@@remote:/usr/bin/whoami}.  If you need to refer to a
+program whose file name would be interpreted as an explicitly-remote
+command, you can use @kbd{/:} to quote the name, e.g.@:
+@samp{/:/ssh:user@@remote:whoami} (@pxref{Quoted File Names,,, emacs,
+The GNU Emacs Manual}).  To disable explicity-remote commands entirely,
+you can set the option @code{eshell-explicit-remote-commands} to
+@code{nil}.
 
 @node History
 @section History
index a8ec89cf5e363b3d9d499f32f1adc0ef2b330492..c69683a3ccb32a7eb236462cf98877a74ce80026 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -903,7 +903,7 @@ By prefixing a command name in Eshell with a remote identifier, like
 "/ssh:user@remote:whoami", you can now run commands on a particular
 host no matter your current directory.  Likewise, you can run a
 command on your local system no matter your current directory via
-"/:whoami".  For more information, see the "(eshell) Remote Access"
+"/local:whoami".  For more information, see the "(eshell) Remote Access"
 node in the Eshell manual.
 
 +++
index b4fce7a82a21817ea4e38b3850e83c635f057cce..c1f2d6d14acb60ff661ccb595ab11875cdc7d8e3 100644 (file)
@@ -167,23 +167,23 @@ external version."
 (defcustom eshell-explicit-remote-commands t
   "If non-nil, support explicitly-remote commands.
 These are commands with a full remote file name, such as
-\"/ssh:host:whoami\".  If this is enabled, you can also run
-explicitly-local commands by using a quoted file name, like
-\"/:whoami\"."
+\"/ssh:host:whoami\".  If this is enabled, you can also explicitly run
+commands on your local host by using the \"/local:\" prefix, like
+\"/local:whoami\"."
   :type 'boolean
   :group 'eshell-ext)
 
 ;;; Functions:
 
+(defconst eshell--local-prefix "/local:")
+
 (defun eshell-ext-initialize ()     ;Called from `eshell-mode' via intern-soft!
   "Initialize the external command handling code."
-  (add-hook 'eshell-named-command-hook #'eshell-explicit-command nil t)
-  (when eshell-explicit-remote-commands
-    (add-hook 'eshell-named-command-hook
-              #'eshell-handle-remote-command nil t)))
+  (add-hook 'eshell-named-command-hook #'eshell-quoted-file-command nil t)
+  (add-hook 'eshell-named-command-hook #'eshell-explicit-command nil t))
 
 (defun eshell-explicit-command (command args)
-  "If a command name begins with `*', call it externally always.
+  "If a command name begins with \"*\", always call it externally.
 This bypasses all Lisp functions and aliases."
   (when (and (> (length command) 1)
             (eq (aref command 0) eshell-explicit-command-char))
@@ -194,39 +194,35 @@ This bypasses all Lisp functions and aliases."
        (error "%s: external command not found"
               (substring command 1))))))
 
-(defun eshell-handle-remote-command (command args)
-  "Handle remote (or quoted) COMMAND names, using ARGS.
-This calls the appropriate function for commands that aren't on
-the connection associated with `default-directory'.  (See
-`eshell-explicit-remote-commands'.)"
-  (if (file-name-quoted-p command)
-      (let ((default-directory (if (file-remote-p default-directory)
-                                   (expand-file-name "~")
-                                 default-directory)))
-        (eshell-external-command (file-name-unquote command) args))
-    (when (file-remote-p command)
-      (eshell-remote-command command args))))
+(defun eshell-quoted-file-command (command args)
+  "If a command name begins with \"/:\", always call it externally.
+Similar to `eshell-explicit-command', this bypasses all Lisp functions
+and aliases, but it also ignores file name handlers."
+  (when (file-name-quoted-p command)
+    (eshell-external-command (file-name-unquote command) args)))
 
 (defun eshell-remote-command (command args)
   "Insert output from a remote COMMAND, using ARGS.
-A remote command is something that executes on a different machine.
-An external command simply means external to Emacs."
+A \"remote\" command in Eshell is something that executes on a different
+machine.  If COMMAND is a remote file name, run it on the host for that
+file; if COMMAND is a local file name, run it locally."
   (let* ((cwd-connection (file-remote-p default-directory))
          (command-connection (file-remote-p command))
          (default-directory (if (equal cwd-connection command-connection)
                                 default-directory
-                              command-connection))
+                              (or command-connection (expand-file-name "~"))))
          ;; Never use the remote connection here.  We don't want to
          ;; expand the local name!  Instead, we want it as the user
          ;; typed, so that if COMMAND is "/ssh:host:cat", we just get
          ;; "cat" as the result.
-         (command-localname (file-remote-p command 'localname 'never)))
-    (unless command-connection
-      (error "%s: not a remote command" command))
+         (command-localname (or (file-remote-p command 'localname 'never)
+                                command)))
     (eshell-external-command command-localname args)))
 
-(defun eshell-external-command (command args)
-  "Insert output from an external COMMAND, using ARGS."
+(defun eshell-connection-local-command (command args)
+  "Insert output from an external COMMAND, using ARGS.
+This always runs COMMAND using the connection associated with the
+current working directory."
   (setq args (eshell-stringify-list (flatten-tree args)))
   (let ((interp (eshell-find-interpreter
                 command
@@ -243,6 +239,19 @@ An external command simply means external to Emacs."
       (eshell-gather-process-output
        (car interp) (append (cdr interp) args)))))
 
+(defun eshell-external-command (command args)
+  "Insert output from an external COMMAND, using ARGS."
+  (cond
+   ((and eshell-explicit-remote-commands
+         (file-remote-p command))
+    (eshell-remote-command command args))
+   ((and eshell-explicit-remote-commands
+         (string-prefix-p eshell--local-prefix command))
+    (eshell-remote-command
+     (substring command (length eshell--local-prefix)) args))
+   (t
+    (eshell-connection-local-command command args))))
+
 (defun eshell/addpath (&rest args)
   "Add a set of paths to PATH."
   (eshell-eval-using-options
index 8abbd74f7370710561ddb295a4afe69bd384f157..ce958d788cc2b011582b31190efdd4448390a692 100644 (file)
          ;; Check the value of $INSIDE_EMACS using `sh' in order to
          ;; delay variable expansion.
          (eshell-match-command-output
-          (format "/:%s -c 'echo $INSIDE_EMACS'" cmd)
+          (format "/local:%s -c 'echo $INSIDE_EMACS'" cmd)
           "eshell\n"))))))
 
 ;; esh-ext-tests.el ends here