合并上下两行的时候,如果是中文,怎样避免增加的空格?

看到这个问题 两行代码格式化为一行,快捷键 的时候,突然想到一个困扰了我很久的问题,就是合并上下两行的时候,如果是中文怎样避免空格?

我想应该从这个函数入手: delete-indentation,它调用了fixup-whitespace这个函数。解决办法应该是调整 fixup-whitespace,使它在前文是中文的时候不添加空格。但是这里是用advice还是用cl-letf比较合适呢?欢迎大家来献策。

Edit: 答案在 17 楼。根据试用过的朋友反馈,除了这个版本以外,这个帖子内的其它版本可能跟 Evil 配合不好。

方案1: advise fixup-whitespace,缺点是不知道这个函数在别的地方有没有用到,会不会造成什么问题。 方案2: 使用 cl-letf,仅针对 delete-indentation 这个函数改变 fixup-whitespace 的定义。

还有什么方案欢迎大家赐教!

搞一个投票吧:

  • 方案1 advise delete-indentation
  • 方案2 advise fixup-whitespace
  • 方案3cl-letf

0 投票者

恐怕只动 delete-indentation 就可以了。

我目前倾向于方案3

也可以试试直接定义新的命令,这样也不用担心有副作用了

(defun my-fixup-whitespace ()
  "同 `fixup-whitespace' 但如前面是中文字符则不留空格."
  (interactive "*")
  (fixup-whitespace)
  (when (save-excursion (forward-char -1)
                        (looking-at "\\cc"))
    (delete-char 1)))

(defun my-delete-indentation ()
  "同 `delete-indentation' 但如前面是中文字符则不留空格."
  (interactive "*")
  (delete-indentation)
  (my-fixup-whitespace))

应该都可以,advice 也可以当作用来临时重定义一个函数,如果及时去掉 advice 的话,比如下面 fixup-whitespace 的 advice 只会对 delete-indentaion 有效。

(define-advice delete-indentation (:around (old-fun &rest r) 中文无须空格)
  (unwind-protect
      (progn
        (define-advice fixup-whitespace (:around (old-fun &rest r) 是的,中文无须空格)
          ;; 仅仅是为了
          (apply old-fun r))
        ;; 举例子
        (apply old-fun r))
    (advice-remove 'fixup-whitespace #'fixup-whitespace@是的,中文无须空格)))
1 个赞

确实,这样简单直接

但是下面的 advice 没看太明白,能不能把注释以外的中文换成英文字符,谢谢!

PS:这是 25 的新 advice 语法吗?我都是用 defadvice,所以没看太明白。。。

嗯,上面的代码是可以用 Emacs 25 运行的,但并没有修改 delete-indentation 的行为,用了举个例子而已。我不会老的 advice 的方法(defadvice),我开始频繁地用 Emacs 的时候(两三年前)它就已经被新的方法取代了。

好吧,我肯定是被哪篇博文误导了,我接触 advice 就是从 defadvice 开始的 :sob:

这个 Advice 的方法(nadvice.el)不是 Emacs 25 才有的,只是 define-advice 是 Emacs 25 新加入的一个辅助宏,效果与 defun + advice-add 一样

;; Emacs 24.4+
(define-advice emacs-version (:around (&rest _) vim)
  (message "Vim 7.4"))

等价于

;; Emacs 25.1+
(defun emacs-version@vim (&rest _)
  (message "Vim 7.4"))
(advice-add 'emacs-version :around (function emacs-version@vim)))
2 个赞

那个 @ 是强制性的吗?还是约定俗成的?

这个回答关于 advice 讲得特别好

如果不是我权限不够,我一定upvote一下

不是。不是。

@define-advice 用来构造函数名的连接号。如果你用 defun + advice-add 的话,随便用什么名字都可以。

1 个赞

还有一个问题,用这种方式 Advise 的话,interactive 怎样处理?我之所以这么问是看了这个: EmacsWiki: Advising Functions

遇到实际的情况再考虑吧,因为很少见。


在函数里面需要判断是否一个命令是从按键还是从 Lisp 调用的情况比较少见,就算用也只是用来显示一条提示,文档(interactive-p / called-interactively-p 的 Doc string)也说要尽量避免。

比如,用户只用 M-x foo 时才打印提示,从 Lisp 调用 (foo buffer) 不打印,如果你 Advice 了 foo 还需要这个打印功能,可以用 funcall-interactively

(defun foo (buffer)
  (interactive "bBuffer: ")
  (let ((filename (buffer-file-name (get-buffer buffer))))
    (when (called-interactively-p 'interactive)
      (message "The buffer file name is %s" filename))
    filename))

(define-advice foo (:around (old-fun buffer) bar)
  (funcall-interactively old-fun (current-buffer)))
1 个赞

特别感谢您的耐心回复,我学到了很多东西 @xuchunyang

我修改了您的代码,简单测试了一下,放到gist上了。有需要的同学可以试用,意见反馈留在 gist 上或这里我好修改: https://gist.github.com/et2010/8dadd64a7e4193b7c93f6a4ba6226ea6#file-fixup-join-lines-in-chinese-el

Edit: 根据 @lxeg0429 的反馈,这个版本跟 evil 配合不好,所以我把链接编辑掉了,推荐用下面一个使用 cl-letf 的版本。

这里是另外一个版本,这个版本用到了 cl-letf 和 around advice,个人觉得这个版本可能更不容易出错,因为上一个版本并未考虑如果原始函数并未添加空格的情况(当然也许这些情形并不重叠)。 Remove needless whitespace when joining two lines in Chinese · GitHub

(defun et/fixup-whitespace ()
  "Fixup white space between objects around point.
Leave one space or none, according to the context."
  (interactive "*")
  (save-excursion
    (delete-horizontal-space)
    (if (or (looking-at "^\\|\\s)")
            (save-excursion (forward-char -1)
                            ;; we adapted the regexp here:
                            (looking-at "\\cc\\|$\\|\\s(\\|\\s'")))
        nil
      (insert ?\s))))

(defun et/delete-indentation (old-func &rest args)
  (cl-letf (((symbol-function 'fixup-whitespace) #'et/fixup-whitespace))
    (apply old-func args)))

(advice-add #'delete-indentation :around #'et/delete-indentation)

PS: 未考虑 interactive 的问题, @xuchunyang 大神帮我看看 PPS:这个版本中的 et/fixup-whitespace 函数是复制粘贴的 emacs 原生函数,只是把其中的正则表达式改了。 PPPS:这种Advice方式真的是比旧的 defadvice 好用太太太太多了,谢谢 @xuchunyang 大神的推荐!

1 个赞

不用考虑,(interactive ...) 是照常工作的。上面(也就是 EmacsWiki: Advising Functions 的末尾)说的是当你要 Advice 的函数体中用到了 interactive-p 时的情况,在 delete-indentation 中并没有用到。

好的,谢谢指点

如何把这个功能直接绑定在S-j上啊,判断如果末尾是中文就删除一个空格,如果是英文就用原生的功能? 能不能给段代码啊?