aboutsummaryrefslogtreecommitdiff
path: root/modules/ui-navigation.el
diff options
context:
space:
mode:
Diffstat (limited to 'modules/ui-navigation.el')
-rw-r--r--modules/ui-navigation.el99
1 files changed, 93 insertions, 6 deletions
diff --git a/modules/ui-navigation.el b/modules/ui-navigation.el
index f2181d97e..c099e0834 100644
--- a/modules/ui-navigation.el
+++ b/modules/ui-navigation.el
@@ -75,14 +75,55 @@ resize -- each moves the active window's divider in the arrow's direction
"<up>" #'windsize-up
"<down>" #'windsize-down)
+(defun cj/window-pull-side (key)
+ "Map a `C-; b' arrow KEY to the side the revealed window opens on.
+The arrow names the edge the current window shrinks toward, so the new
+window opens on the *opposite* side and the current window keeps the
+arrow's edge: <down> -> above, <up> -> below, <left> -> right,
+<right> -> left. Returns nil for anything else."
+ (pcase key
+ ("<down>" 'above)
+ ("<up>" 'below)
+ ("<left>" 'right)
+ ("<right>" 'left)
+ (_ nil)))
+
+(defun cj/window--pull-away (side)
+ "Split the sole window so the previous buffer opens on SIDE.
+SIDE is one of above/below/left/right -- opposite the pressed arrow, so
+the current window keeps the arrow's edge. The new window is minimized
+to a sliver (the current window keeps almost the whole frame) and shows
+`other-buffer'; focus stays on the current window so the sticky arrows
+then shrink it step by step via `windsize', exactly as resizing an
+existing split does. No-op when SIDE is nil."
+ (when side
+ (let ((new (split-window (selected-window) nil side)))
+ (set-window-buffer new (other-buffer (current-buffer) t))
+ ;; Shrink the reveal to the smallest window Emacs allows (~2 lines, the
+ ;; mode line) so the current window keeps almost the whole frame; the
+ ;; sticky `windsize' arrows grow the reveal from there. `minimize-window'
+ ;; floors at `window-min-height' (4 by default), so bind it down to 1.
+ (let ((window-min-height 1))
+ (minimize-window new))
+ new)))
+
(defun cj/window-resize-sticky ()
"Resize the active window's divider in the just-pressed arrow's direction
-(via `windsize'), then keep `cj/window-resize-map' active so bare arrows keep
-nudging until any other key. Bound to `C-; b <left>/<right>/<up>/<down>'."
+\(via `windsize'), then keep `cj/window-resize-map' active so bare arrows keep
+nudging until any other key. Bound to `C-; b <left>/<right>/<up>/<down>'.
+
+When the selected window is the sole window in the frame there is no
+divider to move, so the first arrow instead splits a sliver away on the
+side opposite the arrow (`cj/window--pull-away'), revealing the previous
+buffer; the current window keeps almost the whole frame and the following
+arrows shrink it via `windsize', so it reads the same as resizing an
+existing split."
(interactive)
- (let ((cmd (keymap-lookup cj/window-resize-map
- (key-description (vector last-command-event)))))
- (when cmd (call-interactively cmd)))
+ (let ((key (key-description (vector last-command-event))))
+ (if (one-window-p)
+ (cj/window--pull-away (cj/window-pull-side key))
+ (let ((cmd (keymap-lookup cj/window-resize-map key)))
+ (when cmd (call-interactively cmd)))))
(set-transient-map cj/window-resize-map t))
;; ------------------------------ Window Splitting -----------------------------
@@ -103,6 +144,49 @@ nudging until any other key. Bound to `C-; b <left>/<right>/<up>/<down>'."
(consult-buffer))
(keymap-global-set "M-S-h" #'cj/split-and-follow-below) ;; was M-H
+(defun cj/--dashboard-buffer ()
+ "Return the *dashboard* buffer, creating it if needed, without changing windows."
+ (or (get-buffer "*dashboard*")
+ (save-window-excursion
+ (when (fboundp 'dashboard-open) (dashboard-open))
+ (get-buffer "*dashboard*"))))
+
+(defun cj/--split-show-buffer (split-fn buffer)
+ "Split with SPLIT-FN, show BUFFER in the new window, keep point in the current
+window. Return the new window."
+ (let ((new (funcall split-fn)))
+ (when (and (window-live-p new) buffer)
+ (set-window-buffer new buffer))
+ new))
+
+(defun cj/--split-from-dashboard-p (buffer-name)
+ "Return non-nil when BUFFER-NAME is the dashboard.
+Splitting from the dashboard shows *scratch* in the new window instead of
+the dashboard again."
+ (equal buffer-name "*dashboard*"))
+
+(defun cj/--split-companion-buffer ()
+ "Buffer to show in the new window after a C-x 2 / C-x 3 split.
+The dashboard, or the *scratch* buffer when splitting from the dashboard."
+ (if (cj/--split-from-dashboard-p (buffer-name))
+ (get-scratch-buffer-create)
+ (cj/--dashboard-buffer)))
+
+(defun cj/split-below-with-dashboard ()
+ "Split below and show the companion buffer in the new window; stay in this one.
+The companion is the dashboard, or *scratch* when splitting from the dashboard."
+ (interactive)
+ (cj/--split-show-buffer #'split-window-below (cj/--split-companion-buffer)))
+
+(defun cj/split-right-with-dashboard ()
+ "Split right and show the companion buffer in the new window; stay in this one.
+The companion is the dashboard, or *scratch* when splitting from the dashboard."
+ (interactive)
+ (cj/--split-show-buffer #'split-window-right (cj/--split-companion-buffer)))
+
+(keymap-global-set "C-x 2" #'cj/split-below-with-dashboard)
+(keymap-global-set "C-x 3" #'cj/split-right-with-dashboard)
+
;; ------------------------- Split Window Reorientation ------------------------
(defun toggle-window-split ()
@@ -175,8 +259,11 @@ With numeric prefix ARG, re-open the ARGth most-recently-killed file
(buffer-list)))))
(mapc
(lambda (buf-file)
+ ;; delete (equal), not delq (eq): buf-file is a fresh string from
+ ;; expand-file-name and never eq to the recentf-list entries, so the
+ ;; skip-open-files logic was dead.
(setq recently-killed-list
- (delq buf-file recently-killed-list)))
+ (delete buf-file recently-killed-list)))
buffer-files-list)
(when recently-killed-list
(let ((file (nth (1- arg) recently-killed-list)))