diff options
author | Sean Whitton <spwhitton@spwhitton.name> | 2022-05-10 08:40:22 -0700 |
---|---|---|
committer | Sean Whitton <spwhitton@spwhitton.name> | 2022-05-10 17:52:16 -0700 |
commit | d4117a2d6f0f35010f7e9da95fc726dbac75b334 (patch) | |
tree | 778fe08f3dc2456a440c60f3ccd1aa459c2cd425 /.emacs.d | |
parent | 488ebcbc426a9feb3198b85ac3facf90bf15f614 (diff) | |
download | dotfiles-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.el | 217 |
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 |