From 7af9d8d00cf4c1c9899ddbe7d1e10129d261c09e Mon Sep 17 00:00:00 2001 From: Jim Porter Date: Mon, 3 Jun 2024 22:06:49 -0700 Subject: [PATCH 2/2] Improve implementations of some Eshell output filter functions * lisp/eshell/esh-mode.el (eshell-postoutput-scroll-to-bottom): Use 'get-buffer-window-list' for simplicity. (eshell-handle-control-codes): Use 're-search-forward'; this way is much faster. * test/lisp/eshell/esh-mode-tests.el: New file. --- lisp/eshell/esh-mode.el | 68 +++++++++++++----------------- test/lisp/eshell/esh-mode-tests.el | 62 +++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 38 deletions(-) create mode 100644 test/lisp/eshell/esh-mode-tests.el diff --git a/lisp/eshell/esh-mode.el b/lisp/eshell/esh-mode.el index e6f3cb5f6ad..ec1a07b7e2f 100644 --- a/lisp/eshell/esh-mode.el +++ b/lisp/eshell/esh-mode.el @@ -765,30 +765,25 @@ eshell-postoutput-scroll-to-bottom (current (current-buffer)) (scroll eshell-scroll-to-bottom-on-output)) (unwind-protect - (walk-windows - (lambda (window) - (if (eq (window-buffer window) current) - (progn - (select-window window) - (if (and (< (point) eshell-last-output-end) - (or (eq scroll t) (eq scroll 'all) - ;; Maybe user wants point to jump to end. - (and (eq scroll 'this) - (eq selected window)) - (and (eq scroll 'others) - (not (eq selected window))) - ;; If point was at the end, keep it at end. - (>= (point) eshell-last-output-start))) - (goto-char eshell-last-output-end)) - ;; Optionally scroll so that the text - ;; ends at the bottom of the window. - (if (and eshell-scroll-show-maximum-output - (>= (point) eshell-last-output-end)) - (save-excursion - (goto-char (point-max)) - (recenter -1))) - (select-window selected)))) - nil t) + (dolist (window (get-buffer-window-list current nil t)) + (with-selected-window window + (when (and (< (point) eshell-last-output-end) + (or (eq scroll t) (eq scroll 'all) + ;; Maybe user wants point to jump to end. + (and (eq scroll 'this) + (eq selected window)) + (and (eq scroll 'others) + (not (eq selected window))) + ;; If point was at the end, keep it at end. + (>= (point) eshell-last-output-start))) + (goto-char eshell-last-output-end)) + ;; Optionally scroll so that the text ends at the bottom of + ;; the window. + (when (and eshell-scroll-show-maximum-output + (>= (point) eshell-last-output-end)) + (save-excursion + (goto-char (point-max)) + (recenter -1))))) (set-buffer current)))) (defun eshell-beginning-of-input () @@ -977,27 +972,24 @@ eshell-handle-control-codes (goto-char eshell-last-output-block-begin) (unless (eolp) (beginning-of-line)) - (while (< (point) eshell-last-output-end) - (let ((char (char-after))) + (while (re-search-forward (rx (any ?\r ?\a ?\C-h)) + eshell-last-output-end t) + (let ((char (char-before))) (cond ((eq char ?\r) - (if (< (1+ (point)) eshell-last-output-end) - (if (memq (char-after (1+ (point))) - '(?\n ?\r)) - (delete-char 1) - (let ((end (1+ (point)))) + (if (< (point) eshell-last-output-end) + (if (memq (char-after (point)) '(?\n ?\r)) + (delete-char -1) + (let ((end (point))) (beginning-of-line) (delete-region (point) end))) - (add-text-properties (point) (1+ (point)) - '(invisible t)) - (forward-char))) + (add-text-properties (1- (point)) (point) + '(invisible t)))) ((eq char ?\a) - (delete-char 1) + (delete-char -1) (beep)) ((eq char ?\C-h) - (delete-region (1- (point)) (1+ (point)))) - (t - (forward-char))))))) + (delete-region (- (point) 2) (point)))))))) (custom-add-option 'eshell-output-filter-functions 'eshell-handle-control-codes) diff --git a/test/lisp/eshell/esh-mode-tests.el b/test/lisp/eshell/esh-mode-tests.el new file mode 100644 index 00000000000..306e11ce445 --- /dev/null +++ b/test/lisp/eshell/esh-mode-tests.el @@ -0,0 +1,62 @@ +;;; esh-mode-tests.el --- esh-mode test suite -*- lexical-binding:t -*- + +;; Copyright (C) 2022-2024 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 . + +;;; Commentary: + +;; Tests for Eshell's command invocation. + +;;; Code: + +(require 'ert) +(require 'esh-mode) +(require 'eshell) + +(require 'eshell-tests-helpers + (expand-file-name "eshell-tests-helpers" + (file-name-directory (or load-file-name + default-directory)))) + +;;; Tests: + +(ert-deftest esh-mode-test/handle-control-codes/carriage-return () + "Test that Eshell handles carriage returns properly." + (with-temp-eshell + (eshell-match-command-output "(format \"hello\r\ngoodbye\")" + "\\`hello\ngoodbye\n") + (eshell-match-command-output "(format \"hello\rgoodbye\")" + "\\`goodbye\n") + (eshell-match-command-output "(format \"hello\r\")" + "\\`hello"))) + +(ert-deftest esh-mode-test/handle-control-codes/bell () + "Test that Eshell handles bells properly." + (cl-letf* ((beep-called nil) + ((symbol-function 'beep) (lambda () (setq beep-called t)))) + (with-temp-eshell + (eshell-match-command-output "(format \"hello\athere\")" + "\\`hellothere\n") + (should beep-called)))) + +(ert-deftest esh-mode-test/handle-control-codes/backspace () + "Test that Eshell handles backspaces properly." + (with-temp-eshell + (eshell-match-command-output (format "(format \"hello%c%cp\")" ?\C-h ?\C-h) + "\\`help\n"))) + +;; esh-mode-tests.el ends here -- 2.25.1