summaryrefslogtreecommitdiff
path: root/lisp/tab-bar.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/tab-bar.el')
-rw-r--r--lisp/tab-bar.el296
1 files changed, 167 insertions, 129 deletions
diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el
index 5bfad5f9b11..cf5ae09a247 100644
--- a/lisp/tab-bar.el
+++ b/lisp/tab-bar.el
@@ -200,7 +200,9 @@ a list of frames to update."
(t frames))))
;; Loop over all frames and update `tab-bar-lines'
(dolist (frame frame-lst)
- (unless (frame-parameter frame 'tab-bar-lines-keep-state)
+ (unless (or (frame-parameter frame 'tab-bar-lines-keep-state)
+ (and (eq auto-resize-tab-bars 'grow-only)
+ (> (frame-parameter frame 'tab-bar-lines) 1)))
(set-frame-parameter frame 'tab-bar-lines
(tab-bar--tab-bar-lines-for-frame frame)))))
;; Update `default-frame-alist'
@@ -229,7 +231,7 @@ a list of frames to update."
(defun tab-bar--key-to-number (key)
"Return the tab number represented by KEY.
-If KEY is a symbol 'tab-N', where N is a tab number, the value is N.
+If KEY is a symbol `tab-N', where N is a tab number, the value is N.
If KEY is \\='current-tab, the value is nil.
For any other value of KEY, the value is t."
(cond
@@ -371,31 +373,28 @@ at the mouse-down event to the position at mouse-up event."
(tab-bar-move-tab-to
(if (null to) (1+ (tab-bar--current-tab-index)) to) from))))
-(defvar tab-bar-map
- (let ((map (make-sparse-keymap)))
- (define-key map [down-mouse-1] 'tab-bar-mouse-down-1)
- (define-key map [drag-mouse-1] 'tab-bar-mouse-move-tab)
- (define-key map [mouse-1] 'tab-bar-mouse-1)
- (define-key map [down-mouse-2] 'tab-bar-mouse-close-tab)
- (define-key map [mouse-2] 'ignore)
- (define-key map [down-mouse-3] 'tab-bar-mouse-context-menu)
-
- (define-key map [mouse-4] 'tab-previous)
- (define-key map [mouse-5] 'tab-next)
- (define-key map [wheel-up] 'tab-previous)
- (define-key map [wheel-down] 'tab-next)
- (define-key map [wheel-left] 'tab-previous)
- (define-key map [wheel-right] 'tab-next)
-
- (define-key map [S-mouse-4] 'tab-bar-move-tab-backward)
- (define-key map [S-mouse-5] 'tab-bar-move-tab)
- (define-key map [S-wheel-up] 'tab-bar-move-tab-backward)
- (define-key map [S-wheel-down] 'tab-bar-move-tab)
- (define-key map [S-wheel-left] 'tab-bar-move-tab-backward)
- (define-key map [S-wheel-right] 'tab-bar-move-tab)
-
- map)
- "Keymap for the commands used on the tab bar.")
+(defvar-keymap tab-bar-map
+ :doc "Keymap for the commands used on the tab bar."
+ "<down-mouse-1>" #'tab-bar-mouse-down-1
+ "<drag-mouse-1>" #'tab-bar-mouse-move-tab
+ "<mouse-1>" #'tab-bar-mouse-1
+ "<down-mouse-2>" #'tab-bar-mouse-close-tab
+ "<mouse-2>" #'ignore
+ "<down-mouse-3>" #'tab-bar-mouse-context-menu
+
+ "<mouse-4>" #'tab-previous
+ "<mouse-5>" #'tab-next
+ "<wheel-up>" #'tab-previous
+ "<wheel-down>" #'tab-next
+ "<wheel-left>" #'tab-previous
+ "<wheel-right>" #'tab-next
+
+ "S-<mouse-4>" #'tab-bar-move-tab-backward
+ "S-<mouse-5>" #'tab-bar-move-tab
+ "S-<wheel-up>" #'tab-bar-move-tab-backward
+ "S-<wheel-down>" #'tab-bar-move-tab
+ "S-<wheel-left>" #'tab-bar-move-tab-backward
+ "S-<wheel-right>" #'tab-bar-move-tab)
(global-set-key [tab-bar]
`(menu-item ,(purecopy "tab bar") ignore
@@ -426,7 +425,7 @@ on each new frame when the global `tab-bar-mode' is disabled,
or if you want to disable the tab bar individually on each
new frame when the global `tab-bar-mode' is enabled, by using
- (add-hook 'after-make-frame-functions 'toggle-frame-tab-bar)"
+ (add-hook \\='after-make-frame-functions #\\='toggle-frame-tab-bar)"
(interactive)
(set-frame-parameter frame 'tab-bar-lines
(if (> (frame-parameter frame 'tab-bar-lines) 0) 0 1))
@@ -474,18 +473,22 @@ you can use the command `toggle-frame-tab-bar'."
If t, start a new tab with the current buffer, i.e. the buffer
that was current before calling the command that adds a new tab
(this is the same what `make-frame' does by default).
+If the value is the symbol `window', then keep the selected
+window as a single window on the new tab, and keep all its
+window parameters except `window-atom' and `window-side'.
If the value is a string, use it as a buffer name to switch to
if such buffer exists, or switch to a buffer visiting the file or
directory that the string specifies. If the value is a function,
call it with no arguments and switch to the buffer that it returns.
-If nil, duplicate the contents of the tab that was active
+If `clone', duplicate the contents of the tab that was active
before calling the command that adds a new tab."
:type '(choice (const :tag "Current buffer" t)
+ (const :tag "Current window" window)
(string :tag "Buffer" "*scratch*")
(directory :tag "Directory" :value "~/")
(file :tag "File" :value "~/.emacs")
(function :tag "Function")
- (const :tag "Duplicate tab" nil))
+ (const :tag "Duplicate tab" clone))
:group 'tab-bar
:version "27.1")
@@ -614,7 +617,7 @@ Also add the number of windows in the window configuration."
"Maximum length of the tab name from the current buffer.
Effective when `tab-bar-tab-name-function' is customized
to `tab-bar-tab-name-truncated'."
- :type 'integer
+ :type 'natnum
:group 'tab-bar
:version "27.1")
@@ -751,9 +754,13 @@ Used by `tab-bar-format-menu-bar'."
(menu-bar-keymap))
(popup-menu menu event)))
+(defvar tab-bar-menu-bar-button
+ (propertize "Menu" 'face 'tab-bar-tab-inactive)
+ "Button for the menu bar.")
+
(defun tab-bar-format-menu-bar ()
"Produce the Menu button for the tab bar that shows the menu bar."
- `((menu-bar menu-item (propertize "Menu" 'face 'tab-bar-tab-inactive)
+ `((menu-bar menu-item ,tab-bar-menu-bar-button
tab-bar-menu-bar :help "Menu Bar")))
(defun tab-bar-format-history ()
@@ -907,8 +914,8 @@ when the tab is current. Return the result as a keymap."
(let* ((rest (cdr (memq 'tab-bar-format-align-right tab-bar-format)))
(rest (tab-bar-format-list rest))
(rest (mapconcat (lambda (item) (nth 2 item)) rest ""))
- (hpos (length rest))
- (str (propertize " " 'display `(space :align-to (- right ,hpos)))))
+ (hpos (string-pixel-width (propertize rest 'face 'tab-bar)))
+ (str (propertize " " 'display `(space :align-to (- right (,hpos))))))
`((align-right menu-item ,str ignore))))
(defun tab-bar-format-global ()
@@ -918,7 +925,7 @@ When `tab-bar-format-global' is added to `tab-bar-format'
then modes that display information on the mode line
using `global-mode-string' will display the same text
on the tab bar instead."
- `((global menu-item ,(string-trim-right (format-mode-line global-mode-string)) ignore)))
+ `((global menu-item ,(format-mode-line global-mode-string) ignore)))
(defun tab-bar-format-list (format-list)
(let ((i 0))
@@ -982,10 +989,11 @@ on the tab bar instead."
(wc-point . ,(point-marker))
(wc-bl . ,bl)
(wc-bbl . ,bbl)
- (wc-history-back . ,(gethash (or frame (selected-frame))
- tab-bar-history-back))
- (wc-history-forward . ,(gethash (or frame (selected-frame))
- tab-bar-history-forward))
+ ,@(when tab-bar-history-mode
+ `((wc-history-back . ,(gethash (or frame (selected-frame))
+ tab-bar-history-back))
+ (wc-history-forward . ,(gethash (or frame (selected-frame))
+ tab-bar-history-forward))))
;; Copy other possible parameters
,@(mapcan (lambda (param)
(unless (memq (car param)
@@ -1126,19 +1134,21 @@ Negative TAB-NUMBER counts tabs from the end of the tab bar."
(when wc-bl (set-frame-parameter nil 'buffer-list wc-bl))
(when wc-bbl (set-frame-parameter nil 'buried-buffer-list wc-bbl))
- (puthash (selected-frame)
- (and (window-configuration-p (alist-get 'wc (car wc-history-back)))
- wc-history-back)
- tab-bar-history-back)
- (puthash (selected-frame)
- (and (window-configuration-p (alist-get 'wc (car wc-history-forward)))
- wc-history-forward)
- tab-bar-history-forward)))
+ (when tab-bar-history-mode
+ (puthash (selected-frame)
+ (and (window-configuration-p (alist-get 'wc (car wc-history-back)))
+ wc-history-back)
+ tab-bar-history-back)
+ (puthash (selected-frame)
+ (and (window-configuration-p (alist-get 'wc (car wc-history-forward)))
+ wc-history-forward)
+ tab-bar-history-forward))))
(ws
(window-state-put ws nil 'safe)))
- (setq tab-bar-history-omit t)
+ (when tab-bar-history-mode
+ (setq tab-bar-history-omit t))
(when from-index
(setf (nth from-index tabs) from-tab))
@@ -1193,7 +1203,9 @@ Interactively, ARG is the prefix numeric argument and defaults to 1."
Default values are tab names sorted by recency, so you can use \
\\<minibuffer-local-map>\\[next-history-element]
to get the name of the most recently visited tab, the second
-most recent, and so on."
+most recent, and so on.
+When the tab with that NAME doesn't exist, create a new tab
+and rename it to NAME."
(interactive
(let* ((recent-tabs (mapcar (lambda (tab)
(alist-get 'name tab))
@@ -1201,7 +1213,11 @@ most recent, and so on."
(list (completing-read (format-prompt "Switch to tab by name"
(car recent-tabs))
recent-tabs nil nil nil nil recent-tabs))))
- (tab-bar-select-tab (1+ (or (tab-bar--tab-index-by-name name) 0))))
+ (let ((tab-index (tab-bar--tab-index-by-name name)))
+ (if tab-index
+ (tab-bar-select-tab (1+ tab-index))
+ (tab-bar-new-tab)
+ (tab-bar-rename-tab name))))
(defalias 'tab-bar-select-tab-by-name 'tab-bar-switch-to-tab)
@@ -1301,7 +1317,8 @@ configuration."
(let ((tab-bar-new-tab-choice 'window))
(tab-bar-new-tab))
(tab-bar-switch-to-recent-tab)
- (delete-window)
+ (let ((ignore-window-parameters t))
+ (delete-window))
(tab-bar-switch-to-recent-tab))
@@ -1348,14 +1365,26 @@ After the tab is created, the hooks in
;; Handle the case when it's called in the active minibuffer.
(when (minibuffer-selected-window)
(select-window (minibuffer-selected-window)))
+ ;; Remove window parameters that can cause problems
+ ;; with `delete-other-windows' and `split-window'.
+ (unless (eq tab-bar-new-tab-choice 'clone)
+ (set-window-parameter nil 'window-atom nil)
+ (set-window-parameter nil 'window-side nil))
(let ((ignore-window-parameters t))
- (delete-other-windows))
- (unless (eq tab-bar-new-tab-choice 'window)
- ;; Create a new window to get rid of old window parameters
- ;; (e.g. prev/next buffers) of old window.
- (split-window) (delete-window))
+ (if (eq tab-bar-new-tab-choice 'clone)
+ ;; Create new unique windows with the same layout
+ (window-state-put (window-state-get))
+ (delete-other-windows)
+ (if (eq tab-bar-new-tab-choice 'window)
+ ;; Create new unique window from remaining window
+ (window-state-put (window-state-get))
+ ;; Create a new window to get rid of old window parameters
+ ;; (e.g. prev/next buffers) of old window.
+ (split-window) (delete-window))))
+
(let ((buffer
- (if (functionp tab-bar-new-tab-choice)
+ (if (and (functionp tab-bar-new-tab-choice)
+ (not (memq tab-bar-new-tab-choice '(clone window))))
(funcall tab-bar-new-tab-choice)
(if (stringp tab-bar-new-tab-choice)
(or (get-buffer tab-bar-new-tab-choice)
@@ -1388,6 +1417,11 @@ After the tab is created, the hooks in
;; `pushnew' handles the head of tabs but not frame-parameter
(tab-bar-tabs-set tabs))
+ (when tab-bar-history-mode
+ (puthash (selected-frame) nil tab-bar-history-back)
+ (puthash (selected-frame) nil tab-bar-history-forward)
+ (setq tab-bar-history-omit t))
+
(run-hook-with-args 'tab-bar-tab-post-open-functions
(nth to-index tabs)))
@@ -1426,7 +1460,7 @@ If FROM-NUMBER is a tab number, a new tab is created from that tab."
"Clone the current tab to ARG positions to the right.
ARG and FROM-NUMBER have the same meaning as in `tab-bar-new-tab'."
(interactive "P")
- (let ((tab-bar-new-tab-choice nil)
+ (let ((tab-bar-new-tab-choice 'clone)
(tab-bar-new-tab-group t))
(tab-bar-new-tab arg from-number)))
@@ -1624,9 +1658,10 @@ happens interactively)."
(setq index (max 0 (min index (length tabs))))
(cl-pushnew tab (nthcdr index tabs))
(when (eq index 0)
- ;; pushnew handles the head of tabs but not frame-parameter
+ ;; `pushnew' handles the head of tabs but not frame-parameter
(tab-bar-tabs-set tabs))
- (tab-bar-select-tab (1+ index))))
+ (tab-bar-select-tab (1+ index)))
+ (tab-bar--update-tab-bar-lines))
(message "No more closed tabs to undo")))
@@ -1803,30 +1838,34 @@ Interactively, prompt for GROUP-NAME."
(defvar tab-bar-history-old nil
"Window configuration before the current command.")
-(defvar tab-bar-history-old-minibuffer-depth 0
- "Minibuffer depth before the current command.")
+(defvar tab-bar-history-pre-command nil
+ "Command set to `this-command' by `pre-command-hook'.")
+
+(defvar tab-bar-history-done-command nil
+ "Command handled by `window-configuration-change-hook'.")
(defun tab-bar--history-pre-change ()
- (setq tab-bar-history-old-minibuffer-depth (minibuffer-depth))
- ;; Store window-configuration before possibly entering the minibuffer.
- (when (zerop tab-bar-history-old-minibuffer-depth)
+ ;; Reset before the command could set it
+ (setq tab-bar-history-omit nil)
+ (setq tab-bar-history-pre-command this-command)
+ (when (zerop (minibuffer-depth))
(setq tab-bar-history-old
`((wc . ,(current-window-configuration))
(wc-point . ,(point-marker))))))
(defun tab-bar--history-change ()
- (when (and (not tab-bar-history-omit)
- tab-bar-history-old
- ;; Store window-configuration before possibly entering
- ;; the minibuffer.
- (zerop tab-bar-history-old-minibuffer-depth))
+ (when (and (not tab-bar-history-omit) tab-bar-history-old
+ ;; Don't register changes performed by the same command
+ ;; repeated in sequence, such as incremental window resizing.
+ (not (eq tab-bar-history-done-command tab-bar-history-pre-command))
+ (zerop (minibuffer-depth)))
(puthash (selected-frame)
(seq-take (cons tab-bar-history-old
(gethash (selected-frame) tab-bar-history-back))
tab-bar-history-limit)
- tab-bar-history-back))
- (when tab-bar-history-omit
- (setq tab-bar-history-omit nil)))
+ tab-bar-history-back)
+ (setq tab-bar-history-old nil))
+ (setq tab-bar-history-done-command tab-bar-history-pre-command))
(defun tab-bar-history-back ()
"Restore a previous window configuration used in the current tab.
@@ -1866,6 +1905,10 @@ This navigates forward in the history of window configurations."
(goto-char wc-point)))
(message "No more tab forward history"))))
+(defvar-keymap tab-bar-history-mode-map
+ "C-c <left>" #'tab-bar-history-back
+ "C-c <right>" #'tab-bar-history-forward)
+
(define-minor-mode tab-bar-history-mode
"Toggle tab history mode for the tab bar.
Tab history mode remembers window configurations used in every tab,
@@ -1962,26 +2005,25 @@ For more information, see the function `tab-switcher'."
(defvar-local tab-switcher-column 3)
-(defvar tab-switcher-mode-map
- (let ((map (make-keymap)))
- (suppress-keymap map t)
- (define-key map "q" 'quit-window)
- (define-key map "\C-m" 'tab-switcher-select)
- (define-key map "d" 'tab-switcher-delete)
- (define-key map "k" 'tab-switcher-delete)
- (define-key map "\C-d" 'tab-switcher-delete-backwards)
- (define-key map "\C-k" 'tab-switcher-delete)
- (define-key map "x" 'tab-switcher-execute)
- (define-key map " " 'tab-switcher-next-line)
- (define-key map "n" 'tab-switcher-next-line)
- (define-key map "p" 'tab-switcher-prev-line)
- (define-key map "\177" 'tab-switcher-backup-unmark)
- (define-key map "?" 'describe-mode)
- (define-key map "u" 'tab-switcher-unmark)
- (define-key map [mouse-2] 'tab-switcher-mouse-select)
- (define-key map [follow-link] 'mouse-face)
- map)
- "Local keymap for `tab-switcher-mode' buffers.")
+(defvar-keymap tab-switcher-mode-map
+ :doc "Local keymap for `tab-switcher-mode' buffers."
+ :full t
+ :suppress t
+ "q" #'quit-window
+ "RET" #'tab-switcher-select
+ "d" #'tab-switcher-delete
+ "k" #'tab-switcher-delete
+ "C-d" #'tab-switcher-delete-backwards
+ "C-k" #'tab-switcher-delete
+ "x" #'tab-switcher-execute
+ "SPC" #'tab-switcher-next-line
+ "n" #'tab-switcher-next-line
+ "p" #'tab-switcher-prev-line
+ "DEL" #'tab-switcher-backup-unmark
+ "?" #'describe-mode
+ "u" #'tab-switcher-unmark
+ "<mouse-2>" #'tab-switcher-mouse-select
+ "<follow-link>" 'mouse-face)
(define-derived-mode tab-switcher-mode nil "Window Configurations"
"Major mode for selecting a window configuration.
@@ -2276,9 +2318,9 @@ Interactively, prompt for the buffer to switch to."
(declare (advertised-calling-convention (buffer-or-name) "28.1"))
(interactive
(list (read-buffer-to-switch "Switch to buffer in other tab: ")))
- (display-buffer (window-normalize-buffer-to-switch-to buffer-or-name)
- '((display-buffer-in-tab)
- (inhibit-same-window . nil))))
+ (pop-to-buffer (window-normalize-buffer-to-switch-to buffer-or-name)
+ '((display-buffer-in-tab)
+ (inhibit-same-window . nil))))
(defun find-file-other-tab (filename &optional wildcards)
"Edit file FILENAME, in another tab.
@@ -2357,42 +2399,38 @@ When `switch-to-buffer-obey-display-actions' is non-nil,
(defalias 'tab-group 'tab-bar-change-tab-group)
(defalias 'tab-list 'tab-switcher)
-(define-key tab-prefix-map "n" 'tab-duplicate)
-(define-key tab-prefix-map "N" 'tab-new-to)
-(define-key tab-prefix-map "2" 'tab-new)
-(define-key tab-prefix-map "1" 'tab-close-other)
-(define-key tab-prefix-map "0" 'tab-close)
-(define-key tab-prefix-map "u" 'tab-undo)
-(define-key tab-prefix-map "o" 'tab-next)
-(define-key tab-prefix-map "O" 'tab-previous)
-(define-key tab-prefix-map "m" 'tab-move)
-(define-key tab-prefix-map "M" 'tab-move-to)
-(define-key tab-prefix-map "G" 'tab-group)
-(define-key tab-prefix-map "r" 'tab-rename)
-(define-key tab-prefix-map "\r" 'tab-switch)
-(define-key tab-prefix-map "b" 'switch-to-buffer-other-tab)
-(define-key tab-prefix-map "f" 'find-file-other-tab)
-(define-key tab-prefix-map "\C-f" 'find-file-other-tab)
-(define-key tab-prefix-map "\C-r" 'find-file-read-only-other-tab)
-(define-key tab-prefix-map "t" 'other-tab-prefix)
-
-(defvar tab-bar-switch-repeat-map
- (let ((map (make-sparse-keymap)))
- (define-key map "o" 'tab-next)
- (define-key map "O" 'tab-previous)
- map)
- "Keymap to repeat tab switch key sequences `C-x t o o O'.
-Used in `repeat-mode'.")
+(keymap-set tab-prefix-map "n" #'tab-duplicate)
+(keymap-set tab-prefix-map "N" #'tab-new-to)
+(keymap-set tab-prefix-map "2" #'tab-new)
+(keymap-set tab-prefix-map "1" #'tab-close-other)
+(keymap-set tab-prefix-map "0" #'tab-close)
+(keymap-set tab-prefix-map "u" #'tab-undo)
+(keymap-set tab-prefix-map "o" #'tab-next)
+(keymap-set tab-prefix-map "O" #'tab-previous)
+(keymap-set tab-prefix-map "m" #'tab-move)
+(keymap-set tab-prefix-map "M" #'tab-move-to)
+(keymap-set tab-prefix-map "G" #'tab-group)
+(keymap-set tab-prefix-map "r" #'tab-rename)
+(keymap-set tab-prefix-map "RET" #'tab-switch)
+(keymap-set tab-prefix-map "b" #'switch-to-buffer-other-tab)
+(keymap-set tab-prefix-map "f" #'find-file-other-tab)
+(keymap-set tab-prefix-map "C-f" #'find-file-other-tab)
+(keymap-set tab-prefix-map "C-r" #'find-file-read-only-other-tab)
+(keymap-set tab-prefix-map "t" #'other-tab-prefix)
+
+(defvar-keymap tab-bar-switch-repeat-map
+ :doc "Keymap to repeat tab switch key sequences \\`C-x t o o O'.
+Used in `repeat-mode'."
+ "o" #'tab-next
+ "O" #'tab-previous)
(put 'tab-next 'repeat-map 'tab-bar-switch-repeat-map)
(put 'tab-previous 'repeat-map 'tab-bar-switch-repeat-map)
-(defvar tab-bar-move-repeat-map
- (let ((map (make-sparse-keymap)))
- (define-key map "m" 'tab-move)
- (define-key map "M" 'tab-bar-move-tab-backward)
- map)
- "Keymap to repeat tab move key sequences `C-x t m m M'.
-Used in `repeat-mode'.")
+(defvar-keymap tab-bar-move-repeat-map
+ :doc "Keymap to repeat tab move key sequences \\`C-x t m m M'.
+Used in `repeat-mode'."
+ "m" #'tab-move
+ "M" #'tab-bar-move-tab-backward)
(put 'tab-move 'repeat-map 'tab-bar-move-repeat-map)
(put 'tab-bar-move-tab-backward 'repeat-map 'tab-bar-move-repeat-map)