如何重新打开刚刚终止的缓冲区,如Firefox浏览器中的CSt?


35

有时我不小心杀死了一个缓冲区并想重新打开它,就像CSt在Firefox中撤消已关闭的选项卡一样,但是Emacs中没有内置命令,defun undo-kill-bufferhttp://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)))))

根本不起作用。如果您知道elisp,如何解决这个问题?

如果它可以显示已关闭缓冲区的列表,并且我可以从中选择一个重新打开,那会更好。


7
到目前为止,没有给出的答案可以回答所提出的问题,即“重新打开”或恢复被杀死的缓冲区。那是因为通常这几乎是不可能的-最好的办法是重新创建它。但是,如果您的意思是仅文件访问缓冲区,那么答案很简单,这里给出的答案是合适的。如果是这种情况,请编辑您的问题以反映此限制。
Drew

2
另一种方法是不杀死那些缓冲区。埋头缓冲区很好。您还可以想象到一种更高级的方法,例如(尚待定义)kill-buffer-later,该方法将立即掩埋缓冲区并设置计时器以在几分钟后杀死缓冲区。或稍后再使用kill-buffer可能会在访问文件时杀死该缓冲区,并在不访问该文件时延迟其终止(也许在其名称前添加一个空格,以避免在使用Cx b时造成混乱)。
YoungFrog 2015年

@YoungFrog,确实,我已经在回答中谈到了这个机会。
Mark Karpov

Answers:


27

这是另一个不需要的简单替代方法recentf。钩住第一个函数kill-buffer-hook会将与缓冲区关联的文件名推送到列表中。(请注意,如果您杀死了一个不访问文件的缓冲区,那么它就一去不复返了。)后一个函数将该文件从列表中弹出并访问它:

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

请注意,这killed-file-list是一个列表,因此,例如,您可以编写一个更复杂的函数来遍历该列表,而不是此处描述的简单函数:这取决于您要执行多少操作。

编辑:对不起,我错过了您Q中关于想要从中选择文件列表的最后一项规定。以下功能比上面的版本略胜一筹,因为它用于completing-read让您指定要删除的文件。如果您使用ido,则可以循环浏览在当前会话中杀死的所有文件,默认为最新文件。请注意,它假定您已经需要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")))

5

我想问你:“你真的想杀死它吗?”。确实,杀死缓冲区在Emacs世界中是很常见的事情,但是一旦杀死缓冲区,缓冲区就消失了,正如您的问题所表明的,这并不总是可取的。

但是,我们可以选择另一种方法,这样您就无需还原被杀死的缓冲区-只是更喜欢掩埋而不是杀死。看一下 Kill或Bury Alive软件包,可通过MELPA获得

从包装说明中:

您是否杀死了一些可能想活下来的缓冲区?通常,杀死的动机是“暂时摆脱我的束缚”,在许多情况下,杀死RAM并不是最佳选择,除非您的RAM非常有限。这个软件包允许Emacs教我们要杀死哪些缓冲区以及我们更希望掩埋哪些缓冲区。

当我们真的想杀死一个缓冲区时,事实证明并非所有缓冲区都希望以相同的方式消亡。该软件包允许指定如何终止各种缓冲区。例如,当您使用某些具有关联进程的缓冲区时,这可能特别有用。

但是有时您可能想要摆脱大多数缓冲区,并使Emacs处于或多或少的原始状态。您可能不想杀死暂存缓冲区,也可能不想杀死与ERC相关的缓冲区。您可以指定要清除的缓冲区。


4

我从这篇SO帖子中使用了此解决方案,并且效果很好。

解决方案是优雅的,但不是完美的。它存储活动缓冲区的列表,并从“最新”列表中返回不属于活动缓冲区列表的第一个文件。

;; 重新打开最后终止的缓冲区
;; 资料来源:https://stackoverflow.com/questions/10394213/emacs-reopen-previous-killed-buffer
(需要'cl)
(要求'recentf)
(最近模式1)
(defun undo-kill-buffer()
  (互动)
  (let((active-files(buf循环在(buffer-list)
                            何时(缓冲文件名buf)收集它)))
    (最近的文件列表中的文件循环
          除非(成员文件活动文件)返回(查找文件文件)))

3

您需要打开recentf-mode。为此,请运行M-x recentf-mode。然后,该功能可能无法工作,直到您打开或杀死一些新的缓冲区。我认为您不会recentf-list填写。

如果您希望在Emacs启动时启用此功能,请将其放入您的init文件中:

(recentf-mode)

然后,您可以将defun找到的内容放入其中,并将其绑定到密钥(如果需要)。

该模式的一个缺点似乎是建立了lastf模式来跟踪opened文件,而不是杀死文件。因此,如果您两次运行该函数,它将不会重新打开您最近被杀死的文件。


4
尽管Emacs 24有效地使此选项对于次要模式是可选的,但我仍然倾向于(recentf-mode 1)使用显式参数进行编写,以便在Emacs 23下重新评估其init文件的人最终不会再次切换该模式。
菲尔2014年

1

ErgoEmacs具有一个close-current-buffer特别保留最近关闭的缓冲区列表的功能:

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

所以使用这些可以重新打开此会话关闭的缓冲区

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

1

编辑: 回答时我没有注意,回答了OP没问过的其他问题。再次抱歉。谢谢您的留言,@ CodyChan。

好吧,我不是Emacs的资深人士,也许这可能仅在最新版本中才可用。我知道几年后会来,但也许对其他人会有所帮助,因为我的搜索使我来到了这里。

我使用的是Emacs v25.2.1,recentf已经在这里提供,并且具有可以满足您需要的就绪函数。过去,我已经在旧版本中激活了它,所以我.emacs有:

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

这对我来说非常有效。当然,请更改快捷方式以使您更满意。


2
似乎该功能早已存在,根据Emacs源代码树中的ChangeLog文件,它是2005年。它确实可以工作,甚至可以从先前的Emacs会话中重新打开关闭的缓冲区,但似乎无法列出关闭的缓冲区以让我从列表中选择一个。
CodyChan

2
因此,它不会专门重新打开刚刚终止的缓冲区,而是重新打开最近打开的文件。
CodyChan

@CodyChan,非常抱歉,您当然是对的。那将是最近打开的文件,而不是您所要求的文件。我会尽快删除答案,向您和其他同伴道歉。
查尔斯·罗伯托·卡纳托'17

3
不必要删除此答案,也许有人只是想让您的解决方案简单地重新打开最近打开的文件。:)
CodyChan
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.