From: Jim Porter <jporterbugs@gmail.com>
To: Eli Zaretskii <eliz@gnu.org>
Cc: 60666@debbugs.gnu.org
Subject: bug#60666: 30.0.50; [PATCH] Use field properties in Eshell buffers
Date: Mon, 9 Jan 2023 09:52:53 -0800 [thread overview]
Message-ID: <537ece85-8808-c7fa-71b0-c355ff3740bb@gmail.com> (raw)
In-Reply-To: <83cz7nq588.fsf@gnu.org>
[-- Attachment #1: Type: text/plain, Size: 759 bytes --]
On 1/9/2023 4:09 AM, Eli Zaretskii wrote:
> The only thought I have is "what if anyone _does_ like Home to do what
> it does now?" So this change should have a NEWS ebtry about the
> incompatible changes, and a recipe for how to get back the old
> behavior for those who want it.
Thanks, that makes sense. I've added a NEWS entry to the first part (see
attached). The recipe seems like it's on the edge of what would be
reasonable for users to paste into their configs though. I don't have a
strong opinion here, but if you think the recipe is too long, maybe we
could add the 'move-beginning-of-line-ignoring-fields' function to
simple.el. It might possibly be useful for some small number of people
who want to selectively disable field text motion.
[-- Attachment #2: 0001-Set-the-field-property-for-Eshell-output.patch --]
[-- Type: text/plain, Size: 12960 bytes --]
From d398a2ab7d031b18a633411311bb11323cdcb2c1 Mon Sep 17 00:00:00 2001
From: Jim Porter <jporterbugs@gmail.com>
Date: Sun, 8 Jan 2023 13:00:47 -0800
Subject: [PATCH 1/3] Set the 'field' property for Eshell output
This makes Eshell work more like 'M-x shell', and lets the <home> key
move to the beginning of the user's input at the prompt (bug#60666).
* lisp/eshell/em-prompt.el (eshell-emit-prompt): Add 'field' property
to prompt.
* lisp/eshell/esh-io.el: Declare 'eshell-interactive-print'...
(eshell-output-object-to-target): ... use it.
* lisp/eshell/esh-mode.el (eshell-output-filter-functions): Update
docstring.
(eshell-interactive-print): Set the output to have a field value of
'command-output'.
(eshell-output-filter): Rename to...
(eshell-interactive-filter): ... this, and take a buffer instead of a
process.
* lisp/eshell/esh-proc.el (eshell-interactive-process-filter): New
function, adapted from 'eshell-output-filter'...
(eshell-gather-process-output): ... use it.
* test/lisp/eshell/em-prompt-tests.el: New file.
* etc/NEWS: Announce this change.
---
etc/NEWS | 15 ++++++
lisp/eshell/em-prompt.el | 21 +++++---
lisp/eshell/esh-io.el | 6 +--
lisp/eshell/esh-mode.el | 25 +++++----
lisp/eshell/esh-proc.el | 22 ++++++--
test/lisp/eshell/em-prompt-tests.el | 81 +++++++++++++++++++++++++++++
6 files changed, 146 insertions(+), 24 deletions(-)
create mode 100644 test/lisp/eshell/em-prompt-tests.el
diff --git a/etc/NEWS b/etc/NEWS
index 690e9c3faa9..ecb4d520896 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -100,6 +100,21 @@ of arguments into a command, such as when defining aliases. For more
information, see the "(eshell) Dollars Expansion" node in the Eshell
manual.
+---
+*** Eshell now uses 'field' properties in its output.
+In particular, this means that pressing the <home> key moves the point
+to the beginning of your input, not the beginning of the whole line.
+If you want to go back to the old behavior, add something like this to
+your configuration:
+
+ (defun move-beginning-of-line-ignoring-fields (arg)
+ (interactive "^p")
+ (let ((inhibit-field-text-motion t))
+ (move-beginning-of-line arg)))
+
+ (keymap-set eshell-mode-map "<home>"
+ #'move-beginning-of-line-ignoring-fields)
+
+++
*** 'eshell-read-aliases-list' is now an interactive command.
After manually editing 'eshell-aliases-file', you can use this command
diff --git a/lisp/eshell/em-prompt.el b/lisp/eshell/em-prompt.el
index 575b5a595f1..866a21540d1 100644
--- a/lisp/eshell/em-prompt.el
+++ b/lisp/eshell/em-prompt.el
@@ -134,14 +134,19 @@ eshell-emit-prompt
(if (not eshell-prompt-function)
(set-marker eshell-last-output-end (point))
(let ((prompt (funcall eshell-prompt-function)))
- (and eshell-highlight-prompt
- (add-text-properties 0 (length prompt)
- '(read-only t
- font-lock-face eshell-prompt
- front-sticky (font-lock-face read-only)
- rear-nonsticky (font-lock-face read-only))
- prompt))
- (eshell-interactive-print prompt)))
+ (add-text-properties
+ 0 (length prompt)
+ (if eshell-highlight-prompt
+ '( read-only t
+ field prompt
+ font-lock-face eshell-prompt
+ front-sticky (read-only field font-lock-face)
+ rear-nonsticky (read-only field font-lock-face))
+ '( field prompt
+ front-sticky (field)
+ rear-nonsticky (field)))
+ prompt)
+ (eshell-interactive-filter nil prompt)))
(run-hooks 'eshell-after-prompt-hook))
(defun eshell-backward-matching-input (regexp arg)
diff --git a/lisp/eshell/esh-io.el b/lisp/eshell/esh-io.el
index 4dad4c7429a..cccdb49ce2a 100644
--- a/lisp/eshell/esh-io.el
+++ b/lisp/eshell/esh-io.el
@@ -74,6 +74,8 @@
(eval-when-compile
(require 'cl-lib))
+(declare-function eshell-interactive-print "esh-mode" (string))
+
(defgroup eshell-io nil
"Eshell's I/O management code provides a scheme for treating many
different kinds of objects -- symbols, files, buffers, etc. -- as
@@ -597,8 +599,6 @@ eshell-printn
(eshell-print object)
(eshell-print "\n"))
-(autoload 'eshell-output-filter "esh-mode")
-
(defun eshell-output-object-to-target (object target)
"Insert OBJECT into TARGET.
Returns what was actually sent, or nil if nothing was sent."
@@ -608,7 +608,7 @@ eshell-output-object-to-target
((symbolp target)
(if (eq target t) ; means "print to display"
- (eshell-output-filter nil (eshell-stringify object))
+ (eshell-interactive-print (eshell-stringify object))
(if (not (symbol-value target))
(set target object)
(setq object (eshell-stringify object))
diff --git a/lisp/eshell/esh-mode.el b/lisp/eshell/esh-mode.el
index d80f1d1f390..97edc826c9a 100644
--- a/lisp/eshell/esh-mode.el
+++ b/lisp/eshell/esh-mode.el
@@ -155,7 +155,8 @@ eshell-output-filter-functions
eshell-watch-for-password-prompt)
"Functions to call before output is displayed.
These functions are only called for output that is displayed
-interactively, and not for output which is redirected."
+interactively (see `eshell-interactive-filter'), and not for
+output which is redirected."
:type 'hook)
(defcustom eshell-preoutput-filter-functions nil
@@ -525,9 +526,13 @@ eshell-goto-input-start
(custom-add-option 'eshell-pre-command-hook #'eshell-goto-input-start)
-(defsubst eshell-interactive-print (string)
+(defun eshell-interactive-print (string)
"Print STRING to the eshell display buffer."
- (eshell-output-filter nil string))
+ (when string
+ (add-text-properties 0 (length string)
+ '(field command-output rear-nonsticky (field))
+ string)
+ (eshell-interactive-filter nil string)))
(defsubst eshell-begin-on-new-line ()
"This function outputs a newline if not at beginning of line."
@@ -687,14 +692,14 @@ eshell-kill-new
(custom-add-option 'eshell-input-filter-functions 'eshell-kill-new)
-(defun eshell-output-filter (process string)
- "Send the output from PROCESS (STRING) to the interactive display.
+(defun eshell-interactive-filter (buffer string)
+ "Send output (STRING) to the interactive display, using BUFFER.
This is done after all necessary filtering has been done."
- (let ((oprocbuf (if process (process-buffer process)
- (current-buffer)))
- (inhibit-modification-hooks t))
- (when (and string oprocbuf (buffer-name oprocbuf))
- (with-current-buffer oprocbuf
+ (unless buffer
+ (setq buffer (current-buffer)))
+ (when (and string (buffer-live-p buffer))
+ (let ((inhibit-modification-hooks t))
+ (with-current-buffer buffer
(let ((functions eshell-preoutput-filter-functions))
(while (and functions string)
(setq string (funcall (car functions) string))
diff --git a/lisp/eshell/esh-proc.el b/lisp/eshell/esh-proc.el
index 8a803c67e46..9bae812c922 100644
--- a/lisp/eshell/esh-proc.el
+++ b/lisp/eshell/esh-proc.el
@@ -304,7 +304,7 @@ eshell-gather-process-output
:name (concat (file-name-nondirectory command) "-stderr")
:buffer (current-buffer)
:filter (if (eshell-interactive-output-p eshell-error-handle)
- #'eshell-output-filter
+ #'eshell-interactive-process-filter
#'eshell-insertion-filter)
:sentinel #'eshell-sentinel))
(eshell-record-process-properties stderr-proc eshell-error-handle))
@@ -320,7 +320,7 @@ eshell-gather-process-output
:buffer (current-buffer)
:command (cons command args)
:filter (if (eshell-interactive-output-p)
- #'eshell-output-filter
+ #'eshell-interactive-process-filter
#'eshell-insertion-filter)
:sentinel #'eshell-sentinel
:connection-type conn-type
@@ -381,7 +381,7 @@ eshell-gather-process-output
line (buffer-substring-no-properties lbeg lend))
(set-buffer oldbuf)
(if interact-p
- (eshell-output-filter nil line)
+ (eshell-interactive-process-filter nil line)
(eshell-output-object line))
(setq lbeg lend)
(set-buffer proc-buf))
@@ -402,6 +402,22 @@ eshell-gather-process-output
(setq proc t))))
proc))
+(defun eshell-interactive-process-filter (process string)
+ "Send the output from PROCESS (STRING) to the interactive display.
+This is done after all necessary filtering has been done."
+ (when string
+ (add-text-properties 0 (length string)
+ '(field command-output rear-nonsticky (field))
+ string)
+ (require 'esh-mode)
+ (declare-function eshell-interactive-filter "esh-mode" (buffer string))
+ (eshell-interactive-filter (if process (process-buffer process)
+ (current-buffer))
+ string)))
+
+(define-obsolete-function-alias 'eshell-output-filter
+ #'eshell-interactive-process-filter "30.1")
+
(defun eshell-insertion-filter (proc string)
"Insert a string into the eshell buffer, or a process/file/buffer.
PROC is the process for which we're inserting output. STRING is the
diff --git a/test/lisp/eshell/em-prompt-tests.el b/test/lisp/eshell/em-prompt-tests.el
new file mode 100644
index 00000000000..b67c74e86d0
--- /dev/null
+++ b/test/lisp/eshell/em-prompt-tests.el
@@ -0,0 +1,81 @@
+;;; em-prompt-tests.el --- em-prompt test suite -*- lexical-binding:t -*-
+
+;; Copyright (C) 2022-2023 Free Software Foundation, Inc.
+
+;; 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 <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Tests for Eshell's prompt support.
+
+;;; Code:
+
+(require 'ert)
+(require 'eshell)
+(require 'em-prompt)
+
+(require 'eshell-tests-helpers
+ (expand-file-name "eshell-tests-helpers"
+ (file-name-directory (or load-file-name
+ default-directory))))
+
+;;; Tests:
+
+(ert-deftest em-prompt-test/field-properties ()
+ "Check that field properties are properly set on Eshell output/prompts."
+ (with-temp-eshell
+ (eshell-insert-command "echo hello")
+ (let ((last-prompt (field-string (1- eshell-last-input-start)))
+ (last-input (field-string (1+ eshell-last-input-start)))
+ (last-output (field-string (1+ eshell-last-input-end))))
+ (should (equal-including-properties
+ last-prompt
+ (propertize
+ (format "%s $ " (directory-file-name default-directory))
+ 'read-only t
+ 'field 'prompt
+ 'font-lock-face 'eshell-prompt
+ 'front-sticky '(read-only field font-lock-face)
+ 'rear-nonsticky '(read-only field font-lock-face))))
+ (should (equal last-input "echo hello\n"))
+ (should (equal-including-properties
+ last-output
+ (propertize "hello\n" 'rear-nonsticky '(field)
+ 'field 'command-output))))))
+
+(ert-deftest em-prompt-test/field-properties/no-highlight ()
+ "Check that field properties are properly set on Eshell output/prompts.
+This tests the case when `eshell-highlight-prompt' is nil."
+ (let ((eshell-highlight-prompt nil))
+ (with-temp-eshell
+ (eshell-insert-command "echo hello")
+ (let ((last-prompt (field-string (1- eshell-last-input-start)))
+ (last-input (field-string (1+ eshell-last-input-start)))
+ (last-output (field-string (1+ eshell-last-input-end))))
+ (should (equal-including-properties
+ last-prompt
+ (propertize
+ (format "%s $ " (directory-file-name default-directory))
+ 'field 'prompt
+ 'front-sticky '(field)
+ 'rear-nonsticky '(field))))
+ (should (equal last-input "echo hello\n"))
+ (should (equal-including-properties
+ last-output
+ (propertize "hello\n" 'rear-nonsticky '(field)
+ 'field 'command-output)))))))
+
+;;; em-prompt-tests.el ends here
--
2.25.1
next prev parent reply other threads:[~2023-01-09 17:52 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-01-08 23:34 bug#60666: 30.0.50; [PATCH] Use field properties in Eshell buffers Jim Porter
2023-01-09 12:09 ` Eli Zaretskii
2023-01-09 17:52 ` Jim Porter [this message]
2023-01-10 12:13 ` Eli Zaretskii
2023-01-10 19:28 ` Jim Porter
2023-01-10 20:11 ` Eli Zaretskii
2023-01-10 20:51 ` Jim Porter
2023-01-11 12:15 ` Eli Zaretskii
2023-01-12 18:10 ` Jim Porter
2023-01-13 13:07 ` Eli Zaretskii
2023-01-14 19:30 ` Jim Porter
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=537ece85-8808-c7fa-71b0-c355ff3740bb@gmail.com \
--to=jporterbugs@gmail.com \
--cc=60666@debbugs.gnu.org \
--cc=eliz@gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).