From d85c81577fde949014c2796b44ec8fad18ff7072 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sat, 16 Apr 2022 08:23:14 -0700 Subject: [PATCH v2] New electric forward slash Eshell module * lisp/eshell/em-elecslash.el: New file. * etc/NEWS: * doc/misc/eshell.texi (Electric forward slash): Document the module. (Make / electric): Retitle to "Make / more electric" and update. --- doc/misc/eshell.texi | 63 ++++++++++++++++++- etc/NEWS | 10 +++ lisp/eshell/em-elecslash.el | 120 ++++++++++++++++++++++++++++++++++++ 3 files changed, 190 insertions(+), 3 deletions(-) create mode 100644 lisp/eshell/em-elecslash.el diff --git a/doc/misc/eshell.texi b/doc/misc/eshell.texi index 411e696069..68196563b2 100644 --- a/doc/misc/eshell.texi +++ b/doc/misc/eshell.texi @@ -1560,6 +1560,7 @@ Extension modules * Key rebinding:: * Smart scrolling:: * Terminal emulation:: +* Electric forward slash:: @end menu @node Writing a module @@ -1592,6 +1593,61 @@ Terminal emulation This section is not yet written. +@node Electric forward slash +@section Electric forward slash + +To help with supplying absolute file name arguments to remote +commands, you can add the @code{eshell-elecslash} module to +@code{eshell-modules-list}. Then, typing @kbd{/} as the first +character of a command line argument will automatically insert the +Tramp prefix @file{/method:host:}. If this is not what you want +(e.g.@: because you want to refer to a local file) you can type +another @kbd{/} to undo the automatic insertion. Typing @kbd{~/} also +inserts the Tramp prefix. The automatic insertion applies only when +@code{default-directory} is remote and the command is a Lisp function. +In particular, typing arguments to external commands doesn't insert +the prefix. + +The result is that in most cases of supplying absolute file name +arguments to commands you should see the Tramp prefix inserted +automatically only when that's what you'd reasonably expect. This +frees you from having to keep track of whether commands are Lisp +functions or external when typing command line arguments. For +example, suppose you execute + +@example + cd /ssh:root@@example.com: + find /etc -name "*gnu*" +@end example + +@noindent and in reviewing the output of the command, you identify a +file @file{/etc/gnugnu} that should be moved somewhere else. So you +type + +@example + mv /etc/gnugnu /tmp +@end example + +@noindent But since @command{mv} refers to the local Lisp function +@code{eshell/mv}, not a remote shell command, to say this is to +request that the local file @file{/etc/gnugnu} be moved into the local +@file{/tmp} directory. After you add @code{eshell-elecslash} to +@code{eshell-modules-list}, then when you type the above @command{mv} +invocation you will get the following input, which is what you +intended: + +@example + mv /ssh:root@@example.com:/etc/gnugnu /ssh:root@@example.com:/tmp +@end example + +The code that determines whether or not the Tramp prefix should be +inserted uses simple heuristics. A limitation of the current +implementation is that only the status as Lisp function or external +program of the command at the very beginning of input can be +considered. Thus when chaining commands with the operators @code{&&}, +@code{||}, @code{|} and @code{;}, the electric forward slash is active +only within the first command. + @node Bugs and ideas @chapter Bugs and ideas @cindex reporting bugs and ideas @@ -1995,10 +2051,11 @@ Bugs and ideas @item The first keypress after @kbd{M-x watson} triggers @code{eshell-send-input} -@item Make @kbd{/} electric +@item Make @kbd{/} more electric -So that it automatically expands and corrects pathnames. Or make -pathname completion for Pcomplete auto-expand @samp{/u/i/std@key{TAB}} to +So that it automatically expands and corrects pathnames, beyond what +the @code{em-elecslash} module is able to do. Or make pathname +completion for Pcomplete auto-expand @samp{/u/i/std@key{TAB}} to @samp{/usr/include/std@key{TAB}}. @item Write the @command{pushd} stack to disk along with @code{last-dir-ring} diff --git a/etc/NEWS b/etc/NEWS index 3442ebd81b..c8ff155850 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1245,6 +1245,16 @@ support for pipelines which will move a lot of data. See section "Running Shell Pipelines Natively" in the Eshell manual, node "(eshell) Input/Output". ++++ +*** New module to help supplying absolute file names to remote commands. +After enabling the new module, typing a forward slash as the first +character of a command line argument will automatically insert the +Tramp prefix. The automatic insertion applies only when +'default-directory' is remote and the command is a Lisp function. +This frees you from having to keep track of whether commands are Lisp +function or external when supplying absolute file name arguments. See +"Electric forward slash" in the Eshell manual. + ** Miscellaneous +++ diff --git a/lisp/eshell/em-elecslash.el b/lisp/eshell/em-elecslash.el new file mode 100644 index 0000000000..091acb9a86 --- /dev/null +++ b/lisp/eshell/em-elecslash.el @@ -0,0 +1,120 @@ +;;; em-elecslash.el --- electric forward slashes -*- lexical-binding:t -*- + +;; Copyright (C) 2022 Free Software Foundation, Inc. + +;; Author: Sean Whitton + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Commentary: + +;; Electric forward slash in remote Eshells. + +;;; Code: + +(require 'tramp) +(require 'thingatpt) +(require 'esh-cmd) +(require 'esh-ext) +(require 'esh-mode) + +;; This makes us an option when customizing `eshell-modules-list'. +;;;###autoload +(progn +(defgroup eshell-elecslash nil + "Electric forward slash in remote Eshells. + +This module helps with supplying absolute file name arguments to +remote commands. After enabling it, typing a forward slash as +the first character of a command line argument will automatically +insert the Tramp prefix, /method:host:. The automatic insertion +applies only when `default-directory' is remote and the command +is a Lisp function. + +The result is that in most cases of supplying absolute file name +arguments to commands you should see the Tramp prefix inserted +automatically only when that's what you'd reasonably expect. +This frees you from having to keep track of whether commands are +Lisp functions or external when typing command line arguments." + :tag "Electric forward slash" + :group 'eshell-module)) + +;;; Functions: + +(defun eshell-elecslash-initialize () ;Called from `eshell-mode' via intern-soft! + "Initialize remote Eshell electric forward slash support." + (add-hook 'post-self-insert-hook + #'eshell-electric-forward-slash nil t)) + +(defun eshell-electric-forward-slash () + "Implementation of electric forward slash in remote Eshells. + +Initializing the `eshell-elecslash' module adds this function to +`post-self-insert-hook'. Typing / or ~/ as the first character +of a command line argument automatically inserts the Tramp prefix +in the case that `default-directory' is remote and the command is +a Lisp function. Typing a second forward slash undoes the +insertion." + (when (eq ?/ (char-before)) + (delete-char -1) + (let ((tilde-before (eq ?~ (char-before))) + (command (save-excursion + (eshell-bol) + (skip-syntax-forward " ") + (thing-at-point 'sexp)))) + (if (and (file-remote-p default-directory) + ;; We can't formally parse the input. But if there is + ;; one of these operators behind us, then looking at + ;; the first command would not be sensible. So be + ;; conservative: don't insert the Tramp prefix if there + ;; are any of these operators behind us. + (not (looking-back (regexp-opt '("&&" "|" ";")) + eshell-last-output-end)) + (or (= (point) eshell-last-output-end) + (and tilde-before + (= (1- (point)) eshell-last-output-end)) + (and (or tilde-before + (eq ?\s (char-syntax (char-before)))) + (or (eshell-find-alias-function command) + (and (fboundp (intern-soft command)) + (or eshell-prefer-lisp-functions + (not (eshell-search-path command)))))))) + (let ((map (make-sparse-keymap)) + (start (if tilde-before (1- (point)) (point))) + (localname + (tramp-file-name-localname + (tramp-dissect-file-name default-directory)))) + (when tilde-before (delete-char -1)) + (insert + (substring default-directory 0 + (string-search localname default-directory))) + (unless tilde-before (insert "/")) + ;; Typing a second slash undoes the insertion, for when + ;; you really do want to type a local absolute file name. + (define-key map "/" (lambda () + (interactive) + (delete-region start (point)) + (insert (if tilde-before "~/" "/")))) + (set-transient-map map)) + (insert "/"))))) + +(provide 'em-elecslash) + +;; Local Variables: +;; generated-autoload-file: "esh-groups.el" +;; End: + +;;; esh-elecslash.el ends here -- 2.30.2