├── .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 |
--------------------------------------------------------------------------------