'Guias' do navegador para o emacs?

22

Gostaria de guias como o Firefox, mas para o emacs.

Encontrei o seguinte: http://emacswiki.org/emacs/TabBarMode

Mas basta adicionar a cada buffer ( janela na terminologia do Emacs) uma barra que mostra os buffers atualmente abertos.

Eu gostaria que uma guia pudesse armazenar vários buffers ( janelas no Emacs teminology), que eu posso dividir conforme desejado. Ou seja, cada guia deve corresponder a um "estado da janela" (no sentido de window-state-get).

Eu teria uma guia para minhas tarefas, outra para código, outra para leitura na web etc.

Isso é possível? A tabbar pode ser adaptada para fazer isso?

[edit2]
Esta questão atraiu mais atenção do que eu previa. Parece que existe uma solução, mas que exigiria um pouco de pesquisa e ajustes. Embora esta / a próxima semana esteja um pouco ocupada para mim, analisarei as respostas e tentarei criar algo que funcione. Depois, editarei esta pergunta para refletir minhas descobertas. Aguarde =)

[edit] Mais ou menos
como:
/programming/24157754/make-frames-in-emacs-gui-behaves-like-frames-in-terminal

Gostaria de resolver com vários quadros em uma única sessão GUI também.

Leo Ufimtsev
fonte
2
"Gostaria que uma aba pudesse conter vários buffers, que posso dividir conforme desejado." Você quer dizer várias janelas ?
Malabarba 17/03/2015
1
É mais como eu gostaria de ter abas muito dinâmicas. Eu os criava e depois os preenchia com janelas. Ou seja, eu gostaria que uma guia fosse uma moldura. Em seguida, uma nova guia um novo quadro. Dentro de cada guia / quadro eu poderia abrir as janelas desejadas / (buffers). Isso é factível? (Ou seja, não codificados tampão nomes etc ..)
Leo Ufimtsev
1
Há uma variável associada a janelas específicas, mas faz um mês ou dois desde que eu vi um tópico que falou sobre isso e não sei o que é chamado de imediato. Você pode estar interessado em usar um sistema semelhante ao frame-bufs, em que uma lista contém os buffers associados a um quadro e essa lista é incorporada ao parâmetro frame. Você pode usar a variável associada a uma janela específica e fazer uma lista, adicionar / remover buffers da lista - essa lista seria um grupo de buffers a ser usado pela barra de guias. Tudo isso é teórico, mas acredito que funcionaria.
lawlist
1
Eu acho que você pode se referir a: stackoverflow.com/questions/24157754/… mas esse post não parece ter uma resposta sólida: - /
Leo Ufimtsev
1
Eu recomendaria dar uma olhada no pacote elscreen.
blarghmatey

Respostas:

8

Dividir buffers em grupos

É possível com a barra de tabulação. Você pode adicionar regras para agrupar buffers em grupos. Aqui está um trecho 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"
     ))))

Regras de exemplo:

  1. Listar nomes de buffer:
    ((member (buffer-name)
             '("*scratch*" "*Messages*" "*Help*"))
     "Common" ;; this is a group name
     )
  1. Em relação aos buffers comuns, prefiro colocar em "Common" cada buffer cujo nome começa com uma estrela. Isso fornece um exemplo de como fazer um buffer para esta regra:
    ((string-equal "*" (substring (buffer-name) 0 1))
     "Common"
     )
  1. Aqui está um exemplo de agrupamento de buffers por modo principal:
    ((memq major-mode
           '(org-mode text-mode rst-mode))
     "Text"
     )
  1. Aqui está um exemplo de agrupamento de buffers com base no modo de origem:
    ((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. Aqui está um exemplo de agrupamento de guias por regexp:
    ((string-match "^__" (buffer-name))
     "Templates"
     )
  1. Agrupe 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))

Depois de compor as regras, você pode pressionar + ou - na linha de guias da barra de guias para alternar entre grupos, e também ◀ e ▶ para alternar entre buffers. Ou apenas vincule os seguintes antônimos:

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

e mova-se entre guias e grupos de guias com o teclado.

Pessoalmente, agrupo guias, para ver o que está aberto, mas as navego com elas ido-switch-buffer.

Alternar entre o conjunto de regras

Também é possível definir um conjunto diferente de regras de agrupamento de buffer e alternar entre elas. Aqui está um exemplo de alternância entre dois conjuntos de regras de agrupamento de 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)

Isso alterna entre tabbar-buffer-groups-commone tabbar-buffer-groupsdesativa o agrupamento de guias.

Classificar buffers da barra de guias por nome

Acho útil classificar os buffers da barra de tabulação por nome. Veja como obtê-lo:

(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))))))))))
Adobe
fonte
Obrigado pela resposta detalhada. Vou tentar experimentar o que foi exposto acima e
avisarei
Mas o OP não quer "uma barra de tabulação por janela", ele quer uma barra de tabulação por quadro e cada guia na barra de tabulação deve representar uma "configuração de janela" (ou seja, várias janelas) em vez de um buffer, portanto, o agrupamento de buffers não é o problema .
Stefan
6

ATRIBUTO: O agrupamento de buffers por quadro é uma implementação direta dos conceitos e partes selecionadas de código desenvolvidas / escritas por Alp Aker nos quadros-bufs da biblioteca: https://github.com/alpaker/Frame-Bufs

A seguir, é apresentado um exemplo de como usar a biblioteca tabbar.ele agrupar abas / buffers dinamicamente em cada quadro, adicionando abas / buffers C-c C-aou removendo abas / buffers C-c C-n. Existem apenas dois (2) grupos - associados ao quadro atual (ou seja, "A") e NÃO associados ao quadro atual (ou seja "N"). Os grupos são locais do quadro, o que significa que cada quadro pode ter seu próprio agrupamento. O agrupamento personalizado pode ser redefinido com C-c C-r. Alterne entre grupos associados e não associados com C-tab. Alterne para a próxima guia / buffer no grupo atual com M-s-right. Alterne para a guia / buffer anterior no grupo atual com M-s-left.

Guias / buffers podem ser adicionados ou removidos programaticamente com my-add-buffere my-remove-buffer. Para obter um exemplo de como abrir certos buffers em quadros selecionados, consulte o thread relacionado intitulado Como interceptar um arquivo antes de abrir e decidir qual quadro : /programming//a/18371427/2112489 A função my-add-bufferprecisaria ser incorporado nos locais apropriados do código no link acima, se um usuário optar por implementar esse recurso.

O usuário pode criar uma entrada em um costume mode-line-formatque exiba o nome do grupo de guias atual na linha de modo, incorporando o seguinte trecho: (:eval (when tabbar-mode (format "%s" (tabbar-current-tabset t)))) Personalizar a linha de modo com mais detalhes, no entanto, está além do escopo deste exemplo.

A função tabbar-add-tabfoi modificada para alfabetizar as guias / buffers.

A função tabbar-line-tabfoi modificada para fornecer quatro (4) faces diferentes, dependendo da situação. Se a guia / buffer estiver associada ao quadro e IS selecionada, use tabbar-selected-associatedface. Se a guia / buffer estiver associada ao quadro e NÃO selecionada, use tabbar-unselected-associatedface. Se a guia / buffer NÃO estiver associada ao quadro e for selecionada, use tabbar-selected-unassociatedface. Se a guia / buffer NÃO estiver associada ao quadro e NÃO estiver selecionada, use a 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)

A captura de tela a seguir mostra os dois possíveis agrupamentos de buffer / guia: (1) à esquerda, há um agrupamento dos buffers / guias associados ao quadro denominado SYSTEM[guias amarelo e branco], com a letra maiúscula "A" indicada na linha de modo; e (2) à direita, há um agrupamento dos buffers / guias NÃO associados ao quadro denominado SYSTEM[guias azul e vermelho], com uma letra maiúscula "N" indicada na linha de modo.

Exemplo

lista de leis
fonte
Mas o OP não deseja "uma barra de tabulação por janela", ele deseja uma barra de tabulação por quadro e cada guia na barra de tabulação deve representar uma "configuração de janela" (ou seja, várias janelas) em vez de um buffer.
Stefan
5

Considere verificar o elscreen , embora ele não agrupe buffers.

O que ele faz é agrupar janelas e fornecer acesso a vários layouts (guias) que você pode mover rapidamente. Meu fluxo de trabalho geralmente possui código Ruby e testes associados em uma tela, enquanto minhas notas de todo e organização estão em outra, e talvez um buffer temporário para a elaboração de consultas SQL esteja em um terceiro. Isso me permite alternar facilmente entre tarefas e projetos, mesmo que cada tela se baseie no mesmo conjunto de buffers.

RP Dillon
fonte
3

E o meu plugin, centaur-tabs? Ele tem muitas opções de configuração, é realmente funcional, é suportado por temas muito populares, como o Kaolin Themes e, em geral, é um pacote estético e muito bonito (de acordo com o feedback do usuário). Está disponível no MELPA e fica assim:

insira a descrição da imagem aqui

Emmanuel Bustos
fonte
Essa parece ser outra solução "cada aba representa um buffer e cada janela possui sua própria barra de tabulação".
Stefan
Acabei de adicionar uma personalização para mostrar grupos de guias em vez de nomes de guias, em uma função você estabelece regras (por exemplo, grupo elisp e lisp em um grupo, grupo c e c ++ em outro e assim por diante) e nas guias desses grupos são mostrados.
Emmanuel Bustos
Ainda não responde à pergunta, onde deve haver uma barra de guias por quadro (em vez de por janela) e cada guia representa uma configuração de janela.
Stefan
Ok, eu não entendi. Vou investigar e trabalhar nisso!
Emmanuel Bustos
0

Aqui está a minha configuração, para o que vale a pena. Possui:

  • Operação do mouse nas guias ( mouse-2para fechar, como nos navegadores , mouse-3 para abrir em uma nova janela do Emacs, como no i3 )
  • Controles do teclado ( M-lefte guias da chave direita, como no TMux / Screen )
  • Cores (consistente com a moe-darkconfiguração "Moe theme / " incluída)
  • Agrupamento (atualmente, Emacs *buffers*e "regular")
  • Atualização automática (com pacote de uso )

Barra de abas

(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 - Alterne os 2 últimos buffers (macro KB)

(define-key global-map [(control tab)] (kbd "C-x b <return>")) 
yPhil
fonte