简单地暂停和继续宏

[分享讨论] smart query replace 继续讨论:

有的时候我希望用户(我)能在一个命令运行到一半的时候“暂停”,这样用户可以自由地做一些编辑,然后C-c C-c从暂停的地方继续。一个例子是qurey-replace:首先标记一段区域作为from-string,然后中断命令,让用户自由地编辑标记区域,最后继续命令,把编辑过的区域作为to-string,开始替换。

一般来说想要达到这种中断的效果就需要两个函数和一些全局变量保存状态,这么写显然不简洁。所以我想到用宏包装一下:

(pause CONTINUE &optional QUIT FINALLY)

暂停以后C-c C-c会运行CONTINUEFINALLYC-g会运行QUITFINALLYFINALLY就是清理代码)。

一个例子:

(defun something ()
  ;; 如果想在continue和quit里用之前的变量,不要忘了开lexical scope
  (stage-1)
  (pause
    (stage-2)
    (do-somthing-if-user-quit)
    (clean-up)))

query-replace的例子:

(defun query-replace+ (beg end)
  "Select a region and invoke this function.
Edit the underlined region and press C-c C-c to qurey-replace."
  (interactive "r")
  (if (not (region-active-p))
      (message "Select the text to be replaced first")
    (let ((string (buffer-substring-no-properties
                   beg end))
          (ov (make-overlay beg end nil nil t)))
      (deactivate-mark)
      (overlay-put ov 'face '(:underline t))
      (pause
        (query-replace string (buffer-substring-no-properties
                               (overlay-start ov)
                               (overlay-end ov)))
        nil
        (delete-overlay ov)))))

比之前简洁不少。

实话讲除了这个query-replace我没想到别的用法,以后可能会遇到?

pause的代码:

Emacs有类似的机制啊,叫Recursive Edit

https://www.gnu.org/software/emacs/manual/html_node/elisp/Recursive-Editing.html

包括在query-replace的时候就可以用C-r跳出替换过程手动修改buffer。改累了就直接睡觉,我buffer还蛮大的。改完用C-M-c回到query-replace

3 个赞

能联想到两种内置的思路set-transient-maprecursive-edit ,前者的一个应用是C-x C-+ (text-scale-adjust),后者的一个应用 Lisp Debugger,下面分别用他俩尝试实现你的 query-replace+,估计需要开启 lexical binding,貌似能用,但没认真测试

(defun your-query-replace (beg end)
  (interactive "r")
  (let ((string (buffer-substring-no-properties beg end))
        (ov (make-overlay beg end nil nil t)))
    (deactivate-mark)
    (overlay-put ov 'face '(:underline t))
    (set-transient-map
     (let ((map (make-sparse-keymap)))
       (define-key map (kbd "C-c C-c") #'ignore)
       map)
     (lambda ()
       (not (equal (this-command-keys-vector)
                   (vconcat (kbd "C-c C-c")))))
     (lambda ()
       (query-replace string (buffer-substring-no-properties
                              (overlay-start ov)
                              (overlay-end ov)))
       (delete-overlay ov)))))

(defun your-query-replace-v2 (beg end)
  (interactive "r")
  (let ((string (buffer-substring-no-properties beg end))
        (ov (make-overlay beg end nil nil t)))
    (deactivate-mark)
    (overlay-put ov 'face '(:underline t))
    (recursive-edit)
    (query-replace string (buffer-substring-no-properties
                           (overlay-start ov)
                           (overlay-end ov)))    
    (delete-overlay ov)))

:joy: 我应该先来问一句的。recursive-edit感觉最符合直觉。

这个挺有用的吧!

比如说 ctrl x o 但很多的时候会切换回去

再比如 ctrl x 1 但也想返回以前保存的窗口布局

这两个可以归为一类 buffer及window的undo 操作

timer常用的方式是 执行函数 重新设置timer

这个也应该可以这样 在继续的时候设置另一个继续

这样方式的最简单的例子就是 问卷调查中的用户输入

问卷调查在emacs里没用 但可以和orgmode结合

让一大堆的文字变得有趣 类似于org drill

checkdoc 也是一个带“暂停”功能的常用工具

1 个赞

(recursive-edit)

This function is called by the editor initialization to begin editing.

原来我打开Emacs之后就进入了一个recursive edit中,只有关掉Emacs才能从里面跑出来。Emacs, a cyberpunk editor.

1 个赞