Como reabrir o buffer acabado de matar, como o CSt no navegador Firefox?

35

Às vezes, eu mato acidentalmente um buffer e quero reabri-lo, assim como o CSt para desfazer a guia fechada no Firefox, mas não há comando interno no Emacs, defun undo-kill-bufferno http://www.emacswiki.org/RecentFiles :

(defun undo-kill-buffer (arg)
  "Re-open the last buffer killed.  With ARG, re-open the nth buffer."
  (interactive "p")
  (let ((recently-killed-list (copy-sequence recentf-list))
     (buffer-files-list
      (delq nil (mapcar (lambda (buf)
                  (when (buffer-file-name buf)
                (expand-file-name (buffer-file-name buf)))) (buffer-list)))))
    (mapc
     (lambda (buf-file)
       (setq recently-killed-list
         (delq buf-file recently-killed-list)))
     buffer-files-list)
    (find-file
     (if arg (nth arg recently-killed-list)
       (car recently-killed-list)))))

não funciona de todo. Se você conhece o elisp, como resolver esse problema?

Se ele puder mostrar uma lista dos buffers fechados e eles, posso escolher um deles para reabrir, isso seria melhor.

CodyChan
fonte
7
Nenhuma das respostas dadas até agora responde à pergunta apresentada, que é sobre "reabrir" ou restaurar um buffer morto. Isso porque isso é praticamente impossível, em geral - o melhor que poderia ser feito seria recriá-lo. Mas se você quer dizer apenas buffers de arquivos , a resposta é fácil e as respostas fornecidas aqui são apropriadas. Se for esse o caso , edite sua pergunta para refletir essa restrição.
Tirou
2
Uma abordagem alternativa é não matar esses buffers. enterrar-buffer é bom. Você também pode imaginar uma abordagem mais sofisticada, por exemplo, um (ainda não definido) kill-buffer-later que enterraria o buffer imediatamente e configuraria um temporizador para matar o buffer após alguns minutos. Ou um kill-buffer-maybe-later que mataria o buffer se estiver visitando um arquivo e atrasará sua morte se não estiver (talvez também prefira um espaço ao nome para evitar confusão ao usar Cx b).
YoungFrog
@YoungFrog, exatamente, abordei esta oportunidade na minha resposta.
Mark Karpov

Respostas:

27

Aqui está outra alternativa simples que não requer recentf. Conectar a primeira função kill-buffer-hookempurrará o nome do arquivo associado ao buffer para uma lista. (Observe que, se você matar um buffer que não está visitando um arquivo, ele desapareceu definitivamente.) A última função retira esse arquivo da lista e o visita:

(defvar killed-file-list nil
  "List of recently killed files.")

(defun add-file-to-killed-file-list ()
  "If buffer is associated with a file name, add that file to the
`killed-file-list' when killing the buffer."
  (when buffer-file-name
    (push buffer-file-name killed-file-list)))

(add-hook 'kill-buffer-hook #'add-file-to-killed-file-list)

(defun reopen-killed-file ()
  "Reopen the most recently killed file, if one exists."
  (interactive)
  (when killed-file-list
    (find-file (pop killed-file-list))))

Observe que killed-file-listé uma lista; portanto, você pode, por exemplo, escrever uma função mais complexa para percorrer essa lista, em vez da simples descrita aqui: depende de você o quanto deseja fazer com ela.

EDIT: desculpe, eu perdi a última disposição no seu Q sobre querer uma lista de arquivos para escolher. A função a seguir é um pouco mais sofisticada que a versão acima, na medida em que é usada completing-readpara permitir que você especifique qual dos arquivos mortos você deseja. Se você estiver usando algo parecido ido, ele permitirá percorrer todos os arquivos que você matou na sessão atual, por padrão, para os mais recentes. Observe que presume que você já tenha solicitado cl-lib:

(defun reopen-killed-file-fancy ()
  "Pick a file to revisit from a list of files killed during this
Emacs session."
  (interactive)
  (if killed-file-list
      (let ((file (completing-read "Reopen killed file: " killed-file-list
                                   nil nil nil nil (car killed-file-list))))
        (when file
          (setq killed-file-list (cl-delete file killed-file-list :test #'equal))
          (find-file file)))
    (error "No recently-killed files to reopen")))
Dan
fonte
5

Eu gostaria de perguntar: “Você realmente quer matá-lo?”. De fato, matar um buffer é uma coisa tão comum no mundo do Emacs, mas uma vez morto, o buffer desaparece e, como demonstra sua pergunta, nem sempre é desejável.

No entanto, podemos escolher outra maneira, para que você nunca precise restaurar o buffer morto - apenas prefira enterrar do que matar. Dê uma olhada no pacote Kill or Bury Alive , que está disponível via MELPA .

Da descrição do pacote:

Você já matou algum buffer que você pode querer deixar vivo? A motivação para matar geralmente é "saia do meu caminho por enquanto", e matar pode não ser a melhor escolha em muitos casos, a menos que sua RAM seja muito, muito, muito limitada. Este pacote permite ensinar ao Emacs quais buffers queremos matar e quais preferimos enterrar vivos.

Quando realmente queremos matar um buffer, acontece que nem todos os buffers gostariam de morrer da mesma maneira. O pacote permite especificar como eliminar vários tipos de buffers. Isso pode ser especialmente útil quando você está trabalhando com algum buffer que possui um processo associado, por exemplo.

Mas às vezes você pode se livrar da maioria dos buffers e levar o Emacs a um estado mais ou menos virgem. Você provavelmente não quer matar o buffer temporário e talvez também os relacionados ao ERC. Você pode especificar quais buffers remover.

Mark Karpov
fonte
4

Eu uso esta solução neste post SO e ele funciona bem.

A solução é elegante, mas não perfeita; ele armazena uma lista de buffers ativos e retorna o primeiro arquivo da listaf recente que não pertence à lista de buffers ativos.

;; Reabra o último buffer morto
;; Fonte: https://stackoverflow.com/questions/10394213/emacs-reopen-previous-killed-buffer
(requer 'cl)
(requer 'recentef)
(modo recente 1)
(desfazer desfazer-matar-buffer ()
  (interativo)
  (let ((arquivos ativos (loop para buf in (lista de buffers))
                            quando (buffer-file-name buf) o coleta))))
    (loop para arquivo na listaf recente
          a menos que (arquivo de membro arquivos ativos) retorne (arquivo de arquivo de localização))))
Kaushal Modi
fonte
3

Você precisa ligar recentf-mode. Para fazer isso, corra M-x recentf-mode. Em seguida, a função pode não funcionar até você abrir ou matar alguns novos buffers; Eu não acho que você terá recentf-listpreenchido.

Se você deseja que isso seja ativado quando o Emacs iniciar, coloque isso no seu arquivo init:

(recentf-mode)

Você pode colocar o defunque encontrou lá e vinculá-lo a uma chave, se quiser.

Uma desvantagem desse modo parece ser que o modo recentf foi criado para rastrear openedarquivos, e não arquivos mortos. Portanto, se você executar a função duas vezes, ela não reabrirá o segundo arquivo morto mais recentemente.

zck
fonte
4
Embora o Emacs 24 tenha efetivamente tornado isso opcional para modos menores, eu ainda estaria inclinado a escrever (recentf-mode 1)com o argumento explícito, para que alguém reavaliar seu arquivo init no Emacs 23 não acabe desativando o modo novamente.
Phs #
1

O ErgoEmacs possui uma função close-current-bufferque, em particular, mantém uma lista de buffers fechados recentemente:

(defvar recently-closed-buffers (cons nil nil) "A list of recently closed buffers. The max number to track is controlled by the variable recently-closed-buffers-max.")
(defvar recently-closed-buffers-max 10 "The maximum length for recently-closed-buffers.")

(defun close-current-buffer ()
"Close the current buffer.

Similar to (kill-buffer (current-buffer)) with the following addition:

• prompt user to save if the buffer has been modified even if the buffer is not associated with a file.
• make sure the buffer shown after closing is a user buffer.
• if the buffer is a file, add the path to the list recently-closed-buffers.

A emacs buffer is one who's name starts with *.
Else it is a user buffer."
 (interactive)
 (let (emacsBuff-p isEmacsBufferAfter)
   (if (string-match "^*" (buffer-name))
       (setq emacsBuff-p t)
     (setq emacsBuff-p nil))

   ;; offer to save buffers that are non-empty and modified, even for non-file visiting buffer. (because kill-buffer does not offer to save buffers that are not associated with files)
   (when (and (buffer-modified-p)
              (not emacsBuff-p)
              (not (string-equal major-mode "dired-mode"))
              (if (equal (buffer-file-name) nil) 
                  (if (string-equal "" (save-restriction (widen) (buffer-string))) nil t)
                t
                )
              )
     ;; (progn ;; I'VE ADDED THIS LINE
     ;;   (switch-to-buffer (buffer-name)) ;; AND THIS LINE
     (if (y-or-n-p
          (concat "Buffer " (buffer-name) " modified; Do you want to save?"))
         (save-buffer)
       (set-buffer-modified-p nil))
     ;; ) ;; AND ALSO A PARENTHESIS HERE
     )

   ;; save to a list of closed buffer
   (when (not (equal buffer-file-name nil))
     (setq recently-closed-buffers
           (cons (cons (buffer-name) (buffer-file-name)) recently-closed-buffers))
     (when (> (length recently-closed-buffers) recently-closed-buffers-max)
           (setq recently-closed-buffers (butlast recently-closed-buffers 1))
           )
     )

   ;; close
   (kill-buffer (current-buffer))

   ;; if emacs buffer, switch to a user buffer
   (if (string-match "^*" (buffer-name))
       (setq isEmacsBufferAfter t)
     (setq isEmacsBufferAfter nil))
   (when isEmacsBufferAfter
     (next-user-buffer)
     )
   )
 )

Portanto, usando estes pode-se reabrir este buffer fechado de sessão com

;; undo close this-session buffer:
(defun ergo-undo-close-buffer ()
  "Opens some this-session closed buffer."
  (interactive)
  (let* ((mylist (delq nil (delete-dups (mapcar 'car recently-closed-buffers))))
         (baseName (ido-completing-read "Open this session closed buffer: " mylist))
         (fileName (cdr (assoc baseName recently-closed-buffers))))
    (find-file fileName)))
Adobe
fonte
1

EDIT: Não prestei atenção ao responder e respondi a outra coisa que o OP não pediu. Mais uma vez, me desculpe. Obrigado por suas palavras, @CodyChan.

Bem, eu não sou veterano do Emacs, e talvez isso esteja disponível apenas nas versões recentes. Sei que voltarei alguns anos depois, mas talvez possa ser útil para outras pessoas, pois minha pesquisa me trouxe até aqui.

Estou no Emacs v25.2.1, recentfjá está disponível aqui e tem uma função pronta que faz o que você precisa. Eu já o ativei no passado em versões mais antigas, então .emacstenho:

(recentf-mode 1)
(global-set-key (kbd "C-S-t") 'recentf-open-most-recent-file)

E isso funcionou perfeitamente para mim. Claro, altere o atalho para o que mais lhe agrada.

Charles Roberto Canato
fonte
2
Parece que a função já existia há muito tempo, que é 2005, de acordo com o arquivo ChangeLog na árvore de códigos fonte do Emacs. Funciona e pode até reabrir o buffer fechado da sessão anterior do Emacs, mas parece que não é possível listar os buffers fechados para que eu escolha um da lista.
CodyChan
2
Portanto, ele não reabre apenas o buffer morto especificamente, mas reabre os arquivos abertos recentemente.
CodyChan
@CodyChan, sinto muito e é claro que você está certo. Esse seria o arquivo aberto mais recentemente, e não o que você pediu. Excluirei a resposta em breve, peço desculpas a você e a outros companheiros.
Charles Roberto Canato
3
Desnecessariamente para excluir esta resposta, talvez alguém queira que sua solução simplesmente reabra os arquivos abertos recentemente. :)
CodyChan