summaryrefslogtreecommitdiff
path: root/.emacs.d
diff options
context:
space:
mode:
authorSean Whitton <spwhitton@spwhitton.name>2022-05-10 08:40:22 -0700
committerSean Whitton <spwhitton@spwhitton.name>2022-05-10 17:52:16 -0700
commitd4117a2d6f0f35010f7e9da95fc726dbac75b334 (patch)
tree778fe08f3dc2456a440c60f3ccd1aa459c2cd425 /.emacs.d
parent488ebcbc426a9feb3198b85ac3facf90bf15f614 (diff)
downloaddotfiles-d4117a2d6f0f35010f7e9da95fc726dbac75b334.tar.gz
rework spw/eshell-jump, esp. wrt C-x p e, and restore prev-buffers
Diffstat (limited to '.emacs.d')
-rw-r--r--.emacs.d/init.el217
1 files changed, 111 insertions, 106 deletions
diff --git a/.emacs.d/init.el b/.emacs.d/init.el
index e00000b4..31920916 100644
--- a/.emacs.d/init.el
+++ b/.emacs.d/init.el
@@ -1056,18 +1056,6 @@ don't consider windows satisfying the predicate EXCLUDE."
(if (> count 0) (tab-bar-history-forward) (tab-bar-history-back))))
:cycle-backwards-key [left] :cycle-forwards-key [right])
-(with-eval-after-load 'project
- (when (boundp 'project-prefix-map) ; for Emacs 27
- (transient-cycles-define-commands ()
- (("e" . project-eshell))
- (transient-cycles-buffer-ring-cycler)
- ;; Bind into project-prefix-map, rather than just a remap, so that works
- ;; under C-x 4 p too.
- :keymap project-prefix-map)
- ;; Update project-switch-commands accordingly.
- (when-let ((cell (assoc 'project-eshell project-switch-commands)))
- (setcar cell #'transient-cycles-cmd-project-eshell))))
-
;; For when the buffer's name isn't much help for switching to it, as is often
;; the case with notmuch buffers. Commented out for now because the transient
;; cycling appended to C-x b and C-x 4 C-o now includes buffers of the same
@@ -1431,36 +1419,45 @@ don't consider windows satisfying the predicate EXCLUDE."
;;; getting to Eshell buffers
-(defun spw/eshell-jump (arg &optional chdir)
- "Pop to *eshell*, and offer cycling among other Eshells, unless
-one of the following special circumstances applies:
+(defun spw/eshell-jump (&optional arg chdir)
+ "Pop to the most recently used Eshell not already running a
+command, and offer transient cycling among other Eshells, unless
+one or more of the following apply:
-- If a command is running in *eshell*, rename that buffer out of
- the way and start a new one. Similarly if the buffer is
- narrowed; that was probably done with C-u C-c C-r, and the
- buffer is similarly in use.
+- If a command is running in all Eshells, start a new one.
+ Similarly if all buffers are narrowed; that was probably done
+ with C-u C-c C-r, and so such buffers are probably in use.
-- If CHDIR, and there is no Eshell in `default-directory', also
- change the directory of *eshell* to `default-directory'.
+- If CHDIR, and there is no Eshell in `default-directory' nor any
+ Eshell under the current project root, start a new Eshell in
+ `default-directory'
-- If CHDIR and there is an Eshell in `default-directory',
- including one generated by `project-eshell', switch to that
- Eshell instead of *eshell*.
+- If CHDIR and there is an Eshell in `default-directory', switch
+ to that Eshell instead.
+
+- If CHDIR and there is an Eshell under the current project root,
+ switch to that Eshell instead, and change its directory to
+ `default-directory'.
- If both ARG and CHDIR, or if CHDIR and the current buffer is an
- Eshell buffer which is not running a command, unconditionally
- start a new Eshell in `default-directory'.
+ Eshell buffer, unconditionally start a new Eshell in
+ `default-directory'.
+
+ (I.e. C-u may be used to override reusing an existing Eshell,
+ and separately, if we are already in the buffer that the
+ command would have taken us to, assume we want a fresh one.)
-- If not CHDIR and the current buffer is *eshell*, activate
- transient cycling to make it easy to get back to another
- Eshell. (This is the only case in which we do not use
- `pop-to-buffer' or equivalent, so C-x 4 4 must be used to cycle
- in another window.)
+- If not CHDIR and the current buffer is an Eshell that's not
+ running a command, activate transient cycling to make it easy
+ to get back to another Eshell. (This is the only case in which
+ we do not use `pop-to-buffer' or equivalent, so C-x 4 4 must be
+ used to cycle in another window.)
-For the purpose of cycling, Eshells generated by `project-eshell'
-are sorted below Eshells generated by this function.
+- If CHDIR is `project', as above except that use the root of the
+ current project instead of `default-directory', and select an
+ Eshell already in the root of the project even if it's busy.
-The ideas behind this behaviour are as follows.
+The ideas behind these behaviours are as follows.
- Just like Lisp REPLs, we do not normally need a lot of
different Eshells; it is fine for shell history associated with
@@ -1474,83 +1471,73 @@ The ideas behind this behaviour are as follows.
you to the unnumbered buffer, possibly renaming the numbered one
out of the way.
- This is why we don't try to reuse Eshells especially
- aggressively; for example, we could find an *eshell*<N> not
- running a command a rename it to *eshell*, but we don't.
-
-- Don't pay attention to the current project, as an old version
- of this code did, because if we're using C-c e e and/or C-c e h
- rather than C-x p e, we are probably working in a
- project-agnostic way.
-
- Thus, among the `project-eshell' Eshells available to cycle
- through, don't prioritise those of the current project (for
- example by moving them to the front) -- if we explicitly want
- those, can use C-x p e.
-
-- Treat C-x p e as the primary way to get to Eshells in project
- roots, and avoid changing the directories of those Eshells, as
- it is surprising when C-x p e doesn't take us to an Eshell
- which is ready to run commands in the project root (another
- possibility would be to change the dir back to the project root
- in this case).
-
-- It is assumed we'll sometimes use C-x 4 1 in front of this
- command, and if we're already in Eshell, we might use C-x 4 4
- to start the cycling in another window."
+ We do nevertheless reuse Eshells, not for the sake of creating
+ fewer, but just so that this command can be used to get back to
+ the most recent few Eshells you were working in, to see output.
+
+- We'll sometimes use C-x 4 1 in front of this command, and if
+ we're already in Eshell, we might use C-x 4 4 to start the
+ cycling in another window.
+
+- It's not especially convenient to distinguish between
+ `project-eshell' and `eshell' Eshells. We just want a way to
+ quickly obtain an Eshell in the project root, and bind that to
+ C-x p e."
(interactive "P")
(require 'eshell)
- (cl-flet ((project-eshell-eshell-p ()
- (string-match "\\`\\*.+-eshell\\*[><0-9]*\\'" (buffer-name)))
- (busy-p (buffer)
- (or (get-buffer-process buffer)
- (with-current-buffer buffer (buffer-narrowed-p))))
- (fresh-eshell ()
- (when-let ((buffer (get-buffer eshell-buffer-name)))
- (with-current-buffer buffer (rename-uniquely)))
- (let ((default-directory
- (if chdir default-directory (expand-file-name "~"))))
- (eshell))))
- (let ((current-eshell (and (eq major-mode 'eshell-mode)
- (not (project-eshell-eshell-p))
- (not (busy-p (current-buffer)))
- (current-buffer)))
- (initial-default-directory default-directory)
- default-directory-eshell
- project-eshells
- other-eshells)
- ;; Populate our two lists of all Eshells.
+ (let* ((current-project (and (not (file-remote-p default-directory))
+ (project-current)))
+ (project-root (and current-project (project-root current-project)))
+ (target-directory (expand-file-name
+ (or (and (eq chdir 'project) project-root)
+ default-directory)))
+ target-directory-eshell same-project-eshell all-eshells)
+ (cl-flet ((busy-p (buffer)
+ (or (get-buffer-process buffer)
+ (with-current-buffer buffer (buffer-narrowed-p))))
+ (fresh-eshell ()
+ (when-let ((buffer (get-buffer eshell-buffer-name)))
+ (with-current-buffer buffer (rename-uniquely)))
+ (let ((default-directory
+ (if chdir target-directory (expand-file-name "~"))))
+ (eshell))))
(dolist (buffer (buffer-list))
(with-current-buffer buffer
(when (eq major-mode 'eshell-mode)
- (if (and chdir (not default-directory-eshell)
- (string= default-directory initial-default-directory)
- (not (busy-p buffer)))
- (setq default-directory-eshell buffer)
- (push buffer
- (if (project-eshell-eshell-p)
- project-eshells other-eshells))))))
- ;; Now `pop-to-buffer' if we're going to do that.
- (cond ((and chdir (or arg current-eshell))
- (fresh-eshell))
- ((and chdir default-directory-eshell)
- (pop-to-buffer default-directory-eshell))
- ((or chdir (not (string= (buffer-name) eshell-buffer-name)))
- (if-let ((buffer (get-buffer eshell-buffer-name)))
- (if (busy-p buffer)
- (fresh-eshell)
- (pop-to-buffer buffer)
- (goto-char (point-max))
- (when chdir (spw/eshell-cd initial-default-directory)))
- (fresh-eshell)))
- ;; If `display-buffer-overriding-action' has some entries, pop to
- ;; ourselves, to allow subsequent cycling to a different Eshell in
- ;; another window. E.g. C-c e e C-x 4 4 C-c e e.
- ((cl-find-if-not #'null display-buffer-overriding-action)
- (pop-to-buffer (current-buffer))))
- ;; Finally, generate and return a ring for cycling purposes.
- (let* ((all (delete (current-buffer)
- (nconc project-eshells other-eshells)))
+ (push buffer all-eshells)
+ (when chdir
+ (cond ((and (not target-directory-eshell)
+ (or (not (busy-p buffer)) (eq chdir 'project))
+ (string= default-directory target-directory))
+ (setq target-directory-eshell buffer))
+ ((and project-root (not same-project-eshell)
+ (not (busy-p buffer))
+ (file-in-directory-p default-directory project-root))
+ (setq same-project-eshell buffer)))))))
+ (let ((current-eshell (and (eq major-mode 'eshell-mode)
+ (not (busy-p (current-buffer)))
+ (current-buffer))))
+ (cond ((and chdir (or arg current-eshell))
+ (fresh-eshell))
+ ((and chdir target-directory-eshell)
+ (pop-to-buffer target-directory-eshell))
+ ((and chdir same-project-eshell)
+ (pop-to-buffer same-project-eshell)
+ (goto-char (point-max))
+ (spw/eshell-cd target-directory))
+ (chdir
+ (fresh-eshell))
+ ((not current-eshell)
+ (if-let ((buf (cl-find-if-not #'busy-p (reverse all-eshells))))
+ (pop-to-buffer buf)
+ (fresh-eshell)))
+ ;; If `display-buffer-overriding-action' has some entries, pop
+ ;; to ourselves, to allow subsequent cycling to a different
+ ;; Eshell in another window, and similar. E.g. M-! C-x 4 4 M-!.
+ ((or (car display-buffer-overriding-action)
+ (cdr display-buffer-overriding-action))
+ (pop-to-buffer (current-buffer)))))
+ (let* ((all (delete (current-buffer) all-eshells))
(ring (make-ring (1+ (length all)))))
(dolist (buffer all)
(ring-insert ring buffer))
@@ -1566,6 +1553,24 @@ The ideas behind this behaviour are as follows.
(spw/eshell-jump arg t)))
(transient-cycles-buffer-ring-cycler :ring ret-val))
+(with-eval-after-load 'project
+ (when (boundp 'project-prefix-map) ; for Emacs 27
+ (transient-cycles-define-commands (prev-buffers)
+ ((("e" . spw/project-eshell) (arg)
+ (interactive "P")
+ (prog1 (spw/eshell-jump arg 'project)
+ (setq prev-buffers (window-prev-buffers)))))
+ (transient-cycles-buffer-ring-cycler :ring ret-val)
+ :on-exit (progn (switch-to-buffer (current-buffer) nil t)
+ (set-window-next-buffers nil nil)
+ (set-window-prev-buffers nil prev-buffers))
+ ;; Bind into project-prefix-map, rather than adding a remap, so that we
+ ;; have it under C-x 4 p, C-x 5 p etc. too.
+ :keymap project-prefix-map)
+ ;; Update project-switch-commands accordingly.
+ (when-let ((cell (assoc 'project-eshell project-switch-commands)))
+ (setcar cell #'transient-cycles-cmd-spw/project-eshell))))
+
;;; my commands -- like defining functions in .bashrc where simple aliases are
;;; not enough