emacs的浏览器样式“标签”?


22

我想要像firefox这样的选项卡,但需要emacs。

我发现了这一点:http : //emacswiki.org/emacs/TabBarMode

但是它只是向每个缓冲区(Emacs术语中的窗口)添加了一个显示当前打开的缓冲区的栏。

我希望选项卡能够容纳多个缓冲区(Emacs术语中的窗口),可以根据需要拆分这些缓冲区。也就是说,每个标签都应对应一个“窗口状态”(在的意义上window-state-get)。

我有一个选项卡用于执行任务,另一个选项卡用于代码,另一个选项卡用于网络阅读等。

这可能吗?可以定制Tabbar来做到这一点吗?

[edit2]
这个问题比我预期的吸引了更多关注。看起来有一种解决方案,但是需要一些研究和调整。尽管本周/下周对我来说有点忙,但我将分析答案并尝试构建可行的方法,然后我将编辑此问题以反映我的发现。请坚持=)

[edit]
类似于:https :
//stackoverflow.com/questions/24157754/make-frames-in-emacs-gui-behaves-like-frames-in-terminal

我也将在单个GUI会话中处理多个框架。


2
“我希望选项卡能够容纳多个缓冲区,可以根据需要拆分这些缓冲区。” 您是说多个窗口吗?
Malabarba

1
这更像是我想要非常动态的标签。我将创建它们,然后用Windows填充它们。也就是说,我希望将标签设为框架。然后在新标签页上新建一个框架。在每个选项卡/框架内,我可以打开所需的窗口/(缓冲区)。这可行吗?(即,没有硬编码的缓冲区名称等。)
Leo Ufimtsev

1
有一个与特定窗口相关联的变量,但是已经有一个月或两个月了,因为我看到一个讨论该变量的线程,但我不知道它被称为副手。您可能对使用类似于frame-bufs的系统感兴趣,在该系统中,列表包含与帧关联的缓冲区,并且该列表已合并到frame参数中。您可以使用与特定窗口关联的变量并将其设为列表,从列表中添加/删除缓冲区-该列表将是选项卡使用的缓冲区组。这全是理论上的,但我相信它会起作用。
法律学家

1
我认为您可以参考:stackoverflow.com/questions/24157754/… 但该帖子似乎没有明确的答案:-/
Leo Ufimtsev

1
我建议您看一下elscreen软件包。
blarghmatey

Answers:


8

分组拆分缓冲区

标签栏是可能的。您可以将规则添加到组中的组缓冲区中。这是一个基本片段:

(defun tabbar-buffer-groups ()
  "Returns the list of group names the current buffer belongs to."
  (list
   (cond

    ;; ADD RULES TO SPLIT BUFFERS IN GROUPS HERE!

    ;; if buffer is not grouped by the rules you would add above 
    ;; put it in the "General" group:
    (t
       "General"
     ))))

规则示例:

  1. 列出缓冲区名称:
    ((member (buffer-name)
             '("*scratch*" "*Messages*" "*Help*"))
     "Common" ;; this is a group name
     )
  1. 关于公共缓冲区,我更喜欢在每个以“星号”开头的缓冲区中放入“公共”。这给出了为该规则创建缓冲区的示例:
    ((string-equal "*" (substring (buffer-name) 0 1))
     "Common"
     )
  1. 以下是按主要模式对缓冲区进行分组的示例:
    ((memq major-mode
           '(org-mode text-mode rst-mode))
     "Text"
     )
  1. 这是根据从中派生的模式对缓冲区进行分组的示例:
    ((or (get-buffer-process (current-buffer))
         ;; Check if the major mode derives from `comint-mode' or
         ;; `compilation-mode'.
         (tabbar-buffer-mode-derived-p
          major-mode '(comint-mode compilation-mode)))
     "Process"
     )
  1. 这是通过regexp对标签进行分组的示例:
    ((string-match "^__" (buffer-name))
     "Templates"
     )
  1. 按主要模式对缓冲区进行分组:
    (if (and (stringp mode-name)
                  ;; Take care of preserving the match-data because this
                  ;; function is called when updating the header line.
                  (save-match-data (string-match "[^ ]" mode-name)))
             mode-name
           (symbol-name major-mode))

组成规则后,您可以在标签栏的标签线上按+或-切换组,也可以按◀和▶在缓冲区之间切换。或者只是绑定以下defuns:

tabbar-forward
tabbar-backward
tabbar-forward-group
tabbar-backward-group

并使用键盘在选项卡和选项卡组之间移动。

我个人将标签分组,以便查看打开的内容,但使用进行导航ido-switch-buffer

在一组规则之间切换

也可以定义一组不同的缓冲区分组规则,并在它们之间循环。这是在两组缓冲区分组规则之间循环的示例:

;; tab-groups!
(setq tbbr-md "common")
(defun toggle-tabbar-mode ()
  "Toggles tabbar modes - all buffers vs. defined in the `tabbar-buffer-groups'."
  (interactive)
  (if (string= tbbr-md "groups")
      (progn ;; then
        (setq tabbar-buffer-groups-function 'tabbar-buffer-groups-common)
        (setq tbbr-md "common"))
    (progn ;; else
      (setq tabbar-buffer-groups-function 'tabbar-buffer-groups)
      (setq tbbr-md "groups"))))
;; by default - all tabs:
(setq tabbar-buffer-groups-function 'tabbar-buffer-groups-common)

这会在tabbar-buffer-groups-commontabbar-buffer-groups选项卡分组定义之间切换。

按名称对标签栏缓冲区排序

我发现按名称对标签栏缓冲区进行排序很有益。取得方法如下:

(defun tabbar-add-tab (tabset object &optional append_ignored)
  "Add to TABSET a tab with value OBJECT if there isn't one there yet.
If the tab is added, it is added at the beginning of the tab list,
unless the optional argument APPEND is non-nil, in which case it is
added at the end."
  (let ((tabs (tabbar-tabs tabset)))
    (if (tabbar-get-tab object tabset)
        tabs
      (let ((tab (tabbar-make-tab object tabset)))
        (tabbar-set-template tabset nil)
        (set tabset (sort (cons tab tabs)
                          (lambda (a b) (string< (buffer-name (car a)) (buffer-name (car b))))))))))

感谢您的详细回答。我将尝试尝试上述方法,最终让您知道:)
Leo Ufimtsev

但是OP不想“每个窗口一个标签栏”,他希望每帧一个标签栏,并且标签栏中的每个标签都应该代表一个“窗口配置”(即几个窗口),而不是一个缓冲区,因此对缓冲区进行分组不是问题。
斯特凡

6

属性:在每个帧的基础上对缓冲区进行分组是对概念的直接实现,并选择由Alp Aker在库frame-bufs中开发/编写的代码部分:https : //github.com/alpaker/Frame-Bufs

以下是一个示例,该示例说明如何通过tabbar.el在的基础上动态添加库和组选项卡/缓冲区,方法是添加C-c C-a或来删除选项卡/缓冲区C-c C-n。只有两(2)组-与当前帧(即"A")相关联,而与当前帧(即"N")不相关联。这些组是局部帧的,这意味着每个帧都可以有自己的分组。可以使用重置自定义分组C-c C-r。使用可以在关联和非关联组之间切换C-tab。使用切换到当前组中的下一个选项卡/缓冲区M-s-right。使用切换到当前组中的上一个选项卡/缓冲区M-s-left

选项卡/缓冲器可以被添加或去除编程my-add-buffermy-remove-buffer。有关如何在选定帧中打开某些缓冲区的示例,请参见名为“ 如何在文件打开前拦截文件并确定是哪个帧”的相关线程:https : //stackoverflow.com/a/18371427/2112489 该函数my-add-buffer需要如果用户选择实现该功能,则将其并入以上链接中代码的适当位置。

用户可能希望mode-line-format通过合并以下代码段在自定义中创建一个条目,以在模式行中显示当前选项卡组的名称: (:eval (when tabbar-mode (format "%s" (tabbar-current-tabset t)))) 但是,更详细地自定义模式行不在本示例的范围之内。

该功能tabbar-add-tab已被修改,以便按字母顺序排列选项卡/缓冲区。

对该功能tabbar-line-tab进行了修改,以便根据情况提供四(4)个不同的面孔。如果选项卡/缓冲区与框架关联并且被选中,则使用tabbar-selected-associated面部。如果选项卡/缓冲区与框架关联并且未被选中,则使用tabbar-unselected-associated面部。如果选项卡/缓冲区未与框架相关联且未选中,则使用tabbar-selected-unassociated面部。如果选项卡/缓冲区未与框架关联并且未被选中,则使用tabbar-unselected-unassociated面部。

;; Download tabbar version 2.0.1 by David Ponce:
;;   https://marmalade-repo.org/packages/tabbar
;; or use package-install for marmalade repositories.

;; Place tabbar-2.0.1.el in the `load-path` -- it is okay to rename it to tabbar.el
;; or add the directory (where `tabbar.el` resides) to the `load-path`.
;; EXAMPLE:  (setq load-path (append '("/Users/HOME/.emacs.d/lisp/") load-path))

(require 'tabbar)

(setq tabbar-cycle-scope 'tabs)

(remove-hook 'kill-buffer-hook 'tabbar-buffer-track-killed)

(defun my-buffer-groups ()
  "Function that gives the group names the current buffer belongs to.
It must return a list of group names, or nil if the buffer has no
group.  Notice that it is better that a buffer belongs to one group."
  (list
    (cond
      ((memq (current-buffer) (my-buffer-list (selected-frame)))
        "A")
      (t
        "N"))))

(setq tabbar-buffer-groups-function 'my-buffer-groups) ;; 'tabbar-buffer-groups

;; redefine tabbar-add-tab so that it alphabetizes / sorts the tabs
(defun tabbar-add-tab (tabset object &optional append)
  "Add to TABSET a tab with value OBJECT if there isn't one there yet.
If the tab is added, it is added at the beginning of the tab list,
unless the optional argument APPEND is non-nil, in which case it is
added at the end."
  (let ((tabs (tabbar-tabs tabset)))
    (if (tabbar-get-tab object tabset)
        tabs
      (let* ((tab (tabbar-make-tab object tabset))
             (tentative-new-tabset
               (if append
                 (append tabs (list tab))
                 (cons tab tabs)))
             (new-tabset
               (sort
                  tentative-new-tabset
                  #'(lambda (e1 e2)
                     (string-lessp
                       (format "%s" (car e1)) (format "%s" (car e2)))))))
        (tabbar-set-template tabset nil)
        (set tabset new-tabset)))))

;; AUTHOR:  Alp Aker -- https://github.com/alpaker/Frame-Bufs
;; @lawlist extracted/revised the function(ality) from said library.
(defun my-buffer-list (frame)
  ;; Remove dead buffers.
  (set-frame-parameter frame 'frame-bufs-buffer-list
    (delq nil (mapcar #'(lambda (x) (if (buffer-live-p x) x))
      (frame-parameter frame 'frame-bufs-buffer-list))))
  ;; Return the associated-buffer list.
  (frame-parameter frame 'frame-bufs-buffer-list))

(defun my-kill-buffer-fn ()
"This function is attached to a buffer-local `kill-buffer-hook'."
  (let ((frame (selected-frame))
        (current-buffer (current-buffer)))
    (when (memq current-buffer (my-buffer-list frame))
      (my-remove-buffer current-buffer frame))))

;; AUTHOR:  Alp Aker -- https://github.com/alpaker/Frame-Bufs
;; @lawlist extracted/revised the function(ality) from said library.
(defun my-add-buffer (&optional buf frame)
"Add BUF to FRAME's associated-buffer list if not already present."
(interactive)
  (let* ((buf (if buf buf (current-buffer)))
         (frame (if frame frame (selected-frame)))
         (associated-bufs (frame-parameter frame 'frame-bufs-buffer-list)))
    (unless (bufferp buf)
      (signal 'wrong-type-argument (list 'bufferp buf)))
    (unless (memq buf associated-bufs)
      (set-frame-parameter frame 'frame-bufs-buffer-list (cons buf associated-bufs)))
    (with-current-buffer buf
      (add-hook 'kill-buffer-hook 'my-kill-buffer-fn 'append 'local))
    (when tabbar-mode (tabbar-display-update))))

;; AUTHOR:  Alp Aker -- https://github.com/alpaker/Frame-Bufs
;; @lawlist extracted/revised the function(ality) from said library.
(defun my-remove-buffer (&optional buf frame)
"Remove BUF from FRAME's associated-buffer list."
(interactive)
  (let ((buf (if buf buf (current-buffer)))
        (frame (if frame frame (selected-frame))))
    (set-frame-parameter frame 'frame-bufs-buffer-list
      (delq buf (frame-parameter frame 'frame-bufs-buffer-list)))
    (when tabbar-mode (tabbar-display-update))))

;; AUTHOR:  Alp Aker -- https://github.com/alpaker/Frame-Bufs
;; @lawlist extracted/revised the function(ality) from said library.
(defun my-buffer-list-reset ()
    "Wipe the entire slate clean for the selected frame."
  (interactive)
    (modify-frame-parameters (selected-frame) (list (cons 'frame-bufs-buffer-list nil)))
    (when tabbar-mode (tabbar-display-update)))

(defun my-switch-tab-group ()
"Switch between tab group `A` and `N`."
(interactive)
  (let ((current-group (format "%s" (tabbar-current-tabset t)))
        (tab-buffer-list (mapcar
            #'(lambda (b)
                (with-current-buffer b
                  (list (current-buffer)
                        (buffer-name)
                        (funcall tabbar-buffer-groups-function))))
                 (funcall tabbar-buffer-list-function))))
    (catch 'done
      (mapc
        #'(lambda (group)
            (when (not (equal current-group
                          (format "%s" (car (car (cdr (cdr group)))))))
              (throw 'done (switch-to-buffer (car (cdr group))))))
        tab-buffer-list))))

(defface tabbar-selected-associated
  '((t :background "black" :foreground "yellow" :box (:line-width 2 :color "yellow")))
  "Face used for the selected tab -- associated with the `frame-bufs-buffer-list`."
  :group 'tabbar)

(defface tabbar-unselected-associated
  '((t :background "black" :foreground "white" :box (:line-width 2 :color "white")))
  "Face used for unselected tabs  -- associated with the `frame-bufs-buffer-list`."
  :group 'tabbar)

(defface tabbar-selected-unassociated
  '((t :background "black" :foreground "white" :box (:line-width 2 :color "firebrick")))
  "Face used for the selected tab -- UNassociated with the `frame-bufs-buffer-list`."
  :group 'tabbar)

(defface tabbar-unselected-unassociated
  '((t :background "black" :foreground "white" :box (:line-width 2 :color "blue")))
  "Face used for unselected tabs -- UNassociated with the `frame-bufs-buffer-list`."
  :group 'tabbar)

(setq tabbar-background-color "black")

(defsubst tabbar-line-tab (tab)
  "Return the display representation of tab TAB.
That is, a propertized string used as an `header-line-format' template
element.
Call `tabbar-tab-label-function' to obtain a label for TAB."
  (concat
    (propertize
      (if tabbar-tab-label-function
          (funcall tabbar-tab-label-function tab)
        tab)
      'tabbar-tab tab
      'local-map (tabbar-make-tab-keymap tab)
      'help-echo 'tabbar-help-on-tab
      'mouse-face 'tabbar-highlight
      'face
        (cond
          ((and
              (tabbar-selected-p tab (tabbar-current-tabset))
              (memq (current-buffer) (my-buffer-list (selected-frame))))
            'tabbar-selected-associated)
          ((and
              (not (tabbar-selected-p tab (tabbar-current-tabset)))
              (memq (current-buffer) (my-buffer-list (selected-frame))))
            'tabbar-unselected-associated)
          ((and
              (tabbar-selected-p tab (tabbar-current-tabset))
              (not (memq (current-buffer) (my-buffer-list (selected-frame)))))
            'tabbar-selected-unassociated)
          ((and
              (not (tabbar-selected-p tab (tabbar-current-tabset)))
              (not (memq (current-buffer) (my-buffer-list (selected-frame)))))
            'tabbar-unselected-unassociated))
      'pointer 'hand)
    tabbar-separator-value))

(define-key global-map "\C-c\C-r" 'my-buffer-list-reset)

(define-key global-map "\C-c\C-a" 'my-add-buffer)

(define-key global-map "\C-c\C-n" 'my-remove-buffer)

(define-key global-map (kbd "<M-s-right>") 'tabbar-forward)

(define-key global-map (kbd "<M-s-left>") 'tabbar-backward)

(define-key global-map [C-tab] 'my-switch-tab-group)

(tabbar-mode 1)

以下屏幕快照描述了两种可能的缓冲区/选项卡分组:(1)左侧是与名为SYSTEM[黄色和白色选项卡] 的框架相关联的那些缓冲区/选项卡的分组,大写字母“ A”表示在模式行 (2)右边是与名为SYSTEM[blue and red tabs] 的框架相关联的那些缓冲区/选项卡的组合,在模式行中用大写字母“ N”表示。

例


但是OP不需要“每个窗口一个标签栏”,他希望每一个标签栏,并且标签栏中的每个标签都应该代表“窗口配置”(即几个窗口),而不是缓冲区。
斯特凡

5

考虑签出elscreen,尽管实际上并没有对缓冲区进行分组。

它的作用是对窗口进行分组,并提供对可以快速切换的多个布局(选项卡)的访问。我的工作流通常在一个屏幕上有一些Ruby代码和相关的测试,而我的todo和Org注释在另一个屏幕上,也许草拟SQL查询的暂存缓冲区在第三屏幕中。即使每个屏幕都使用相同的缓冲池,这也使我可以轻松地在任务和项目之间切换。


3

我的插件centaur-tabs呢?它具有许多配置选项,它确实具有功能性,并受到非常受欢迎的主题(例如高岭土主题)的支持,并且总体来说是一个非常漂亮且美观的软件包(根据用户的反馈)。它在MELPA中可用,看起来像这样:

在此处输入图片说明


这似乎是另一个“非选项卡,每个选项卡代表一个缓冲区,每个窗口都有其自己的选项卡”。
斯特凡

我只是添加了一个自定义项来显示选项卡组而不是选项卡名称,因此您可以在函数中建立规则(即,一组中的elisp和lisp,另一组中的c和c ++组,依此类推),然后在选项卡中显示这些组。
伊曼纽尔·布斯托斯

仍然没有回答这个问题,每(而不是每个窗口)应该有一个标签栏,每个标签代表一个窗口配置。
斯特凡

好吧,我没有不明白。我将进行调查并继续努力!
Emmanuel Bustos

0

这是我的配置,物有所值。它具有以下特点:

  • 在标签(鼠标操作mouse-2接近,像浏览器mouse-3 弹出打开新Emacs窗口,就像在I3
  • 键盘控件(M-left和右侧开关选项卡,如TMux / Screen中的
  • 颜色(与“ Moe主题/ moe-dark”配置一致)
  • 分组(当前为Emacs *buffers*和“常规”)
  • 自动更新(带有use-package

标签栏

(use-package tabbar
  :ensure t
  :bind
  ("<M-left>" . tabbar-backward)
  ("<M-right>" . tabbar-forward)

  :config
  (set-face-attribute
   'tabbar-button nil
   :box '(:line-width 1 :color "gray19"))

  (set-face-attribute
   'tabbar-selected nil
   :foreground "orange"
   :background "gray19"
   :box '(:line-width 1 :color "gray19"))

  (set-face-attribute
   'tabbar-unselected nil
   :foreground "gray75"
   :background "gray25"
   :box '(:line-width 1 :color "gray19"))

  (set-face-attribute
   'tabbar-highlight nil
   :foreground "black"
   :background "orange"
   :underline nil
   :box '(:line-width 1 :color "gray19" :style nil))

  (set-face-attribute
   'tabbar-modified nil
   :foreground "orange red"
   :background "gray25"
   :box '(:line-width 1 :color "gray19"))

  (set-face-attribute
   'tabbar-selected-modified nil
   :foreground "orange red"
   :background "gray19"
   :box '(:line-width 1 :color "gray19"))

  (custom-set-variables
   '(tabbar-separator (quote (0.2))))

  (defun tabbar-buffer-tab-label (tab)
    "Return a label for TAB.
  That is, a string used to represent it on the tab bar."
    (let ((label  (if tabbar--buffer-show-groups
                      (format " [%s] " (tabbar-tab-tabset tab))
                    (format " %s " (tabbar-tab-value tab)))))
      (if tabbar-auto-scroll-flag
          label
        (tabbar-shorten
         label (max 1 (/ (window-width)
                         (length (tabbar-view
                                  (tabbar-current-tabset)))))))))

  (defun px-tabbar-buffer-select-tab (event tab)
    "On mouse EVENT, select TAB."
    (let ((mouse-button (event-basic-type event))
          (buffer (tabbar-tab-value tab)))
      (cond
       ((eq mouse-button 'mouse-2) (with-current-buffer buffer (kill-buffer)))
       ((eq mouse-button 'mouse-3) (pop-to-buffer buffer t))
       (t (switch-to-buffer buffer)))
      (tabbar-buffer-show-groups nil)))

  (defun px-tabbar-buffer-help-on-tab (tab)
    "Return the help string shown when mouse is onto TAB."
    (if tabbar--buffer-show-groups
        (let* ((tabset (tabbar-tab-tabset tab))
               (tab (tabbar-selected-tab tabset)))
          (format "mouse-1: switch to buffer %S in group [%s]"
                  (buffer-name (tabbar-tab-value tab)) tabset))
      (format "\
mouse-1: switch to %S\n\
mouse-2: kill %S\n\
mouse-3: Open %S in another window"
              (buffer-name (tabbar-tab-value tab))
              (buffer-name (tabbar-tab-value tab))
              (buffer-name (tabbar-tab-value tab)))))

  (defun px-tabbar-buffer-groups ()
    "Sort tab groups."
    (list (cond ((or
                  (eq major-mode 'dired-mode)
                  (string-equal "*" (substring (buffer-name) 0 1))) "emacs")
                (t "user"))))
  (setq tabbar-help-on-tab-function 'px-tabbar-buffer-help-on-tab
        tabbar-select-tab-function 'px-tabbar-buffer-select-tab
        tabbar-buffer-groups-function 'px-tabbar-buffer-groups)

  :init
  (tabbar-mode 1))

附件1-Moe主题

(use-package moe-theme
  :ensure t
  :config
  (progn
    (load-theme 'moe-dark :no-confirm)
    (set-face-attribute 'fringe nil :background "gray19")))

附件2-切换最后2个缓冲区(KB宏)

(define-key global-map [(control tab)] (kbd "C-x b <return>")) 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.