aboutsummaryrefslogtreecommitdiff
path: root/.config/emacs/lisp/evil-collection/modes/eshell
diff options
context:
space:
mode:
authorJack Jamison <jackqjamison@gmail.com>2026-07-05 01:19:30 -0400
committerJack Jamison <jackqjamison@gmail.com>2026-07-05 01:19:30 -0400
commitbdf9a71ab7baa2b1de9abcfd5df1a9107a55d141 (patch)
treec4524e6c41aa5107211103401dabfaefc81aa882 /.config/emacs/lisp/evil-collection/modes/eshell
parentfe3984f541bd32bdfa418afb305b614176b55ca0 (diff)
add a bunch of emacs packages HELP
Diffstat (limited to '.config/emacs/lisp/evil-collection/modes/eshell')
-rw-r--r--.config/emacs/lisp/evil-collection/modes/eshell/README.org36
-rw-r--r--.config/emacs/lisp/evil-collection/modes/eshell/evil-collection-eshell.el168
2 files changed, 204 insertions, 0 deletions
diff --git a/.config/emacs/lisp/evil-collection/modes/eshell/README.org b/.config/emacs/lisp/evil-collection/modes/eshell/README.org
new file mode 100644
index 0000000..0d4ab5f
--- /dev/null
+++ b/.config/emacs/lisp/evil-collection/modes/eshell/README.org
@@ -0,0 +1,36 @@
+* eshell
+
+ Evil bindings for [[https://www.gnu.org/software/emacs/manual/html_node/eshell/index.html][Eshell]].
+
+** Key bindings
+
+ Normal-state bindings added to =eshell-mode-map=.
+
+ | Command | Original | Evil |
+ |---------------------------------------------------+-----------+-------------------|
+ | =eshell-previous-prompt= | =C-c C-p= | =[[=, =gk=, =C-k= |
+ | =eshell-next-prompt= | =C-c C-n= | =]]=, =gj=, =C-j= |
+ | =eshell-backward-argument= | =C-c C-b= | =M-h= |
+ | =eshell-forward-argument= | =C-c C-f= | =M-l= |
+ | =eshell-previous-matching-input-from-input= | =C-c M-p= | =C-p= |
+ | =eshell-next-matching-input-from-input= | =C-c M-n= | =C-n= |
+ | =eshell-bol= (Emacs < 30) | =C-a= | =0=, =^= |
+ | =eshell-kill-input= (when =evil-want-C-u-delete=) | =C-c C-u= | =C-u= (insert) |
+
+ =RET= sends input in the state named by =evil-collection-repl-submit-state=,
+ and inserts a newline in the complementary state.
+
+** New commands
+
+ | Command | Key | |
+ |--------------------------------------------+-----------+-----------------------------------------|
+ | =evil-collection-eshell-interrupt-process= | =C-c C-c= | interrupt process, then enter insert |
+ | =evil-collection-eshell-evil-change= | =c= | change, never touches prompt or history |
+ | =evil-collection-eshell-evil-change-line= | =C= | change to end-of-line |
+ | =evil-collection-eshell-evil-delete= | =d= | delete, never touches prompt or history |
+ | =evil-collection-eshell-evil-delete-line= | =D= | delete to end-of-line |
+
+** Notes
+
+ - On entering insert/replace state, point jumps to the next prompt.
+ - On entering normal state, point stays put (no Vim-style backward step).
diff --git a/.config/emacs/lisp/evil-collection/modes/eshell/evil-collection-eshell.el b/.config/emacs/lisp/evil-collection/modes/eshell/evil-collection-eshell.el
new file mode 100644
index 0000000..30580d2
--- /dev/null
+++ b/.config/emacs/lisp/evil-collection/modes/eshell/evil-collection-eshell.el
@@ -0,0 +1,168 @@
+;;; evil-collection-eshell.el --- Evil bindings for Eshell -*- lexical-binding: t -*-
+
+;; Copyright (C) 2017, 2021 Pierre Neidhardt
+
+;; Author: Pierre Neidhardt <mail@ambrevar.xyz>
+;; Maintainer: James Nguyen <james@jojojames.com>
+;; Pierre Neidhardt <mail@ambrevar.xyz>
+;; URL: https://github.com/emacs-evil/evil-collection
+;; Version: 0.0.1
+;; Package-Requires: ((emacs "26.3"))
+;; Keywords: evil, eshell, tools
+
+;; This file 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, or (at your
+;; option) any later version.
+;;
+;; This file 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.
+;;
+;; For a full copy of the GNU General Public License
+;; see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;; Evil bindings for Eshell.
+
+;;; Code:
+(require 'em-prompt)
+(require 'eshell)
+(require 'evil-collection)
+
+(defconst evil-collection-eshell-maps '(eshell-mode-map))
+
+(defun evil-collection-eshell-next-prompt ()
+ "`evil' wrapper around `eshell-next-prompt'."
+ (when (get-text-property (point) 'read-only)
+ ;; If at end of prompt, `eshell-next-prompt' will not move, so go backward.
+ (beginning-of-line)
+ (eshell-next-prompt 1)))
+
+(defun evil-collection-eshell-next-prompt-on-insert ()
+ "Go to next prompt on `evil' replace/insert enter."
+ (dolist (hook '(evil-replace-state-entry-hook evil-insert-state-entry-hook))
+ (add-hook hook 'evil-collection-eshell-next-prompt nil t)))
+
+(defun evil-collection-eshell-interrupt-process ()
+ "Interupt `eshell' process and enter insert state."
+ (interactive)
+ (eshell-interrupt-process)
+ (evil-insert 1))
+
+(defun evil-collection-eshell-escape-stay ()
+ "Go back to normal state but don't move cursor backwards.
+Moving cursor backwards is the default Vim behavior but it is not
+appropriate in some cases like terminals."
+ (setq-local evil-move-cursor-back nil))
+
+;; Taken from Doom Emacs
+(evil-define-operator evil-collection-eshell-evil-change (beg end type register yank-handler delete-func)
+ "Like `evil-change' but will not delete/copy the prompt."
+ (interactive "<R><x><y>")
+ (save-restriction
+ (narrow-to-region eshell-last-output-end (point-max))
+ (evil-change (max beg (point-min))
+ (if (eq type 'line) (point-max) (min (or end (point-max)) (point-max)))
+ type register yank-handler delete-func)))
+
+;; Taken from Doom Emacs
+(evil-define-operator evil-collection-eshell-evil-change-line (beg end type register yank-handler)
+ "Change to end of line."
+ :motion evil-end-of-line
+ (interactive "<R><x><y>")
+ (evil-collection-eshell-evil-change beg end type register yank-handler #'evil-delete-line))
+
+;; Taken from Doom Emacs
+(evil-define-operator evil-collection-eshell-evil-delete (beg end type register yank-handler)
+ "Like `evil-delete' but will not delete/copy the prompt."
+ (interactive "<R><x><y>")
+ (save-restriction
+ (narrow-to-region eshell-last-output-end (point-max))
+ (evil-delete (if beg (max beg (point-min)) (point-min))
+ (if (eq type 'line) (point-max) (min (or end (point-max)) (point-max)))
+ type register yank-handler)))
+
+;; Taken from Doom Emacs
+;;
+;; Although the BEG argument doesn't get used in any meaningful way,
+;; `evil-define-operator' must access it nonetheless, so putting an underscore
+;; in front will make the CI fail.
+(evil-define-operator evil-collection-eshell-evil-delete-line (beg end type register yank-handler)
+ "Change to end of line."
+ :motion nil
+ :keep-visual t
+ (interactive "<R><x>")
+ (evil-collection-eshell-evil-delete (point) end type register yank-handler))
+
+;; `eshell-mode-map' is reset when Eshell is initialized in `eshell-mode'. We
+;; need to add bindings to `eshell-first-time-mode-hook'.
+(defun evil-collection-eshell-setup-keys ()
+ "Set up `evil' bindings for `eshell'."
+ (evil-collection-define-key 'normal 'eshell-mode-map
+ ;; motion
+ "[[" 'eshell-previous-prompt
+ "]]" 'eshell-next-prompt
+ (kbd "C-k") 'eshell-previous-prompt
+ (kbd "C-j") 'eshell-next-prompt
+ "gk" 'eshell-previous-prompt
+ "gj" 'eshell-next-prompt
+ (kbd "M-h") 'eshell-backward-argument
+ (kbd "M-l") 'eshell-forward-argument
+
+ (kbd "C-n") 'eshell-next-matching-input-from-input
+ (kbd "C-p") 'eshell-previous-matching-input-from-input
+
+ (kbd "C-c C-c") 'evil-collection-eshell-interrupt-process
+ "c" 'evil-collection-eshell-evil-change
+ "C" 'evil-collection-eshell-evil-change-line
+ "d" 'evil-collection-eshell-evil-delete
+ "D" 'evil-collection-eshell-evil-delete-line)
+
+ (let* ((submit evil-collection-repl-submit-state)
+ (newline-state (if (eq submit 'normal) 'insert 'normal)))
+ (evil-collection-define-key submit 'eshell-mode-map
+ (kbd "RET") 'eshell-send-input)
+ (evil-collection-define-key newline-state 'eshell-mode-map
+ (kbd "RET") 'newline))
+
+ (when (< emacs-major-version 30)
+ (evil-collection-define-key 'normal 'eshell-mode-map
+ "0" 'eshell-bol
+ "^" 'eshell-bol))
+
+ (when evil-want-C-u-delete
+ (evil-collection-define-key 'insert 'eshell-mode-map
+ (kbd "C-u") 'eshell-kill-input))
+
+ (evil-collection-define-key 'visual 'eshell-mode-map
+ ;; motion
+ ;; TODO: This does not work with `evil-visual-line'.
+ "[[" 'eshell-previous-prompt
+ "]]" 'eshell-next-prompt
+ (kbd "C-k") 'eshell-previous-prompt
+ (kbd "C-j") 'eshell-next-prompt
+ "gk" 'eshell-previous-prompt
+ "gj" 'eshell-next-prompt)
+
+ (when (< emacs-major-version 30)
+ (evil-collection-define-key 'visual 'eshell-mode-map
+ "0" 'eshell-bol
+ "^" 'eshell-bol))
+
+ (evil-normalize-keymaps)
+ (unless evil-collection-always-run-setup-hook-after-load
+ (run-hook-with-args
+ 'evil-collection-setup-hook 'eshell evil-collection-eshell-maps)))
+
+;; TODO: Compare this setup procedure with evil-ediff.
+;;;###autoload
+(defun evil-collection-eshell-setup ()
+ "Set up `evil' bindings for `eshell'."
+ (add-hook 'eshell-mode-hook 'evil-collection-eshell-escape-stay)
+ (add-hook 'eshell-mode-hook 'evil-collection-eshell-next-prompt-on-insert)
+ (add-hook 'eshell-first-time-mode-hook 'evil-collection-eshell-setup-keys))
+
+(provide 'evil-collection-eshell)
+;;; evil-collection-eshell.el ends here