Emacs 添加删除hook和修正文本高亮功能

一直是用msearch.el做文本高亮的, 最近发现一个问题, 每次执行复制命令之后, 被复制是文本会被高亮, 虽然复制之后文本已经不是处于选中状态了. 此时如果离开当前的buffer, 例如将文本粘贴到另一个buffer, 则高亮会一直保持着, 此时从另外的buffer返回, 发现这些高亮区域已经对光标移动命令没有反应了, 正常情况是, 当移动光标的时候, 任何高亮的部分应该会被取消.

原因是之前写的一个hook, 这个hook挂在post-command-hook上, 即每次执行一个命令的时候都会执行, 这个hook检查当前是否有被选中的文本, 如果有的话, 将当前buffer中所有相同的文本高亮, 否则清除所有现存的高亮部分, 代码如下

 
(setq need-cleanup t)
(add-hook 'post-command-hook
  (lambda ()
    (if (use-region-p)
      (progn
        ;(message (buffer-substring-no-properties (region-beginning) (region-end)))
        (msearch-cleanup)
        (msearch-set-word (buffer-substring-no-properties (region-beginning)  (region-end)))
        (setq need-cleanup t)
      )
      (progn 
        (if need-cleanup (msearch-cleanup) )
        (setq need-cleanup nil) 
      )
    )
  )
)
 

不知道什么原因, 执行复制即kill-ring-save命令之后, 选区已经没有了, 但是被复制的文本仍然保持高亮, 这样(use-region-p)就是总是false, 即不会执行清除高亮的动作.

于是我想改写kill-ring-save命令, 执行正常的kill-ring-save之后执行一次清除高亮的动作

 
(defun my-kill-ring-save ()
  (interactive )
  (kill-ring-save (region-beginning) (region-end))
  (msearch-cleanup)
)
 
(global-set-key "\C-c" 'my-kill-ring-save)
 

结果并没有起作用, 合理的猜测是文本被高亮的动作其实是在king-ring-save之后执行的, 因此这里的(msearch-cleanup)没有起到作用. 那么应该在post-command-hook里面执行.

这里要注意add-hook是将hook加到一个hook链上, 没执行一次, hook列表就会增加一个元素, 如果要更新一个hook, 应该首先将其删掉, 方法如下, 打开ielm, 执行.

 
(remove-hook 'post-command-hook (first post-command-hook))
 

反复执行直到返回结果是nil.

修改之后的hook如下

 
(setq need-cleanup t)
(add-hook 'post-command-hook
  (lambda ()
    (if (and (use-region-p) (not need-cleanup))
      (progn
        ;(message (buffer-substring-no-properties (region-beginning) (region-end)))
        (msearch-cleanup)
        (msearch-set-word (buffer-substring-no-properties (region-beginning)  (region-end)))
        (setq need-cleanup t)
      )
      (progn 
        (if need-cleanup (msearch-cleanup) )
        (setq need-cleanup nil) 
      )
    )
  )
)
 
(defun my-kill-ring-save ()
  (interactive )
  (kill-ring-save (region-beginning) (region-end))
  (setq need-cleanup t)
)
 
(global-set-key "\C-c" 'my-kill-ring-save)
 

方法是在king-ring-save的时候, 设置一个全局变量, 这个变量导致之后执行的post-command-hook中的if语句获得通过, 无论执行if的那一个分支, 都会清除高亮 .