Emacs 次要模式编程:取消切换程序?

Emacs minor mode programming: cancel toggle-off procedure?

我正在使用类似于这个最小示例的习惯用法和宏来编写自己的 Emacs 次要模式:

(define-minor-mode foo-mode
  "Toggle Foo mode."
  :init-value nil
  :lighter " Foo"
  :group 'foo
  (if foo-mode
      (do-enable-foo)
    (do-disable-foo))
  )

(defun do-enable-foo ()
  "Enable foo minor mode"
  (message "Enabling foo...")
  (if test-something
      (message "Foo enabled after disabling was canceled!")
    (message "Foo enabled from scratch"))
  )

(defun do-disable-foo ()
  "Disable foo minor mode"
  (message "Disabling foo...")
  (if (not certain-situation)
      (message "... done.") ; finish disabling foo
    ;; else forms:
    (message "Need to cancel disabling foo!")
    (foo-mode 1)) ; turning foo mode on again
  )

在关闭次要模式期间,当我必须取消切换时可能会出现 'certain-situation。目前,我在想,因为我正在使用 define-minor-mode 宏,所以我无法摆脱困境,而只需要使用 (foo-mode 1) 以编程方式再次打开模式,如代码中所示。

如果我走这条路,我将不得不在启用函数 do-enable-foo 中以不同的方式处理这个问题——所以我的第一个问题是如何使用代码中的占位符 'test-something 检测这种情况以上?

或者,是否有更简洁的方法来实现取消,例如,通过发出错误信号而不是 (foo-mode 1) 来防止模式切换通过?

我会非常害怕阻止用户关闭次要模式。我的意思是,如果次要模式坏了,或者行为不端,你就会给他们留下一个必须杀死的坏 Emacs。您考虑的条件是什么?

相反的情况——在某些情况下拒绝开启的模式——已经发生了——paredit-mode 就是一个很好的例子。它只是在 define-minor-mode 主体中抛出一个错误。

如果您想取消 "turn off",而不是调用 (foo-mode 1),您可以 (setq foo-mode t)

如果出于某种原因你真的想递归地调用 (foo-mode 1) 来(重新)启用该模式,那么你可以通过以下方式进行:

(defvar foo-mode--reenabling nil)
....
(defun do-enable-foo ()
  (if foo-mode--reenabling
      ...
    ...))
...
(defun do-disable-foo ()
  ...
  (let ((foo-mode--reenabling t))
    (foo-mode 1)))

我最终按照以下方式使用了 Stefan 的建议,使用变量向次要模式定义发出信号,表明由于取消禁用过程(实际上是用户查询的结果)而启用了该模式。我不敢使用简单地将模式变量设置为 t 的想法,但我再也找不到警告不要这样做的文档。可能值得看看使用 (setq foo-mode t) 是否真的可以让我消除新变量 canceled-foo-off.

(defvar canceled-foo-off nil "Set to `true' if user canceled toggling off foo.")
(make-variable-buffer-local 'canceled-foo-off)

(define-minor-mode foo-mode
  ...
  (if foo-mode
      (if canceled-foo-off
        (setq canceled-foo-off nil) ; Mode was turned back on from cancelling 
      (do-enable-foo)) ; Mode was turned on from scratch
    (do-disable-foo))
  )

(defun do-disable-foo ()
  "Disable foo minor mode"
  (if (not certain-situation)
      ...
    ;; else forms:
    (setq canceled-foo-off t) ; make sure mode starting procedure is not run!
    (foo-mode 1)) ; turning foo mode on again
  )