2

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

使用来自另一个问题的一些代码作为起点,我想出了以下小代码片段来做我想做的事情:

(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 的评论启发我将它们切换到devfarand 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)on 替换为:

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

这以某种方式设法使其附加到缓冲区,而不是覆盖它......所以我的 if 语句似乎在某种程度上工作......但是我在*Messages*缓冲区中看不到消息,所以...... . 我不太确定发生了什么事。

我想我注意到的一件事(虽然很难确定,因为当这种情况发生时我可能不会积极关注)是这发生在一个非当前活动的缓冲区中,而不是我现在的缓冲区中目前正在编辑。

所以,问题:

  1. 我在这里做错了吗?
  2. 还有其他/更好的方法吗?
  3. 有没有我可以使用的以编程方式保存状态的标准方法?(我适当地四处寻找,但没有找到任何东西......虽然也许我只是不知道要寻找什么。)
  4. 我能做些什么来帮助自己追踪这个问题?(有没有办法可以设置断点或其他东西?)
  5. 我可以在这样的代码中使用其他保护措施吗?

欢迎任何其他想法。我正在添加更多(message)表单,希望同时获得更多调试信息。

更新:我发现这只发生在post-gc-hook. 我不知道我的变量是否以某种方式被破坏(也许切换到defvardefcustom会解决这个问题?),或者在 gc-hook 后处理中是否存在某种晦涩的错误......检查重现测试用例有了这个最新的变化。

4

2 回答 2

2

你确实可以设置断点,一个简单的方法是放在(edebug)你想要中断的地方。然后您可以使用nfor next、SPCfor step 和eto eval。您可以在此处阅读有关 edebug的更多信息。

因此,您可以在调用之前将条件断点设置为保护/警告,如下所示(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))

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

以下是 docstring 的一部分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) 发生了什么。

于 2013-10-09T00:06:32.097 回答
1

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

但是你是正确的使用with-current-buffer而不是save-excursion跟随set-buffer

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

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

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

于 2013-10-09T02:02:32.597 回答