¿'Pestañas' al estilo del navegador para emacs?


22

Me gustaría pestañas como Firefox pero para emacs.

Encontré esto: http://emacswiki.org/emacs/TabBarMode

Pero solo agrega a cada búfer ( ventana en la terminología de Emacs) una barra que muestra los búferes abiertos actualmente.

Me gustaría que una pestaña pudiera contener múltiples buffers ( ventanas en la teminología de Emacs), que puedo dividir según lo desee. Es decir, cada pestaña debe corresponder a un "estado de ventana" (en el sentido de window-state-get).

Tendría una pestaña para mis tareas, otra para el código, otra para leer en la web, etc.

es posible? ¿Se puede adaptar el tabbar para hacer esto?

[edit2]
Esta pregunta ha atraído más atención de lo que esperaba. Parece que hay una solución, pero que requeriría un poco de investigación y ajustes. Si bien esta semana próxima está un poco ocupada para mí, analizaré las respuestas e intentaré crear algo que funcione y luego editaré esta pregunta para reflejar mis hallazgos. Por favor espera =)

[editar] Más o menos
similar a:
/programming/24157754/make-frames-in-emacs-gui-behaves-like-frames-in-terminal

También me conformaría con múltiples marcos en una sola sesión de GUI.


2
"Me gustaría que una pestaña pueda contener múltiples buffers, que puedo dividir según lo desee". ¿Te refieres a múltiples ventanas ?
Malabarba

1
Es más como si quisiera tener pestañas muy dinámicas. Los crearía y luego los llenaría con ventanas. Es decir, me gustaría que una pestaña sea un marco. Luego una nueva pestaña un nuevo marco. Dentro de cada pestaña / marco podría abrir las ventanas / (buffers) deseadas. ¿Es esto factible? (Es decir, no hay nombres de búfer codificados, etc.)
Leo Ufimtsev

1
Hay una variable asociada con ventanas particulares, pero han pasado uno o dos meses desde que vi un hilo que hablaba de ello y no sé cómo se llama de forma manual. Puede que le interese utilizar un sistema similar a frame-bufs donde una lista contiene los buffers asociados con un marco y esa lista se incorpora al parámetro del marco. Puede usar la variable asociada con una ventana en particular y convertirla en una lista, agregar / eliminar buffers de la lista; esa lista sería un grupo de buffer que usará tabbar. Todo esto es teórico, pero creo que funcionaría.
ley

1
Creo que puede consultar: stackoverflow.com/questions/24157754/… pero esa publicación no parece tener una respuesta sólida: - /
Leo Ufimtsev

1
Recomendaría echar un vistazo al paquete de elscreen.
blarghmatey

Respuestas:


8

Split buffers en grupos

Es posible con tabbar. Puede agregar reglas para agrupar buffers en grupos. Aquí hay un fragmento básico:

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

Reglas de ejemplo:

  1. Lista de nombres de búfer:
    ((member (buffer-name)
             '("*scratch*" "*Messages*" "*Help*"))
     "Common" ;; this is a group name
     )
  1. Con respecto a los búferes comunes, prefiero poner en "Común" cada búfer cuyo nombre comienza con una estrella. Esto da un ejemplo de hacer un búfer para esta regla:
    ((string-equal "*" (substring (buffer-name) 0 1))
     "Common"
     )
  1. Aquí hay un ejemplo de agrupación de buffers por modo mayor:
    ((memq major-mode
           '(org-mode text-mode rst-mode))
     "Text"
     )
  1. Aquí hay un ejemplo de agrupación de búferes según el modo del que derivan:
    ((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. Aquí hay un ejemplo de agrupación de pestañas por regexp:
    ((string-match "^__" (buffer-name))
     "Templates"
     )
  1. Agrupar buffers por modo principal:
    (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))

Una vez que compuso las reglas, puede presionar + o - en la línea de pestañas de la barra de pestañas para alternar grupos, y también ◀ y ▶ para cambiar entre buffers. O simplemente une los siguientes defuns:

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

y moverse entre pestañas y grupos de pestañas con el teclado.

Personalmente, agrupo las pestañas para ver lo que está abierto, pero navegar con ellas ido-switch-buffer.

Cambiar entre un conjunto de reglas

También se puede definir un conjunto diferente de reglas de agrupación de búfer y alternar entre ellas. Aquí hay un ejemplo de ciclo entre dos conjuntos de reglas de agrupación de búfer:

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

Esto alterna entre tabbar-buffer-groups-commony la tabbar-buffer-groupsagrupación de tabulaciones defuns.

Ordenar los búferes de tabbar por nombre

Me resulta beneficioso ordenar los búferes de tabbar por nombre. Aquí se explica cómo obtenerlo:

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

Gracias por la respuesta detallada. Intentaré probar lo anterior y te lo haré saber ~ eventualmente :)
Leo Ufimtsev

Pero el OP no quiere "una barra de pestañas por ventana", quiere una barra de pestañas por marco y cada pestaña en la barra de pestañas debe representar una "configuración de ventana" (es decir, varias ventanas) en lugar de un búfer, por lo que agrupar buffers no es el problema .
Stefan

6

ATRIBUTO: La agrupación de buffers por cuadro es una implementación directa de los conceptos y porciones selectas de código desarrolladas / escritas por Alp Aker en la biblioteca frame-bufs: https://github.com/alpaker/Frame-Bufs

El siguiente es un ejemplo de cómo usar la biblioteca tabbar.ely agrupar pestañas / buffers dinámicamente por cuadro agregando pestañas / buffers con C-c C-ao eliminando pestañas / buffers con C-c C-n. Solo hay dos (2) grupos: asociados con el cuadro actual (es decir "A") y NO asociados con el cuadro actual (es decir "N"). Los grupos son marco local, lo que significa que cada marco puede tener su propia agrupación. La agrupación personalizada se puede restablecer con C-c C-r. Cambiar entre grupos asociados y no asociados con C-tab. Cambie a la siguiente pestaña / búfer en el grupo actual con M-s-right. Cambie a la pestaña / búfer anterior en el grupo actual con M-s-left.

Las pestañas / buffers se pueden agregar o quitar mediante programación con my-add-buffery my-remove-buffer. Para ver un ejemplo de cómo abrir ciertos búferes en marcos seleccionados, consulte el hilo relacionado titulado Cómo interceptar un archivo antes de abrirlo y decidir qué marco : /programming//a/18371427/2112489 La función my-add-buffernecesitaría incorporarse en las ubicaciones apropiadas del código en el enlace anterior si un usuario elige implementar esa característica.

El usuario puede desear crear una entrada en una costumbre mode-line-formatque muestre el nombre del grupo de pestañas actual en la línea de modo incorporando el siguiente fragmento: (:eval (when tabbar-mode (format "%s" (tabbar-current-tabset t)))) Personalizar la línea de modo con más detalle, sin embargo, está más allá del alcance de este ejemplo.

La función tabbar-add-tabse ha modificado para alfabetizar las pestañas / buffers.

La función tabbar-line-tabse ha modificado para proporcionar cuatro (4) caras diferentes según la situación. Si la pestaña / búfer está asociada con el marco y se selecciona IS, entonces use tabbar-selected-associatedface. Si la pestaña / búfer está asociada con el marco y NO está seleccionada, utilice la tabbar-unselected-associatedcara. Si la pestaña / búfer NO está asociada con el marco y se selecciona IS, entonces use la tabbar-selected-unassociatedcara. Si la pestaña / búfer NO está asociada con el marco y NO está seleccionada, entonces use la tabbar-unselected-unassociatedcara.

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

La siguiente captura de pantalla muestra las dos posibles agrupaciones de búferes / pestañas: (1) a la izquierda hay una agrupación de los búferes / pestañas que están asociados con el marco denominado SYSTEM[pestañas amarillas y blancas], con la letra mayúscula "A" indicada en el línea de modo; y (2) a la derecha hay una agrupación de los búferes / pestañas que NO están asociados con el cuadro denominado SYSTEM[pestañas azules y rojas], con una letra mayúscula "N" indicada en la línea de modo.

Ejemplo


Pero el OP no quiere "una barra de pestañas por ventana", quiere una barra de pestañas por marco y cada pestaña en la barra de pestañas debe representar una "configuración de ventana" (es decir, varias ventanas) en lugar de un búfer.
Stefan

5

Considere revisar elscreen , aunque en realidad no agrupa los buffers.

Lo que hace es agrupar ventanas y proporcionar acceso a múltiples diseños (pestañas) entre los que puede moverse rápidamente. Mi flujo de trabajo a menudo tiene un código Ruby y pruebas asociadas en una pantalla, mientras que mis notas de tarea y Org están en otra, y tal vez un búfer de memoria virtual para redactar consultas SQL está en una tercera. Esto me permite saltar fácilmente entre tareas y proyectos, a pesar de que cada pantalla se basa en el mismo grupo de buffers.


3

¿Qué tal mi plugin, centaur-tabs? Tiene muchas opciones de configuración, es realmente funcional, es compatible con temas muy populares como Kaolin Themes y, en general, es un paquete muy atractivo y estético (según los comentarios del usuario). Está disponible en MELPA y se ve así:

ingrese la descripción de la imagen aquí


Esto parece ser otra "cada pestaña representa un búfer y cada ventana tiene su propia barra de pestañas" sin solución.
Stefan

Acabo de agregar una personalización para mostrar grupos de pestañas en lugar de nombres de pestañas para que en una función establezca reglas (es decir, grupo elisp y lisp en un grupo, grupo c y c ++ en otro y así sucesivamente) y en las pestañas se muestran esos grupos.
Emmanuel Bustos

Todavía no responde la pregunta, donde debería haber una barra de pestañas por cuadro (en lugar de por ventana) y cada pestaña representa una configuración de ventana.
Stefan

Ok, no entendí. ¡Investigaré y trabajaré en ello!
Emmanuel Bustos

0

Aquí está mi configuración, por lo que vale. Cuenta con:

  • Operación del mouse en las pestañas ( mouse-2para cerrar, como en los navegadores , mouse-3 para abrir en una nueva ventana de Emacs, como en i3 )
  • Controles del teclado ( M-lefty pestañas del interruptor derecho, como en TMux / Screen )
  • Colores (consistentes con la moe-darkconfiguración " Tema Moe / " incluida)
  • Agrupación (actualmente, Emacs *buffers*y "regular")
  • Actualización automática (con paquete de uso )

Barra de pestañas

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

Anexo 1 - Tema Moe

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

Anexo 2: alternar los 2 últimos búferes (macro KB)

(define-key global-map [(control tab)] (kbd "C-x b <return>")) 
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.