自带 HTML+ 模式和 electric-pair-mode 在 JS 区域的一个问题

自带的 HTML+ 模式很酷,不知道用了什么 hack 支持 CSS、JS,不只是语法高亮,keymap 、甚至代码补全也支持。目前我遇到了一个问题,我开启了 electric-pair-mode 这个全局模式来输入括号,在 HTML+ 模式下输入 <,会得到 <>,这很对,因为这是 HTML 标签,但是光标移动到 JS 中还是这样就不对了,我已经有 N 次因为它把 for 循环写错,后面多出来的 > 就是 electric-pair-mode 插入的:

for (let i = 0; i < 10; i++>)

不知道如何避免。

awesome-pair会自动识别vue文件的html,css,js不同部分,智能决定什么时候补全括号。

1赞

之前不知道在哪看到的避免方式:

  ;; disable <> auto pairing in electric-pair-mode for org-mode
  (add-hook 'org-mode-hook
            '(lambda ()
               (setq-local electric-pair-inhibit-predicate
                           `(lambda (c)
                              (if (char-equal c ?<) t
                                (,electric-pair-inhibit-predicate c))))))

我没找到 HTML+,它在哪个文件?


找到了,正式名称是 mhtml-mode

用了一下发现,它有子模式的概念:

(defconst mhtml--js-submode
  (mhtml--construct-submode 'js-mode
                            :name "JS"
                            :end-tag "</script>"
                            :syntax-table js-mode-syntax-table
                            :propertize #'js-syntax-propertize
                            :keymap js-mode-map))

进入 JS 区域自动切换成 js-mode (可运行 M-: major-mode 观察)。这就好办了,把 < 加入 electric-pair 禁止列表即可:

(add-to-list 'electric-pair-inhibit-predicate-mode-chars-alist
             '(js-mode . (?<)))

有这个变量吗?我这没有,搜索了下 Emacs 源代码也没发现。是不是你自己定制了 electric-pair-inhibit-predicate

哎呀,不好意思。是我自己定义的,很久没改这部分配置,都忘了,完整代码:

;;; pair.el --- Electric Pair -*- lexical-binding: t; -*-

(defvar electric-pair-inhibit-predicate-mode-chars-alist
  '((t . nil))
  "A list of major-mode and inhibit chars. For example,

(setq electric-pair-inhibit-predicate-mode-chars-alist
      '((example-mode . (?<))
        (t . nil)))

t means for all modes.")

(defun electric-pair-inhibit-predicate-function (c)
  (let ((chars
         (-concat
          (assoc-default major-mode electric-pair-inhibit-predicate-mode-chars-alist)
          (assoc-default t          electric-pair-inhibit-predicate-mode-chars-alist))))
    (or (member c chars)
        (electric-pair-default-inhibit c))))

(with-eval-after-load 'elec-pair
  (setq electric-pair-inhibit-predicate
        #'electric-pair-inhibit-predicate-function))

;;; pair.el ends here
1赞

我也试着改了下 electric-pair-inhibit-predicate,只修复 MHTML 下 JS 的问题,简单试了下符合预期

(defun chunyang-mhtml-mode-electric-pair-inhibit-predicate (char)
  (or (and (eq major-mode 'js-mode)
           (= char ?<))
      (electric-pair-default-inhibit char)))

(defun chunyang-mhtml-mode-setup ()
  (setq-local electric-pair-inhibit-predicate
              #'chunyang-mhtml-mode-electric-pair-inhibit-predicate))

(add-hook 'mhtml-mode-hook #'chunyang-mhtml-mode-setup)

electric-pair会不会自动补全是syntax table决定的吧,应该改syntax table。另外输入html一定要用emmet-mode的emmet-expand-line/emmet-expand-yas

https://emmet.io/

1赞

不懂 mhtml 的 submode 的实现,在单独的 js-mode 下是正常的,< 不会匹配。

被主模式“传染”的吧。

在 mhtml 模式下,文件任何地方执行 (electric-pair-syntax-info ?<) 都会得到:

(40 62 nil nil)

这条 syntax 在 sgml 模式中有定义(mhtml << html << sgml):

(defun sgml-make-syntax-table (specials)
  (let ((table (make-syntax-table text-mode-syntax-table)))
    (modify-syntax-entry ?< "(>" table)
    ;;                       ^^
    ...
    ))

在单独的 js 模式下 (electric-pair-syntax-info ?<) 返回 nil。