elisp代码破坏缓冲区,而不是在其他地方存钱......为什么?

时间:2013-10-08 23:46:11

标签: emacs elisp

我在尝试设置保存某些持久状态的东西时遇到了一些困难,因此我可以在emacs调用之间使用数据。

使用another question中的一些代码作为起点,我想出了以下一些我想要做的事情的代码片段:

(defmacro with-output-to-file (path &rest body)
  "record output of commands in body to file"
  `(save-excursion
     (let* ((buf (find-file-noselect ,path))
            (standard-output buf))
       (set-buffer buf)
       (erase-buffer)
       ,@body
       (save-buffer)
       (kill-buffer))))

然后我有一个使用它的函数,如:

(defun my-save-some-data ()
  (with-output-to-file my-data-save-file
                       (prin1 my-data)))

编辑:这些都遵循以下代码(之前,这些都是setq;感谢@phils的评论,鼓励我将它们切换为devfar和{{1 }}):

defcustom

(注意:我还有一个函数来重新读取数据,这在加载时或按需自动发生。)

我已经设定了在几种情况下运行(可能太多了?可能选择不好?无论如何,这就是我设置的):

; note: my actual variable names (and filename value) are different;
; changed for example sake:
(defvar my-data (make-hash-table :test 'equal) "Data for a thing")
(defcustom my-data-save-file "~/tmp/my-data.el" "File to save my data to")

大部分时间,这都很好。但是,每隔一段时间,我就会遇到一个问题,即数据被写入我之前打开的缓冲区之一(在那里杀死所有以前的内容!),然后该缓冲区会被保存的更改杀死。

我只想说,这非常令人讨厌,因为发生这种情况的缓冲区经常出现在我正在做某些工作的地方,而且还不一定要检查它。

我尝试更改上面的宏,从(add-hook 'auto-save-hook 'my-save-some-data) (add-hook 'kill-emacs-hook 'my-save-some-data) (add-hook 'post-gc-hook 'my-save-some-data) 替换为:

(set-buffer buf)

这已经设法导致它追加到缓冲区,而不是覆盖它...所以我的if语句似乎确实在某种程度上起作用...但是我没有看到我的 (with-current-buffer buf ; because set-buffer wasn't working?? (erase-buffer) ,@body (if (eq buf (current-buffer)) (progn (save-buffer) (kill-buffer)) (message "buffer changed?!")))))) 缓冲区中的消息,所以...我不太确定发生了什么。

想到的一件事我注意到了(尽管我很难确定,因为当这种情况发生时我可能没有积极关注)是这种情况发生在当时不是 - 当时 - 活动缓冲区,而不是我正在编辑的缓冲区。

所以,问题:

  1. 我在这里做错了吗?
  2. 还有其他/更好的方法吗?
  3. 有没有标准的方法以程序方式保存状态,我可以使用? (我在apropos中略微戳了一下,但未能找到任何东西......虽然也许我只是不知道该找什么。)
  4. 我可以做些什么来帮助自己跟踪这个? (有没有办法设置断点或什么?)
  5. 我可以在这样的代码中使用其他保护措施吗?
  6. 欢迎任何其他想法。我正在添加更多*Messages*表单,希望同时获得更多调试信息。

    更新:我发现这只发生在(message)上。我不知道我的变量是否以某种方式被破坏(并且可能切换到post-gc-hookdefvar将解决这个问题?),或者在后gc-hook处理中是否存在某种模糊的错误...通过最新的更改检查复制测试用例。

2 个答案:

答案 0 :(得分:2)

您确实可以设置断点,一种简单的方法是将(edebug)放在您想要中断的位置。然后,您可以使用n作为下一个,SPC作为步骤,e作为评估。您可以阅读有关edebug here的更多信息。

因此,在致电(set-buffer)之前,您可以将条件断点设置为保护/警告:

(when (get-file-buffer my-data-save-file) 
 (read-from-minibuffer 
   (format "Warning: %s is already being visited by a buffer, contents will be overwritten! Entering edebug" my-data-save-file))
   (edebug))

这将警告您,然后进入调试器,如果您正在某个缓冲区中访问的文件即将被您的宏覆盖,您可以在其中检查正在进行的操作。

以下是find-file-no-select

的文档字符串的一部分
Read file FILENAME into a buffer and return the buffer.
If a buffer exists visiting FILENAME, return that one, but
verify that the file has not changed since visited or saved.

我的猜测是my-data-save-file已被缓冲区访问,因此返回(随后被覆盖)的缓冲区。但是你真的可以找到正在发生的事情(edebug)。

答案 1 :(得分:1)

快速回复你所说的一些内容。您的消息永远不会出现,可能是因为您测试with-current-buffer的缓冲区是否为current-buffer,它始终是,除非body更改当前缓冲区。

但您使用with-current-buffer代替save-excursion后跟set-buffer是正确的。

至于其他方式:为什么不将数据放在临时缓冲区中,然后使用write-fileappend-to-filewrite-region

FWIW,我简单地尝试了你的代码,没有看到任何问题。但我只是为(prin1 (symbol-function 'my-save-some-data))尝试了一个简单的body和文件的常量文件名。我尝试使用预先存在的文件,是否使用预先存在的缓冲区,以及是否使用预先存在的未保存的修改缓冲区。

您是使用解释代码(例如,宏存在)还是字节编译代码进行测试?