diff options
Diffstat (limited to 'lisp/tab-bar.el')
-rw-r--r-- | lisp/tab-bar.el | 296 |
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) |