├── .gitignore ├── README.md ├── screenshot.png └── sort-tab.el /.gitignore: -------------------------------------------------------------------------------- 1 | .aider* 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | # What is sort-tab? 6 | sort-tab is smarter tab solution for Emacs, it sort tab with using frequency. 7 | 8 | sort-tab stay on top of Emacs, it won't waste any vertically space when you split window. 9 | 10 | ## Installation 11 | 1. Clone or download this repository (path of the folder is the `` used below). 12 | 2. In your `~/.emacs`, add the following two lines: 13 | ```elisp 14 | (add-to-list 'load-path "") ; add sort-tab to your load-path 15 | (require 'sort-tab) 16 | (sort-tab-mode 1) 17 | ``` 18 | 19 | ## Usage 20 | * sort-tab-select-next-tab: select next tab 21 | * sort-tab-select-prev-tab: select previous tab 22 | * sort-tab-select-first-tab: select first tab 23 | * sort-tab-select-last-tab: select last tab 24 | * sort-tab-close-current-tab: close current tab 25 | 26 | #### SwitchTabByIndex 27 | You can bind the number keys to the command ```sort-tab-select-visible-tab```, such as s-1, s-2, s-3 ... etc. 28 | 29 | ``` 30 | (global-set-key (kbd "s-1") 'sort-tab-select-visible-tab) 31 | (global-set-key (kbd "s-2") 'sort-tab-select-visible-tab) 32 | (global-set-key (kbd "s-3") 'sort-tab-select-visible-tab) 33 | (global-set-key (kbd "s-4") 'sort-tab-select-visible-tab) 34 | (global-set-key (kbd "s-5") 'sort-tab-select-visible-tab) 35 | (global-set-key (kbd "s-6") 'sort-tab-select-visible-tab) 36 | (global-set-key (kbd "s-7") 'sort-tab-select-visible-tab) 37 | (global-set-key (kbd "s-8") 'sort-tab-select-visible-tab) 38 | (global-set-key (kbd "s-9") 'sort-tab-select-visible-tab) 39 | (global-set-key (kbd "s-0") 'sort-tab-select-visible-tab) 40 | (global-set-key (kbd "s-Q") 'sort-tab-close-all-tabs) 41 | (global-set-key (kbd "s-q") 'sort-tab-close-mode-tabs) 42 | (global-set-key (kbd "C-;") 'sort-tab-close-current-tab) 43 | ``` 44 | 45 | This function automatically recognizes the number at the end of the keystroke 46 | and switches to the tab of the corresponding index. 47 | 48 | ## Option 49 | * sort-tab-hide-function: you can customize this function to hide tab that match some rule, example, if we want hide all dired buffer, you can customize this function like this `(setq sort-tab-hide-function '(lambda (buf) (with-current-buffer buf (derived-mode-p 'dired-mode))))` 50 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manateelazycat/sort-tab/3affb16fc61838bacccdb208bc10888c537b224b/screenshot.png -------------------------------------------------------------------------------- /sort-tab.el: -------------------------------------------------------------------------------- 1 | ;;; sort-tab.el --- Smarter tab solution for Emacs, it sort tab with using frequency. -*- lexical-binding: t; -*- 2 | 3 | ;; Filename: sort-tab.el 4 | ;; Description: Provide an out of box configuration to use sort-tab in Emacs. 5 | ;; Author: Andy Stewart 6 | ;; Maintainer: Andy Stewart 7 | ;; Copyright (C) 2021, Andy Stewart, all rights reserved. 8 | ;; Created: 2021-10-26 22:14:34 9 | ;; Version: 1.0 10 | ;; Last-Updated: 2021-10-26 22:14:34 11 | ;; By: Andy Stewart 12 | ;; URL: http://www.emacswiki.org/emacs/download/sort-tab.el 13 | ;; Keywords: 14 | ;; Compatibility: GNU Emacs 29.0.50 15 | ;; 16 | ;; Features that might be required by this library: 17 | ;; 18 | ;; 19 | 20 | ;;; This file is NOT part of GNU Emacs 21 | 22 | ;;; License 23 | ;; 24 | ;; This program is free software; you can redistribute it and/or modify 25 | ;; it under the terms of the GNU General Public License as published by 26 | ;; the Free Software Foundation; either version 3, or (at your option) 27 | ;; any later version. 28 | 29 | ;; This program is distributed in the hope that it will be useful, 30 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 31 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 32 | ;; GNU General Public License for more details. 33 | 34 | ;; You should have received a copy of the GNU General Public License 35 | ;; along with this program; see the file COPYING. If not, write to 36 | ;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth 37 | ;; Floor, Boston, MA 02110-1301, USA. 38 | 39 | ;;; Commentary: 40 | ;; 41 | ;; Smarter tab solution for Emacs, it sort tab with using frequency. 42 | ;; 43 | 44 | ;;; Installation: 45 | ;; 46 | ;; Put sort-tab.el to your load-path. 47 | ;; The load-path is usually ~/elisp/. 48 | ;; It's set in your ~/.emacs like this: 49 | ;; (add-to-list 'load-path (expand-file-name "~/elisp")) 50 | ;; 51 | ;; And the following to your ~/.emacs startup file. 52 | ;; 53 | ;; (require 'sort-tab) 54 | ;; (sort-tab-mode t) 55 | ;; 56 | ;; No need more. 57 | ;; 58 | 59 | (defgroup sort-tab nil 60 | "Display sort-tab in top of Emacs." 61 | :group 'convenience) 62 | 63 | (defcustom sort-tab-buffer-name "*sort-tab*" 64 | "The buffer name of sort-tab." 65 | :type 'string) 66 | 67 | (defcustom sort-tab-name-max-length 50 68 | "Max length of tab name." 69 | :type 'int) 70 | 71 | (defcustom sort-tab-separator "|" 72 | "The separator between tabs." 73 | :type 'string) 74 | 75 | (defcustom sort-tab-align 'left 76 | "The align of sort-tab." 77 | :type '(choice (const :tag "Left" left) 78 | (const :tag "Center" center))) 79 | 80 | (defcustom sort-tab-show-index-number nil 81 | "Show index number before tab name." 82 | :type 'boolean) 83 | 84 | (defcustom sort-tab-hide-function nil 85 | "Customize function to hide buffer match rule. 86 | 87 | Customize function only need argument `buffer', you can write any code to filter buffer. 88 | 89 | If you want buffer hide, return t, or return nil.") 90 | 91 | (defcustom sort-tab-render-function 'sort-tab-render-tabs 92 | "Customize function to render tabs.") 93 | 94 | (defface sort-tab-current-tab-face 95 | '((((background light)) 96 | :background "#d5c9c0" :foreground "#282828" :bold t) 97 | (t 98 | :background "#504945" :foreground "#fbf1c7" :bold t)) 99 | "Face for current tab.") 100 | 101 | (defface sort-tab-other-tab-face 102 | '((((background light)) 103 | :foreground "#665c54" :bold nil) 104 | (t 105 | :foreground "#bdae93" :bold nil)) 106 | "Face for inactive tabs.") 107 | 108 | (defface sort-tab-separator-face 109 | '((((background light)) 110 | :foreground "#bdae93" :bold t) 111 | (t 112 | :foreground "#665c54" :bold t)) 113 | "Face for separator.") 114 | 115 | (defconst sort-tab-propertized-separator 116 | (propertize sort-tab-separator 'face 'sort-tab-separator-face)) 117 | 118 | (defvar sort-tab-mode-map 119 | (let ((km (make-sparse-keymap))) 120 | km) 121 | "Keymap to use in sort-tab mode.") 122 | 123 | (defvar sort-tab-window nil) 124 | 125 | (defvar sort-tab-buffer-freq-count-timer nil 126 | "Timer used for count buffer used frequency.") 127 | 128 | (defvar-local sort-tab-buffer-freq 0 129 | "Used frequency of current buffer.") 130 | 131 | (defvar sort-tab-count-freq-idle-time 1 132 | "Add used frequency after being idle for this much secs.") 133 | 134 | (defvar sort-tab-visible-buffers nil) 135 | 136 | (defvar sort-tab-last-active-buffer nil) 137 | 138 | (defun sort-tab-get-buffer () 139 | (get-buffer-create sort-tab-buffer-name)) 140 | 141 | (defun sort-tab-turn-on () 142 | (interactive) 143 | ;; Create sort-tab buffer. 144 | (with-current-buffer (sort-tab-get-buffer) 145 | ;; Disable line numbers mode. 146 | (when display-line-numbers 147 | (setq-local display-line-numbers nil)) 148 | ;; Disable tab-line. 149 | (when (version< "27.0" emacs-version) 150 | (setq-local tab-line-format nil)) 151 | ;; Disable hl-line, header-line and mode-line in input buffer. 152 | (setq-local header-line-format nil) 153 | (setq-local mode-line-format nil) 154 | ;; Disable cursor type if option `disable-cursor' is non-nil. 155 | (setq-local cursor-type nil) 156 | ;; Set Mini height to make sure sort-tab window always 1-line height. 157 | (setq-local window-min-height 1) 158 | ;; Disable wrap line. 159 | (setq-local truncate-lines t) 160 | ;; Disable window resize. 161 | (setq-local window-size-fixed 'height)) 162 | 163 | ;; Create sort-tab window. 164 | (sort-tab-create-window) 165 | 166 | ;; Start count buffer frequency. 167 | (sort-tab-start-count-freq) 168 | 169 | ;; Update sort-tab buffer list. 170 | (sort-tab-update-list) 171 | 172 | ;; Keymap 173 | (with-current-buffer "*sort-tab*" 174 | (local-set-key (kbd "") 'push-button) 175 | (local-set-key (kbd "") 'push-button) 176 | (local-set-key (kbd "") 'push-button)) 177 | 178 | ;; Add update hook. 179 | (add-hook 'buffer-list-update-hook #'sort-tab-update-list) 180 | 181 | (setq sort-tab-mode t)) 182 | 183 | (defun sort-tab-create-window () 184 | ;; Split top window. 185 | (ignore-errors 186 | (dotimes (i 50) 187 | (windmove-up))) 188 | (split-window-vertically 1) 189 | 190 | ;; Record sort-tab window. 191 | (setq sort-tab-window (selected-window)) 192 | (switch-to-buffer (sort-tab-get-buffer)) 193 | (other-window 1) 194 | 195 | ;; Set window dedicated to make sure pop buffer won't use sort-tab window. 196 | (set-window-dedicated-p sort-tab-window t) 197 | 198 | ;; Make sure sort-tab window can skip `delete-other-windows' and 'other-window'. 199 | (set-window-parameter sort-tab-window 'no-delete-other-windows t) 200 | (set-window-parameter sort-tab-window 'window-side 'top) 201 | (set-window-parameter sort-tab-window 'window-slot 0) 202 | (set-window-parameter sort-tab-window 'no-other-window t)) 203 | 204 | (defun sort-tab-turn-off () 205 | (interactive) 206 | (setq sort-tab-mode nil) 207 | 208 | (when (sort-tab-live-p) 209 | ;; Reset window parameter. 210 | (set-window-parameter sort-tab-window 'no-delete-other-windows nil) 211 | (set-window-parameter sort-tab-window 'window-side nil) 212 | (set-window-parameter sort-tab-window 'window-slot nil) 213 | (set-window-parameter sort-tab-window 'no-other-window nil) 214 | 215 | ;; Kill sort-tab window. 216 | (delete-window (get-buffer-window (sort-tab-get-buffer)))) 217 | 218 | ;; Reset sort-tab window. 219 | (setq sort-tab-window nil) 220 | 221 | ;; Stop count. 222 | (sort-tab-stop-count-freq) 223 | 224 | ;; Remove update hook. 225 | (remove-hook 'buffer-list-update-hook #'sort-tab-update-list)) 226 | 227 | (defun sort-tab-live-p () 228 | (and (buffer-live-p (get-buffer sort-tab-buffer-name)) 229 | sort-tab-window 230 | (window-live-p sort-tab-window))) 231 | 232 | (defun sort-tab-increase-buffer-freq () 233 | "Increase the used frequency of current buffer by 1." 234 | (cl-incf sort-tab-buffer-freq)) 235 | 236 | (defun sort-tab-start-count-freq () 237 | "Start counting buffer used frequency." 238 | (setq sort-tab-buffer-freq-count-timer 239 | (run-with-idle-timer sort-tab-count-freq-idle-time 240 | t #'sort-tab-increase-buffer-freq))) 241 | 242 | (defun sort-tab-stop-count-freq () 243 | "Stop counting buffer used frequency." 244 | (cancel-timer sort-tab-buffer-freq-count-timer) 245 | (setq sort-tab-buffer-freq-count-timer nil)) 246 | 247 | (defun sort-tab-buffer-freq (buf) 248 | "Return the used frequency of buffer BUF." 249 | (or (buffer-local-value 'sort-tab-buffer-freq buf) 250 | 0)) 251 | 252 | (defun sort-tab-get-buffer-mode (buf) 253 | (with-current-buffer buf 254 | major-mode)) 255 | 256 | (defun sort-tab-is-eaf-browser-buffer-p (buf) 257 | (with-current-buffer buf 258 | (and (eq major-mode 'eaf-mode) 259 | (equal eaf--buffer-app-name "browser")))) 260 | 261 | (defun sort-tab-is-eaf-file-manager-buffer-p (buf) 262 | (with-current-buffer buf 263 | (and (eq major-mode 'eaf-mode) 264 | (equal eaf--buffer-app-name "file-manager")))) 265 | 266 | ;; Let ace-window ignore *sort-tab* buffer. 267 | (when (featurep 'ace-window) 268 | (with-eval-after-load 'ace-window 269 | (add-to-list 'aw-ignored-buffers "*sort-tab*"))) 270 | 271 | (defun sort-tab-buffer-freq-higher-p (buf1 buf2) 272 | "Return t if the used frequency of BUF1 is higher than BUF2." 273 | (cond 274 | ;; EAF Browser tab sorted first. 275 | ((and (sort-tab-is-eaf-browser-buffer-p buf1) 276 | (not (sort-tab-is-eaf-browser-buffer-p buf2))) 277 | t) 278 | ((and (sort-tab-is-eaf-browser-buffer-p buf2) 279 | (not (sort-tab-is-eaf-browser-buffer-p buf1))) 280 | nil) 281 | ;; EAF File manager tab sorted last. 282 | ((and (sort-tab-is-eaf-file-manager-buffer-p buf1) 283 | (not (sort-tab-is-eaf-file-manager-buffer-p buf2))) 284 | nil) 285 | ((and (sort-tab-is-eaf-file-manager-buffer-p buf2) 286 | (not (sort-tab-is-eaf-file-manager-buffer-p buf1))) 287 | t) 288 | (t 289 | (let ((buf1-index (or (cl-position buf1 sort-tab-visible-buffers :test #'eq) -1)) 290 | (buf2-index (or (cl-position buf2 sort-tab-visible-buffers :test #'eq) -1))) 291 | ;; Do not swapped tab if two tags are adjacent and current command is next or prev tab. 292 | (if (and (<= (abs (- buf1-index buf2-index)) 1) 293 | (member this-command '(sort-tab-select-next-tab sort-tab-select-prev-tab))) 294 | (< buf1-index buf2-index) 295 | 296 | ;; Otherwise, sort by frequency of tab. 297 | (> (sort-tab-buffer-freq buf1) (sort-tab-buffer-freq buf2) ) 298 | ))))) 299 | 300 | (defun sort-tab-is-magit-buffer-p (buf) 301 | (with-current-buffer buf ;not magit buffer 302 | (or (derived-mode-p 'magit-status-mode) 303 | (derived-mode-p 'magit-process-mode) 304 | (derived-mode-p 'magit-diff-mode) 305 | (derived-mode-p 'magit-log-mode) 306 | (derived-mode-p 'magit-status-mode) 307 | ))) 308 | 309 | (defun sort-tab-buffer-need-hide-p (buf) 310 | (let* ((name (buffer-name buf))) 311 | (or 312 | (cl-some (lambda (prefix) (string-prefix-p prefix name)) '("*" " *" "COMMIT_EDITMSG")) 313 | (eq (aref name 0) ?\s) 314 | (sort-tab-is-magit-buffer-p buf) 315 | (when sort-tab-hide-function 316 | (funcall sort-tab-hide-function buf)) 317 | ))) 318 | 319 | (defun sort-tab-is-normal-buffer-p (current-buffer) 320 | (and 321 | (not (window-minibuffer-p)) 322 | (not (eq current-buffer sort-tab-last-active-buffer)) 323 | (not (sort-tab-buffer-need-hide-p current-buffer)) 324 | )) 325 | 326 | (defun sort-tab-is-hidden-buffer-p (buf) 327 | (let* ((name (buffer-name buf))) 328 | (and 329 | (sort-tab-buffer-need-hide-p buf) 330 | (not (window-minibuffer-p)) 331 | (not (cl-some (lambda (prefix) (string-prefix-p prefix name)) '(" *eldoc" " *snails" "*Help" "*Flycheck" "COMMIT_EDITMSG" " *rime" "*color-rg*"))) 332 | (not (string-equal sort-tab-buffer-name name)) 333 | (not (sort-tab-is-magit-buffer-p buf)) 334 | ))) 335 | 336 | (cl-defmacro sort-tab-update-tabs (&rest body) 337 | `(with-current-buffer (sort-tab-get-buffer) 338 | ;; Clean buffer. 339 | (erase-buffer) 340 | 341 | ;; Update tabs. 342 | ,@body 343 | 344 | (when (eq sort-tab-align 'center) 345 | (goto-char (point-min)) 346 | (insert sort-tab-propertized-separator) 347 | (let* ((width (window-width (get-buffer-window))) 348 | (content-length (length (buffer-string))) 349 | (padding (max 0 (/ (- width content-length) 2)))) 350 | (goto-char (point-min)) 351 | (insert (make-string padding ?\s)))) 352 | 353 | ;; Record last active buffer. 354 | (setq sort-tab-last-active-buffer (current-buffer)) 355 | )) 356 | 357 | (defun sort-tab-get-visible-buffer-infos () 358 | (let* ((current-buffer (window-buffer)) 359 | visible-buffer-infos) 360 | (cond 361 | ;; Return empty list if current buffer is sort-tab buffer. 362 | ((string-equal sort-tab-buffer-name (buffer-name current-buffer)) 363 | nil) 364 | (t 365 | ;; Show hide buffer at left when current buffer is match hidden rule. 366 | (when (sort-tab-is-hidden-buffer-p current-buffer) 367 | (add-to-list 'visible-buffer-infos current-buffer t)) 368 | 369 | ;; Don't sort tabs if using sort-tab commands. 370 | (unless (string-prefix-p "sort-tab-" (prin1-to-string last-command)) 371 | (setq sort-tab-visible-buffers (sort-tab-get-buffer-list))) 372 | 373 | (dolist (buf sort-tab-visible-buffers) 374 | (add-to-list 'visible-buffer-infos buf t)) 375 | 376 | visible-buffer-infos 377 | )))) 378 | 379 | (defun sort-tab-render-tabs (visible-buffer-infos current-buffer) 380 | (sort-tab-update-tabs 381 | (when visible-buffer-infos 382 | (let* ((current-tab-start-column 0) 383 | (current-tab-end-column 0) 384 | (tab-window (get-buffer-window (sort-tab-get-buffer))) 385 | (buffer-index -1) 386 | found-current-tab 387 | tab) 388 | ;; Calculate the current tab column. 389 | (dolist (buf visible-buffer-infos) 390 | ;; Insert tab. 391 | (setq buffer-index (+ buffer-index 1)) 392 | (setq tab (sort-tab-get-tab-name buf current-buffer buffer-index)) 393 | (insert-button tab 394 | 'action `(lambda (x) 395 | (sort-tab-select-visible-nth-tab ,(1+ buffer-index))) 396 | 'face '(:underline nil)) 397 | (insert sort-tab-propertized-separator) 398 | 399 | ;; Calculate the current tab column. 400 | (unless found-current-tab 401 | (when (eq buf current-buffer) 402 | (setq found-current-tab t) 403 | (setq current-tab-start-column current-tab-end-column)) 404 | (setq current-tab-end-column (+ current-tab-end-column (length tab) (length sort-tab-separator))))) 405 | 406 | ;; Adjust tab column when current buffer is minibuffer. 407 | (when (window-minibuffer-p) 408 | (setq current-tab-start-column 0) 409 | (setq current-tab-end-column 0)) 410 | 411 | ;; Make tab always visible. 412 | (when tab-window 413 | (with-selected-window tab-window 414 | (cond ((> current-tab-end-column (+ (window-hscroll) (window-width))) 415 | (scroll-left (+ (- current-tab-end-column (window-hscroll) (window-width)) (/ (window-width) 2)))) 416 | ((< current-tab-start-column (window-hscroll)) 417 | (set-window-hscroll tab-window current-tab-start-column)) 418 | ))))))) 419 | 420 | (defun sort-tab-update-list () 421 | ;; Debug usage. 422 | ;; (with-current-buffer (get-buffer-create "sort-tab-debug") 423 | ;; (goto-char (point-max)) 424 | ;; (insert (format "**** %s %s\n" 425 | ;; last-command 426 | ;; (buffer-name current-buffer)))) 427 | 428 | (when sort-tab-render-function 429 | (funcall sort-tab-render-function (sort-tab-get-visible-buffer-infos) 430 | (if (minibufferp) 431 | ;; Return buffer before enter in minibuffer. 432 | (window-buffer (minibuffer-selected-window)) 433 | (window-buffer))))) 434 | 435 | (defun sort-tab-get-tab-name (buf current-buffer &optional buffer-index) 436 | (propertize 437 | (format " %s%s " 438 | (if (or (not sort-tab-show-index-number) (not buffer-index) (> buffer-index 8)) 439 | "" 440 | (let ((show-numbers '("①" "②" "③" "④" "⑤" "⑥" "⑦" "⑧" "⑨"))) 441 | (concat (nth buffer-index show-numbers) " "))) 442 | (let ((bufname (buffer-name buf)) 443 | (ellipsis "...")) 444 | ;; We need remove space in web page title. 445 | (when (sort-tab-is-eaf-browser-buffer-p buf) 446 | (setq bufname (replace-regexp-in-string "\\s-" "" bufname))) 447 | 448 | (if (> (length bufname) sort-tab-name-max-length) 449 | (format "%s%s" (substring bufname 0 (- sort-tab-name-max-length (length ellipsis))) ellipsis) 450 | bufname))) 451 | 'face 452 | (if (eq buf current-buffer) 453 | 'sort-tab-current-tab-face 454 | 'sort-tab-other-tab-face))) 455 | 456 | (defun sort-tab-get-buffer-list () 457 | (sort (cl-remove-if 458 | #'sort-tab-buffer-need-hide-p 459 | (buffer-list)) 460 | #'sort-tab-buffer-freq-higher-p)) 461 | 462 | (defun sort-tab-get-index () 463 | (cl-position (window-buffer) sort-tab-visible-buffers :test #'eq)) 464 | 465 | (defun sort-tab-get-next-buffer () 466 | (let ((index (sort-tab-get-index))) 467 | (cond 468 | ((or (null index) (eq index (1- (length sort-tab-visible-buffers)))) 469 | (car sort-tab-visible-buffers)) 470 | (t 471 | (nth (1+ index) sort-tab-visible-buffers))))) 472 | 473 | (defun sort-tab-get-prev-buffer () 474 | (let ((index (sort-tab-get-index))) 475 | (cond 476 | ((or (null index) (eq index 0)) 477 | (car (last sort-tab-visible-buffers))) 478 | (t 479 | (nth (1- index) sort-tab-visible-buffers))))) 480 | 481 | (defun sort-tab-get-first-buffer () 482 | (cl-first sort-tab-visible-buffers)) 483 | 484 | (defun sort-tab-get-last-buffer () 485 | (car (last sort-tab-visible-buffers))) 486 | 487 | (defun sort-tab-select-prev-tab () 488 | (interactive) 489 | (switch-to-buffer (sort-tab-get-prev-buffer))) 490 | 491 | (defun sort-tab-select-next-tab () 492 | (interactive) 493 | (switch-to-buffer (sort-tab-get-next-buffer))) 494 | 495 | (defun sort-tab-select-first-tab () 496 | (interactive) 497 | (switch-to-buffer (sort-tab-get-first-buffer))) 498 | 499 | (defun sort-tab-select-last-tab () 500 | (interactive) 501 | (switch-to-buffer (sort-tab-get-last-buffer))) 502 | 503 | (defun sort-tab-close-current-tab-and-select-previous () 504 | (interactive) 505 | (let* ((buf (window-buffer))) 506 | (sort-tab-kill-buffer buf))) 507 | 508 | (defun sort-tab-close-current-tab () 509 | (interactive) 510 | (let* ((buf (window-buffer)) 511 | (prev-buffer (sort-tab-get-prev-buffer)) 512 | (next-buffer (sort-tab-get-next-buffer)) 513 | (last-buffer (sort-tab-get-last-buffer)) 514 | (is-last-buffer (eq buf last-buffer))) 515 | ;; Then kill current buffer. 516 | (sort-tab-kill-buffer buf) 517 | ;; Switch to previous buffer if current buffer is last buffer, 518 | ;; otherwise switch to next buffer. 519 | (if is-last-buffer 520 | (when (buffer-live-p prev-buffer) 521 | (switch-to-buffer prev-buffer)) 522 | (when (buffer-live-p next-buffer) 523 | (switch-to-buffer next-buffer)) 524 | ))) 525 | 526 | (defun sort-tab-close-all-tabs () 527 | (interactive) 528 | ;; Force redisplay, make sure `sort-tab-visible-buffers' return all visible tabs. 529 | (redisplay t) 530 | (let ((visible-buffers sort-tab-visible-buffers)) 531 | (setq sort-tab-visible-buffers nil) 532 | (mapc #'kill-buffer visible-buffers) 533 | )) 534 | 535 | (defun sort-tab-close-other-tabs () 536 | (interactive) 537 | ;; Force redisplay, make sure `sort-tab-visible-buffers' return all visible tabs. 538 | (let* ((current-buf (current-buffer)) 539 | (other-buffers (cl-remove-if (lambda (buf) (eq buf current-buf)) sort-tab-visible-buffers))) 540 | (setq sort-tab-visible-buffers '(current-buf)) 541 | (mapc #'kill-buffer other-buffers))) 542 | 543 | (defun sort-tab-close-mode-tabs () 544 | (interactive) 545 | (let ((modes (sort-tab-get-buffer-modes)) 546 | (current-mode (sort-tab-get-current-buffer-mode)) 547 | close-mode) 548 | (setq close-mode (completing-read (format "Close buffers match mode (%s): " current-mode) modes)) 549 | (when (string-equal close-mode "") 550 | (setq close-mode current-mode)) 551 | (dolist (buffer (buffer-list)) 552 | (with-current-buffer buffer 553 | (when (or (and (string-prefix-p "eaf:" close-mode) 554 | (eq major-mode 'eaf-mode) 555 | (string-equal eaf--buffer-app-name (cadr (split-string close-mode ":")))) 556 | (string-equal (prin1-to-string major-mode) close-mode)) 557 | (sort-tab-kill-buffer buffer) 558 | ))))) 559 | 560 | (defun sort-tab-kill-buffer (buffer) 561 | ;; Update `sort-tab-visible-buffers' first. 562 | (setq sort-tab-visible-buffers (delete buffer sort-tab-visible-buffers)) 563 | (kill-buffer buffer)) 564 | 565 | (defun sort-tab-get-buffer-modes () 566 | (let ((mode-table (make-hash-table :test 'equal)) 567 | modes) 568 | (dolist (buffer (buffer-list)) 569 | (with-current-buffer buffer 570 | (when (sort-tab-is-normal-buffer-p buffer) 571 | (puthash (sort-tab-get-current-buffer-mode) "" mode-table)))) 572 | (maphash (lambda (k v) (push k modes)) mode-table) 573 | modes)) 574 | 575 | (defun sort-tab-get-current-buffer-mode () 576 | (if (eq major-mode 'eaf-mode) 577 | (format "eaf:%s" eaf--buffer-app-name) 578 | (prin1-to-string major-mode))) 579 | 580 | (defun sort-tab-select-visible-nth-tab (&optional tab-index) 581 | (interactive "p") 582 | (switch-to-buffer (nth (1- tab-index) sort-tab-visible-buffers))) 583 | 584 | (defun sort-tab-select-visible-tab () 585 | (interactive) 586 | (let* ((event last-command-event) 587 | (key (make-vector 1 event)) 588 | (key-desc (key-description key))) 589 | (sort-tab-select-visible-nth-tab 590 | (string-to-number (car (last (split-string key-desc "-"))))))) 591 | 592 | (defun sort-tab-kill-buffer-advisor (orig-fun &optional arg &rest args) 593 | (if (equal (buffer-name) sort-tab-buffer-name) 594 | (message "sort-tab buffer can't be kill, please use `sort-tab-turn-off' command to quit sort-tab.") 595 | (apply orig-fun arg args))) 596 | 597 | (defun sort-tab-bury-buffer-advisor (&optional arg) 598 | (sort-tab-update-list)) 599 | 600 | (advice-add #'kill-buffer :around #'sort-tab-kill-buffer-advisor) 601 | (advice-add #'bury-buffer :after #'sort-tab-bury-buffer-advisor) 602 | (advice-add #'unbury-buffer :after #'sort-tab-update-list) 603 | 604 | (defun initialize-sort-tab-delay (&optional frame) 605 | (run-with-idle-timer 0 nil 'sort-tab-turn-on)) 606 | 607 | ;;;###autoload 608 | (define-minor-mode sort-tab-mode 609 | "Toggle display of a sort-tab. 610 | With prefix argument ARG, turn on if positive, otherwise off. 611 | Returns non-nil if the new state is enabled. 612 | 613 | \\{sort-tab-mode-map}" 614 | :group 'sort-tab 615 | :require 'sort-tab 616 | :global t 617 | :keymap sort-tab-mode-map 618 | (if sort-tab-mode 619 | (progn 620 | (sort-tab-turn-on) 621 | 622 | ;; Add hook for emacs daemon. 623 | (when (and (fboundp 'daemonp) (daemonp)) 624 | (add-hook 'after-make-frame-functions #'initialize-sort-tab-delay t))) 625 | (sort-tab-turn-off) 626 | 627 | ;; Remove hook for emacs daemon. 628 | (when (and (fboundp 'daemonp) (daemonp)) 629 | (remove-hook 'after-make-frame-functions #'initialize-sort-tab-delay) 630 | ))) 631 | 632 | (provide 'sort-tab) 633 | 634 | ;;; sort-tab.el ends here 635 | --------------------------------------------------------------------------------