新打开一个文件, 第一次用yasnippet, 如果用中文输入法会报错

c++-mode, 只在打开文件后第一次使用yasnippet的时候出现, 现象:

snippet展开之后, 光标处于高亮文本上, 此时切换为中文输入法, 输入文字后, emacs报错, 错误信息:

ad-Advice-c-forward-sws: Wrong type argument: integer-or-marker-p, nil

好像是跟cc-mode冲突了.

测试的snippet如下:

# name: test snippet
# key: test
# --
// hello ${1:world}

最新版yasnippet, emacs26.

Backtrace 是怎样的?

Debugger entered--Lisp error: (wrong-type-argument integer-or-marker-p nil)
  c-forward-sws()
  c-unfind-coalesced-tokens(87 92)
  c-before-change(87 92)
  delete-region(87 #<marker at 92 in c.cpp>)
  (if (= (progn (or (and (memq (type-of field) cl-struct-yas--field-tags) t) (signal 'wrong-type-argument (list 'yas--field field))) (aref field 2)) (progn (or (and (memq (type-of field) cl-struct-yas--field-tags) t) (signal 'wrong-type-argument (list 'yas--field field))) (aref field 3))) nil (delete-region (or from (progn (or (and (memq (type-of field) cl-struct-yas--field-tags) t) (signal 'wrong-type-argument (list 'yas--field field))) (aref field 2))) (progn (or (and (memq (type-of field) cl-struct-yas--field-tags) t) (signal 'wrong-type-argument (list 'yas--field field))) (aref field 3))))
  yas--skip-and-clear(#s(yas--field :number 1 :start #<marker at 86 in c.cpp> :end #<marker at 92 in c.cpp> :parent-field nil :mirrors nil :transform nil :modified-p t :next nil) 87)
  (progn (yas--skip-and-clear field end))
  (if (yas--skip-and-clear-field-p field beg end length) (progn (yas--skip-and-clear field end)))
  (lambda nil (if (yas--skip-and-clear-field-p field beg end length) (progn (yas--skip-and-clear field end))) (progn (or (and (memq (type-of field) cl-struct-yas--field-tags) t) (signal 'wrong-type-argument (list 'yas--field field))) (let* ((v field)) (aset v 7 t))) (yas--advance-end-maybe field (overlay-end overlay)) (save-excursion (yas--field-update-display field)) (yas--update-mirrors snippet))()
  funcall((lambda nil (if (yas--skip-and-clear-field-p field beg end length) (progn (yas--skip-and-clear field end))) (progn (or (and (memq (type-of field) cl-struct-yas--field-tags) t) (signal 'wrong-type-argument (list 'yas--field field))) (let* ((v field)) (aset v 7 t))) (yas--advance-end-maybe field (overlay-end overlay)) (save-excursion (yas--field-update-display field)) (yas--update-mirrors snippet)))
  (let nil (funcall '(lambda nil (if (yas--skip-and-clear-field-p field beg end length) (progn (yas--skip-and-clear field end))) (progn (or (and (memq (type-of field) cl-struct-yas--field-tags) t) (signal 'wrong-type-argument (list 'yas--field field))) (let* ((v field)) (aset v 7 t))) (yas--advance-end-maybe field (overlay-end overlay)) (save-excursion (yas--field-update-display field)) (yas--update-mirrors snippet))))
  eval((let nil (funcall '(lambda nil (if (yas--skip-and-clear-field-p field beg end length) (progn (yas--skip-and-clear field end))) (progn (or (and (memq (type-of field) cl-struct-yas--field-tags) t) (signal 'wrong-type-argument (list 'yas--field field))) (let* ((v field)) (aset v 7 t))) (yas--advance-end-maybe field (overlay-end overlay)) (save-excursion (yas--field-update-display field)) (yas--update-mirrors snippet)))))
  (let* ((syms (mapcar (function car) envvar)) (vals (mapcar (function (lambda (v-f) (eval (car (cdr v-f))))) envvar)) (body (function (lambda nil (if (yas--skip-and-clear-field-p field beg end length) (progn (yas--skip-and-clear field end))) (progn (or (and (memq (type-of field) cl-struct-yas--field-tags) t) (signal 'wrong-type-argument (list 'yas--field field))) (let* ((v field)) (aset v 7 t))) (yas--advance-end-maybe field (overlay-end overlay)) (save-excursion (yas--field-update-display field)) (yas--update-mirrors snippet)))) (binds nil)) (while syms (setq binds (cons (list (car-safe (prog1 syms (setq syms (cdr syms)))) (list 'quote (car-safe (prog1 vals (setq vals (cdr vals)))))) binds))) (eval (list 'let binds (list 'funcall (list 'quote body)))))
  (progn (let* ((syms (mapcar (function car) envvar)) (vals (mapcar (function (lambda (v-f) (eval (car (cdr v-f))))) envvar)) (body (function (lambda nil (if (yas--skip-and-clear-field-p field beg end length) (progn (yas--skip-and-clear field end))) (progn (or (and (memq (type-of field) cl-struct-yas--field-tags) t) (signal 'wrong-type-argument (list 'yas--field field))) (let* ((v field)) (aset v 7 t))) (yas--advance-end-maybe field (overlay-end overlay)) (save-excursion (yas--field-update-display field)) (yas--update-mirrors snippet)))) (binds nil)) (while syms (setq binds (cons (list (car-safe (prog1 syms (setq syms (cdr syms)))) (list 'quote (car-safe (prog1 vals (setq vals (cdr vals)))))) binds))) (eval (list 'let binds (list 'funcall (list 'quote body))))))
  (let ((envvar (progn (or (and (memq (type-of snippet) cl-struct-yas--snippet-tags) t) (signal 'wrong-type-argument (list 'yas--snippet snippet))) (aref snippet 1)))) (progn (let* ((syms (mapcar (function car) envvar)) (vals (mapcar (function (lambda (v-f) (eval (car (cdr v-f))))) envvar)) (body (function (lambda nil (if (yas--skip-and-clear-field-p field beg end length) (progn (yas--skip-and-clear field end))) (progn (or (and (memq (type-of field) cl-struct-yas--field-tags) t) (signal 'wrong-type-argument (list 'yas--field field))) (let* ((v field)) (aset v 7 t))) (yas--advance-end-maybe field (overlay-end overlay)) (save-excursion (yas--field-update-display field)) (yas--update-mirrors snippet)))) (binds nil)) (while syms (setq binds (cons (list (car-safe (prog1 syms (setq syms (cdr syms)))) (list 'quote (car-safe (prog1 vals (setq vals (cdr vals)))))) binds))) (eval (list 'let binds (list 'funcall (list 'quote body)))))))
  (progn (let ((envvar (progn (or (and (memq (type-of snippet) cl-struct-yas--snippet-tags) t) (signal 'wrong-type-argument (list 'yas--snippet snippet))) (aref snippet 1)))) (progn (let* ((syms (mapcar (function car) envvar)) (vals (mapcar (function (lambda (v-f) (eval (car (cdr v-f))))) envvar)) (body (function (lambda nil (if (yas--skip-and-clear-field-p field beg end length) (progn (yas--skip-and-clear field end))) (progn (or (and (memq (type-of field) cl-struct-yas--field-tags) t) (signal 'wrong-type-argument (list 'yas--field field))) (let* ((v field)) (aset v 7 t))) (yas--advance-end-maybe field (overlay-end overlay)) (save-excursion (yas--field-update-display field)) (yas--update-mirrors snippet)))) (binds nil)) (while syms (setq binds (cons (list (car-safe (prog1 syms (setq syms (cdr syms)))) (list 'quote (car-safe (prog1 vals (setq vals (cdr vals)))))) binds))) (eval (list 'let binds (list 'funcall (list 'quote body))))))))
  (unwind-protect (progn (let ((envvar (progn (or (and (memq (type-of snippet) cl-struct-yas--snippet-tags) t) (signal 'wrong-type-argument (list 'yas--snippet snippet))) (aref snippet 1)))) (progn (let* ((syms (mapcar (function car) envvar)) (vals (mapcar (function (lambda (v-f) (eval (car (cdr v-f))))) envvar)) (body (function (lambda nil (if (yas--skip-and-clear-field-p field beg end length) (progn (yas--skip-and-clear field end))) (progn (or (and (memq (type-of field) cl-struct-yas--field-tags) t) (signal 'wrong-type-argument (list 'yas--field field))) (let* ((v field)) (aset v 7 t))) (yas--advance-end-maybe field (overlay-end overlay)) (save-excursion (yas--field-update-display field)) (yas--update-mirrors snippet)))) (binds nil)) (while syms (setq binds (cons (list (car-safe (prog1 syms (setq syms (cdr syms)))) (list 'quote (car-safe (prog1 vals (setq vals (cdr vals)))))) binds))) (eval (list 'let binds (list 'funcall (list 'quote body)))))))) (set-match-data save-match-data-internal 'evaporate))
  (let ((save-match-data-internal (match-data))) (unwind-protect (progn (let ((envvar (progn (or (and (memq (type-of snippet) cl-struct-yas--snippet-tags) t) (signal 'wrong-type-argument (list 'yas--snippet snippet))) (aref snippet 1)))) (progn (let* ((syms (mapcar (function car) envvar)) (vals (mapcar (function (lambda (v-f) (eval (car (cdr v-f))))) envvar)) (body (function (lambda nil (if (yas--skip-and-clear-field-p field beg end length) (progn (yas--skip-and-clear field end))) (progn (or (and (memq (type-of field) cl-struct-yas--field-tags) t) (signal 'wrong-type-argument (list 'yas--field field))) (let* ((v field)) (aset v 7 t))) (yas--advance-end-maybe field (overlay-end overlay)) (save-excursion (yas--field-update-display field)) (yas--update-mirrors snippet)))) (binds nil)) (while syms (setq binds (cons (list (car-safe (prog1 syms (setq syms (cdr syms)))) (list 'quote (car-safe (prog1 vals (setq vals (cdr vals)))))) binds))) (eval (list 'let binds (list 'funcall (list 'quote body)))))))) (set-match-data save-match-data-internal 'evaporate)))
  (if (yas--snippet-live-p snippet) (let ((save-match-data-internal (match-data))) (unwind-protect (progn (let ((envvar (progn (or (and (memq (type-of snippet) cl-struct-yas--snippet-tags) t) (signal 'wrong-type-argument (list 'yas--snippet snippet))) (aref snippet 1)))) (progn (let* ((syms (mapcar (function car) envvar)) (vals (mapcar (function (lambda (v-f) (eval (car (cdr v-f))))) envvar)) (body (function (lambda nil (if (yas--skip-and-clear-field-p field beg end length) (progn (yas--skip-and-clear field end))) (progn (or (and (memq (type-of field) cl-struct-yas--field-tags) t) (signal 'wrong-type-argument (list 'yas--field field))) (let* ((v field)) (aset v 7 t))) (yas--advance-end-maybe field (overlay-end overlay)) (save-excursion (yas--field-update-display field)) (yas--update-mirrors snippet)))) (binds nil)) (while syms (setq binds (cons (list (car-safe (prog1 syms (setq syms (cdr syms)))) (list 'quote (car-safe (prog1 vals (setq vals (cdr vals)))))) binds))) (eval (list 'let binds (list 'funcall (list 'quote body)))))))) (set-match-data save-match-data-internal 'evaporate))) (lwarn '(yasnippet zombie) :warning "Killing zombie snippet!") (delete-overlay overlay))
  (let* ((inhibit-modification-hooks nil) (yas--inhibit-overlay-hooks t) (field (overlay-get overlay 'yas--field)) (snippet (overlay-get yas--active-field-overlay 'yas--snippet))) (if (yas--snippet-live-p snippet) (let ((save-match-data-internal (match-data))) (unwind-protect (progn (let ((envvar (progn (or (and (memq (type-of snippet) cl-struct-yas--snippet-tags) t) (signal 'wrong-type-argument (list 'yas--snippet snippet))) (aref snippet 1)))) (progn (let* ((syms (mapcar (function car) envvar)) (vals (mapcar (function (lambda (v-f) (eval (car (cdr v-f))))) envvar)) (body (function (lambda nil (if (yas--skip-and-clear-field-p field beg end length) (progn (yas--skip-and-clear field end))) (progn (or (and (memq (type-of field) cl-struct-yas--field-tags) t) (signal 'wrong-type-argument (list 'yas--field field))) (let* ((v field)) (aset v 7 t))) (yas--advance-end-maybe field (overlay-end overlay)) (save-excursion (yas--field-update-display field)) (yas--update-mirrors snippet)))) (binds nil)) (while syms (setq binds (cons (list (car-safe (prog1 syms (setq syms (cdr syms)))) (list 'quote (car-safe (prog1 vals (setq vals (cdr vals)))))) binds))) (eval (list 'let binds (list 'funcall (list 'quote body)))))))) (set-match-data save-match-data-internal 'evaporate))) (lwarn '(yasnippet zombie) :warning "Killing zombie snippet!") (delete-overlay overlay)))
  (if (or (not after\?) yas--inhibit-overlay-hooks (not (overlayp yas--active-field-overlay)) (not (overlay-buffer overlay)) (yas--undo-in-progress)) nil (let* ((inhibit-modification-hooks nil) (yas--inhibit-overlay-hooks t) (field (overlay-get overlay 'yas--field)) (snippet (overlay-get yas--active-field-overlay 'yas--snippet))) (if (yas--snippet-live-p snippet) (let ((save-match-data-internal (match-data))) (unwind-protect (progn (let ((envvar (progn (or (and (memq (type-of snippet) cl-struct-yas--snippet-tags) t) (signal 'wrong-type-argument (list 'yas--snippet snippet))) (aref snippet 1)))) (progn (let* ((syms (mapcar (function car) envvar)) (vals (mapcar (function (lambda (v-f) (eval (car (cdr v-f))))) envvar)) (body (function (lambda nil (if (yas--skip-and-clear-field-p field beg end length) (progn (yas--skip-and-clear field end))) (progn (or (and (memq (type-of field) cl-struct-yas--field-tags) t) (signal 'wrong-type-argument (list 'yas--field field))) (let* ((v field)) (aset v 7 t))) (yas--advance-end-maybe field (overlay-end overlay)) (save-excursion (yas--field-update-display field)) (yas--update-mirrors snippet)))) (binds nil)) (while syms (setq binds (cons (list (car-safe (prog1 syms (setq syms (cdr syms)))) (list 'quote (car-safe (prog1 vals (setq vals (cdr vals)))))) binds))) (eval (list 'let binds (list 'funcall (list 'quote body)))))))) (set-match-data save-match-data-internal 'evaporate))) (lwarn '(yasnippet zombie) :warning "Killing zombie snippet!") (delete-overlay overlay))))
  yas--on-field-overlay-modification(#<overlay from 86 to 92 in c.cpp> t 86 87 0)
  self-insert-command(1)
  funcall-interactively(self-insert-command 1)
  call-interactively(self-insert-command nil nil)
  command-execute(self-insert-command)

同样的配置文件同样的系统, emacs25没问题. 而且只在打开文件的第一个snippet出现, 后面就正常了.

从 Emacs -Q 试了下,像是遇到了同样的问题

Debugger entered--Lisp error: (wrong-type-argument integer-or-marker-p nil)
  c-forward-sws()
  c-unfind-coalesced-tokens(10 11)
  c-before-change(10 11)
  ns-delete-working-text()
  ns-unput-working-text()
  funcall-interactively(ns-unput-working-text)
  call-interactively(ns-unput-working-text nil [(ns-unput-working-text)])
  command-execute(ns-unput-working-text nil [(ns-unput-working-text)] t)

似乎是 cc-mode 跟 yasnippet 在某处起了冲突。


版本信息:

emacs-version
     => "27.0.50"
(chunyang-straight-git-version "yasnippet")
     => "0.12.2-41-g15761e8"

你是mac版本吗?我在mac里不能重现,配置文件一样。mac上的emacs是老一点的26,linux上是这几天刚编译的26。感觉像是emacs最近才引入的问题

是,不过终端里的 Emacs 照样能重现,可能跟平台没关。

(emacs-version)
     => "GNU Emacs 27.0.50 (build 13, x86_64-apple-darwin17.4.0, NS appkit-1561.20 Version 10.13.3 (Build 17D102))
 of 2018-03-14"
emacs-repository-version
     => "4c33ad4a244db59bfe128aa54380904efdc775ba"
window-system
     => ns

直接从 Emacs -Q 重现,完全排除掉个人配置。

用 emacs-24.5.2 简单试了下,没能重现。

emacs -Q 怎麼裝包的?

这个问题很重要,但没有好的解决办法。Emacs -Q 对于报告 Bug 非常必要。

Emacs -Q 没法安装包。你得自己想办法「安装」,有些包只有一个 Emacs Lisp 并且没有任意依赖,你可以:

$ emacs -Q -l yasnippet.el

如果有依赖的话,你可以看看包的作者有没有提供 Emacs -Q 的办法,Magit 有 M-x magit-emacs-Q-command、Helm 有 emacs-helm.sh,之外你就得手动设置 load-path 甚至还得考虑 byte-compile。

我暂时用 straight.el 作为包管理器,它的文档里有介绍 Emacs -Q 的办法,我自己也写了个命令干这事。


(defun chunyang-straight-emacs-Q-command (package)
  (interactive
   (list
    (straight--select-package "Package" nil 'installed)))
  (let ((cmd (mapconcat
              #'shell-quote-argument
              `(,(concat invocation-directory invocation-name)
                "-Q" "--eval" "(setq debug-on-error t)"
                "--load" "~/.emacs.d/straight/repos/straight.el/bootstrap.el"
                "--eval" ,(format "(straight-use-package '%s)" package))
              " ")))
    (message
     "Uncustomized %s command saved to kill-ring, please run it in a terminal"
     package)
    (kill-new cmd)))
1 个赞

emacs-25上不会出现. 另外26上用yasnippet 0.11.0也不会出现. 情况有点复杂

刚才又重现了一下, 这个问题竟然还会影响undo-tree, 不能undo了:

primitive-undo: Changes to be undone are outside visible portion of buffer

不过不是百分百重现, 用着有点担心

能否向上游上报一下问题? 我英文不行. 今天用了一次, 竟然会影响undo-tree, 导致不能undo, 还是有点严重的.

向哪个上游报告?Emacs?Yasnippet?

我没感受到什么影响,当然我不写 C++,也几乎不用 Yasnippet,我没更多的动力关注这个问题。

单纯这个问题倒还可以忍受, 主要是担心有潜在的其他问题, 引出一些非预期行为, 比如损坏文件或数据.

不过牵涉到中文输入法的问题, 确实很难上报, 他们不用输入法,

除了前面提到的能重现之外,我从来都没遇到过这个问题,或许是因为触发的条件很苛刻。

至于开发者能不能解决你的问题是你无法控制的,你该做的是把 Bug Report 写清楚。

报给yasnippet了, 也许他们能根据错误信息看出点什么

我不清楚你那边的具体情况,但有可能是默认的 c-noise-macro-name-re(值为 "\\<\\>" )可以匹配 中英文 字符串(如 "中a"),导致下面的 (match-end 1) 返回 nil。

我完全不懂 c-mode,你可以试试设置变量 c-noise-macro-namesc-noise-macro-with-parens-names,看看会不会再重现该问题。

确实是这个地方报的错:

这行代码是不是有问题? 搜索的正则表达式里没有分组, (match-end 1) 永远都是nil, 是不是作者写错了?

你可以参考函数 c-make-noise-macro-regexps,我还是不太理解 c-noise-macro-name-re 初值设为 "\\<\\>" 的意图。

没看那么多,就看到这个值有点异常,跟上游反馈了,他们应该会检查一下代码