«Вкладки» в стиле браузера для emacs?

22

Я хотел бы вкладки, как Firefox, но для Emacs.

Я нашел это: http://emacswiki.org/emacs/TabBarMode

Но он просто добавляет к каждому буферу ( окну в терминологии Emacs) панель, которая показывает открытые в данный момент буферы.

Мне бы хотелось, чтобы вкладка могла содержать несколько буферов ( окна в терминологии Emacs), которые я могу разделить по своему усмотрению. Т.е. каждая вкладка должна соответствовать «состоянию окна» (в смысле window-state-get).

У меня была бы одна вкладка для моих задач, другая для кода, другая для чтения в Интернете и т.д ..

Это возможно? Можно ли настроить вкладку для этого?

[edit2]
Этот вопрос привлек больше внимания, чем я ожидал. Похоже, что есть решение, но оно потребует некоторых исследований и доработок. Хотя эта / следующая неделя для меня немного занята, я разберу ответы и попытаюсь создать что-то, что будет работать, а затем отредактирую этот вопрос, чтобы отразить мои выводы. Пожалуйста, держись =)

[править]
Вроде как:
/programming/24157754/make-frames-in-emacs-gui-behaves-like-frames-in-terminal

Я бы согласился с несколькими кадрами в одной сессии GUI также.

Лев Уфимцев
источник
2
«Мне бы хотелось, чтобы вкладка могла содержать несколько буферов, которые я могу разделить по своему желанию». Вы имеете в виду несколько окон ?
Малабарба
1
Я бы хотел, чтобы у меня были очень динамичные вкладки. Я создал бы их и затем заполнил их окнами. Т.е. я бы хотел, чтобы вкладка была рамкой. Затем новая вкладка, новый кадр. Внутри каждой вкладки / фрейма я мог бы открыть нужные окна / (буферы). Это выполнимо? (Т. Е. Нет жестко запрограммированных имен буферов и т.д ..)
Лев Уфимцев
1
Существует переменная, связанная с конкретными окнами, но прошел месяц или два с тех пор, как я увидела поток, в котором говорилось об этом, и я не знаю, как это называется. Возможно, вас заинтересует использование системы, аналогичной frame-bufs, в которой список содержит буферы, связанные с кадром, и этот список включается в параметр frame. Вы можете использовать переменную, связанную с конкретным окном, и сделать ее списком, добавить / удалить буферы из списка - этот список будет группой буферов, которую будет использовать панель вкладок. Это все теоретически, но я верю, что это сработает.
юрист
1
Я думаю, что вы можете сослаться на: stackoverflow.com/questions/24157754/… но этот пост, кажется, не имеет твердого ответа: - /
Лев Уфимцев
1
Я бы порекомендовал взглянуть на пакет elscreen.
blarghmatey

Ответы:

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. Что касается общих буферов, я предпочитаю ставить в «Common» каждый буфер, имя которого начинается со звезды. Это дает пример создания буфера для этого правила:
    ((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. Вот пример группировки вкладок по регулярному выражению:
    ((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-commonи tabbar-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))))))))))
саман
источник
Спасибо за подробный ответ. Я постараюсь опробовать вышесказанное и сообщу вам ~ в конце концов :)
Лев Уфимцев
Но 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-bufferи my-remove-buffer. Для примера того, как открыть определенные буферы в выбранных кадрах, см. Связанный поток, озаглавленный Как перехватить файл перед его открытием, и решить, какой кадр : /programming//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) различных лица в зависимости от ситуации. Если вкладка / буфер связан с фреймом и выбран IS, тогда используйте tabbar-selected-associatedface. Если вкладка / буфер связана с рамкой и НЕ выбрана, тогда используйте tabbar-unselected-associatedлицо. Если вкладка / буфер НЕ связана с фреймом и выбрана IS, тогда используйте tabbar-selected-unassociatedface. Если вкладка / буфер НЕ связана с фреймом и НЕ выбрана, используйте tabbar-unselected-unassociatedface.

;; 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[синие и красные вкладки], с заглавной буквой «N», обозначенной в строке режима.

пример

lawlist
источник
Но OP не хочет «одну панель вкладок на окно», он хочет одну панель вкладок на кадр, и каждая вкладка на панели вкладок должна представлять «конфигурацию окна» (т.е. несколько окон), а не буфер.
Стефан
5

Попробуйте проверить elscreen , хотя на самом деле он не группирует буферы.

Что он делает, так это группирует окна и предоставляет доступ к нескольким макетам (вкладкам), между которыми вы можете быстро перемещаться. Мой рабочий процесс часто содержит некоторый Ruby-код и связанные тесты на одном экране, в то время как мои заметки todo и Org находятся на другом, и, возможно, рабочий буфер для составления запросов SQL находится на третьем. Это позволяет мне легко переключаться между задачами и проектами, даже если каждый экран использует один и тот же пул буферов.

Р.П. Диллон
источник
3

Как насчет моего плагина, кентавр-вкладок? У него много опций конфигурации, он действительно функционален, поддерживается очень популярными темами, такими как Kaolin Themes, и в целом представляет собой действительно красивый и эстетичный пакет (согласно отзывам пользователей). Он доступен в MELPA и выглядит так:

введите описание изображения здесь

Эммануэль Бустос
источник
Похоже, это еще одна «каждая вкладка представляет буфер, а каждое окно имеет свою собственную панель вкладок» - не решение.
Стефан
Я просто добавил настройку для отображения групп вкладок вместо имен вкладок, чтобы в функции вы устанавливали правила (то есть группы elisp и lisp в одной группе, группы c и c ++ в другой и т. Д.) И на вкладках эти группы отображались.
Эммануэль Бустос
Это все еще не отвечает на вопрос, где должна быть одна панель вкладок на кадр (а не на окно), и каждая вкладка представляет конфигурацию окна.
Стефан
Хорошо, я не поняла. Я буду исследовать и работать над этим!
Эммануэль Бустос
0

Вот мой конфиг, для чего стоит. Особенности:

  • Работа мыши на вкладках ( mouse-2чтобы закрыть, как в браузерах , mouse-3 чтобы открыть в новом окне Emacs, как в i3 )
  • Управление с клавиатуры ( M-leftи вкладки правого переключателя, как в TMux / Screen )
  • Цвета (в соответствии с конфигом «Моя тема / moe-dark»)
  • Группировка (на данный момент Emacs *buffers*и "обычная")
  • Автообновляется (с использованием пакета )

TabBar

(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 - Тема Мо

(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>")) 
yPhil
источник