"Вкладки" веб-переглядача для 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, де список містить буфери, пов'язані з кадром, і цей список включений у параметр кадру. Ви можете використовувати змінну, пов’язану з певним вікном, і зробити її списком, додати / видалити буфери зі списку - цей список був би буферною групою, яку використовуватимуть панелі вкладок. Це все теоретично, але я вважаю, що це спрацювало б.
законник

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. Щодо загальних буферів, я вважаю за краще вставити в "Загальний" кожен буфер, назва якого починається зірочкою. Це дає приклад створення буфера для цього правила:
    ((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))

Після складання правил ви можете натиснути на + або - на вкладці рядка вкладки для перемикання груп, а також ◀ і ▶ для перемикання між буферами. Або просто прив’яжіть наступні порушення:

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))))))))))

Дякую за детальну відповідь. Я спробую перейти до випробування вищесказаного і повідомляю вас ~ зрештою :)
Лев Уфімцев

Але ОП не хоче "однієї панелі вкладок на вікно", він хоче одну панель вкладки на кадр, і кожна вкладка на панелі вкладок повинна представляти "конфігурацію вікон" (тобто декілька вікон), а не буфер, тому групування буферів не є проблемою. .
Стефан

6

УВАГА: Групування буферів на основі кадру - це безпосередня реалізація концепцій та вибору частин коду, розроблених / написаних Альпом Акером у бібліотеці кадрів-буфів: 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) різні грані залежно від ситуації. Якщо вкладка / буфер пов’язана з кадром та вибрано ІС, то використовуйте tabbar-selected-associatedобличчя. Якщо вкладка / буфер пов’язана з кадром, а НЕ вибрана, використовуйте tabbar-unselected-associatedобличчя. Якщо вкладка / буфер НЕ асоціюється з кадром і вибрано IS, використовуйте 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[жовті та білі вкладки], з великої літери "А", зазначеної у режим-лінія; і (2) праворуч - це група тих буферів / вкладок, які НЕ асоціюються з кадром з ім'ям SYSTEM[сині та червоні вкладки], з великої літери "N", зазначеної в рядку режиму.

Приклад


Але ОП не хоче "одну панель вкладки на вікно", він хоче одну панель вкладки на кадр, і кожна вкладка на панелі вкладки повинна представляти "конфігурацію вікон" (тобто кілька вікон), а не буфер.
Стефан

5

Подумайте про те, щоб перевірити електронний екран , хоча він фактично не групує буфери.

Що це робить - це групові вікна та надають доступ до декількох макетів (вкладок), по яких можна швидко переходити. У моєму робочому процесі часто є якийсь код Ruby і пов'язані тести на одному екрані, тоді як мої нотатки todo та Org знаходяться в іншому, і, можливо, буфер подряпин для складання SQL запитів знаходиться в третьому. Це дозволяє мені легко переходити між завданнями та проектами, навіть незважаючи на те, що кожен екран використовує один і той же пул буферів.


3

Як щодо мого плагіна, кентавра-вкладок? У ньому багато варіантів конфігурації, він дійсно функціональний, він підтримується дуже популярними темами, такими як Kaolin Themes, і загалом це дійсно приємний зовнішній вигляд та естетичний пакет (за відгуками користувачів). Він доступний у MELPA і виглядає приблизно так:

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


Здається, це ще одне "кожна вкладка являє собою буфер, і кожне вікно має власну нерозв'язку".
Стефан

Я щойно додав налаштування для відображення груп вкладок замість імен вкладок, тому у функції ви встановлюєте правила (тобто групи elisp та lisp в одній групі, групи c і c ++ в іншій тощо), а також на вкладках цих груп показано.
Еммануель Бустос

Він все ще не відповідає на питання, де має бути одна панель вкладки на кадр (а не на вікно), і кожна вкладка являє собою конфігурацію вікна.
Стефан

Гаразд, я не відмовлявся. Я буду досліджувати і працювати над цим!
Еммануель Бустос

0

Ось моя конфігурація, для чого варто. У ньому є:

  • Операція миші на вкладках ( mouse-2закривати, як у веб-переглядачах , mouse-3 відкривати нове вікно Emacs, як в i3 )
  • Клавіші управління ( M-left& праві вкладки перемикачів, як у TMux / Екран )
  • Кольори (узгоджується з 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>")) 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.