有没有办法在 emacs 实现 vscode 那样的 tab 分组方式?

我的感觉是:

  • tab 鼠标/GUI 友好;helm/ivy 键盘/终端友好。

  • tab 用视觉定位,所以相邻 tabs 切换很直观,但相隔远了按键跟不上眼睛,需要鼠标操作;helm/ivy 用关键字定位,所以不惧远近,但也可以免搜索一键切换前/后/最近的 buffer。

  • 用 tab "被迫"养成控制 tabs 数量的习惯;用 helm/ivy 则眼不见为净。

  • tab 排列顺序跟文件打开先后有关,可以手动辅助调整;helm/ivy 则会根据使用频度来调整 buffer 顺序(如果 tab 也根据频度调整,视觉感受估计不太好)

综合各位的观点,我打算把 tab 纳入配置。这样当我单手操作/只读文件的时候,可以用 tab 来切换(以前都是开一个 sublime text)。不过无论 awesome-tab 还是 centaur-tabs 都跟 helm 冲突(ema2159/centaur-tabs#110),M-x 一片空白:

image

3 个赞

你可以用snails😏

哇,这个看着超厉害的。我现在使用 doom emacs 习惯使用 ivy,之前试过切换到 embark 就费了好大劲,最后还是不爽又换回来了。没有勇气换 snails。。

snails是重新实现的helm简化版,只专注于搜索和回车,没有多余的乱七八糟功能,性能也好,也好维护。

用舒服就一直舒服,不会像helm那样配置复杂,最后没法维护。

它可以像vscode那样性感弹出小窗口,也可以像helm那样弹出底部窗口。

积习难改,等 helm 彻底凉了再说。

可以用 tab-line-mode,27在之后自带功能,我这个性能不错,比较轻量。 如果括号不匹配还请见谅:

(use-package tab-bar
  :if(> emacs-major-version 26)
  :config
  (require 'tab-line)
  (require 'projectile)
  (tab-bar-mode -1)
  (global-tab-line-mode t)

  ;; set a local variable to boost performance
  (defvar buffer-project-name "~")
  (make-local-variable 'buffer-project-name)
  (defun my/update-buf-proj-name()
  (if (projectile-project-p)
  (setq-local buffer-project-name (projectile-project-name))
  (setq-local buffer-project-name "other")))
  ;; update local variable when major mode changes
  (add-hook 'after-change-major-mode-hook 'my/update-buf-proj-name)

  (defun my/tab-line-buffer-group (buffer)
   (interactive)
   (with-current-buffer buffer
   (cond
    ((s-match "scratch"  (buffer-name)) "Scratch")
    ((string= major-mode "eshell-mode") "Eshell")
    ((string= major-mode "dired-mode") "Dir")
    ((string= major-mode "ranger-mode") "Dir")
    ((string= major-mode "vterm-mode") "vTerm")
    ((s-match "Customize"  (buffer-name)) "Customize")
    ((s-match "magit:"  (buffer-name)) "Magit")
    ((s-match "magit "  (buffer-name)) "Magit others")
    ((s-match "magit-"  (buffer-name)) "Magit others")
    ((string= (s-left 1 (buffer-name)) "*") "Utilities")
    ;; group buffers using projectile
    ((boundp 'buffer-project-name) buffer-project-name)
    (major-mode (format-mode-line mode-name))
    (t "other"))))
  (setq tab-line-tabs-buffer-group-function #'my/tab-line-buffer-group)
  (setq tab-line-tabs-function #'tab-line-tabs-buffer-groups)
)
3 个赞

好像写了点代码配合 persp-mode 基本实现了我的需要。。先凑合用着吧。。 写的时候发现这个 centaur-tabs-buffer-groups 居然是不停的运行的。。我移动光标他也执行。。难怪会慢。。。还好我也没多少逻辑。。

  (defun wd/get-buffer-persp-group (buffer)
    (let* ((name))
      (dolist (persp (persp-persps))
        (if persp
            (if (persp-contain-buffer-p buffer persp)
                (setq name (safe-persp-name persp)))))
      name
      ))

  (defun centaur-tabs-buffer-groups ()
    (list
     (cond
      ((or (string-equal "*" (substring (buffer-name) 0 1))
         (memq major-mode '(magit-process-mode
                            magit-status-mode
                            magit-diff-mode
                            magit-log-mode
                            magit-file-mode
                            magit-blob-mode
                            magit-blame-mode
                            )))
     "Emacs")
    ;; ((derived-mode-p 'eshell-mode)
    ;;  "EShell")
    ;; ((derived-mode-p 'emacs-lisp-mode)
    ;;  "Elisp")
    ;; ((derived-mode-p 'dired-mode)
    ;;  "Dired")
    ((memq major-mode '(org-mode org-agenda-mode diary-mode))
     "OrgMode")
    (t
     (let ((name (wd/get-buffer-persp-group (current-buffer))))
       (if name
           name
         ;;(centaur-tabs-get-group-name (current-buffer))
         "Other"
         )
       )))))

我是把window当tab用的,在一个显示器/frame里面,合适的window数是1~4,最多4个可以满足大概70%的需求,短时间内不用切换buffer,剩下的情况就只能切了。

好处是,在这70%的情况里,我可以直接看buffer内容来选择一个buffer/window,这不比看tab条或ivy的buffer列表香?

分window的问题是每个window相对于只有一个的window要小一些,我觉得 [偶尔maximize再返回] 能无痛解决这个问题。

;; from https://gist.github.com/3402786
(defun toggle-maximize-window ()
  "Maximize window."
  (interactive)
  (if (and (= 1 (length (window-list)))
           (assoc ?_ register-alist))
      (jump-to-register ?_)
    (progn
      (window-configuration-to-register ?_)
      (delete-other-windows))))

当然还要保存window layout,eyebrowse简单好用,persp我记得有“不同的workspace能看到的open buffer是不同的”功能,很反直觉。

2 个赞

文件多了咋搞? 每个窗口都很小啊。

@JJPandari 有一个 GitHub - emacsorphanage/zoom-window: Zoom and Unzoom window ,你或许可以看看。可以缩放的同时改 mode line 颜色。

@manateelazycat 应该是只需要显示器够大就行。。。 。。。

讲道理在写一个局部的功能的时候(20分钟),4个window应该足够了,但是工作中会碰到项目的convention是一个功能要拆好几个文件的情况(硬要mvc,硬要interface+class),我有两种勉强能避免不断调起ivy的practice:

  • 使用alternate-buffer-back-and-forth命令,把比如.model.sql文件贴到一个window的正反两面,这样我一个frame的buffer容量能增加到4*2=8个。
  • 开两个window layout,SPC l TAB来回切,适用于“抄个项目,被抄的项目放一个layout,我的抄出来的放一个”这种情况。
(defun alternate-buffer (&optional window)
  "Switch back and forth between current and last buffer in the
current window."
  (interactive)
  (let ((current-buffer (window-buffer window)))
    ;; if no window is found in the windows history, `switch-to-buffer' will
    ;; default to calling `other-buffer'.
    (switch-to-buffer
     (cl-find-if (lambda (buffer)
                   (not (eq buffer current-buffer)))
                 (mapcar #'car (window-prev-buffers window))))))
3 个赞