让 C-x C-e 支持被跨行注释的代码

C-x C-e (eval-last-sexp) 支持

;; (+ 1 2 3)

却不支持

;; (+ 1
;;    2
;;    3)

之前遇到这种情况就只能取消注释、执行、加上注释,有点儿烦。刚刚想了个一步到位的办法:

(define-advice elisp--preceding-sexp (:around (old-fun) multiline-comment)
  "Support sexp in multiline comment."
  (condition-case err
      (funcall old-fun)
    (scan-error
     (if (nth 4 (syntax-ppss))
         (let ((work-buffer (current-buffer))
               (temp-buffer (generate-new-buffer " *temp*"))
               found sexp error)
           (with-current-buffer temp-buffer
             (delay-mode-hooks (emacs-lisp-mode)))
           (save-excursion
             (comment-normalize-vars)
             (while (and (comment-beginning)
                         (not found))
               (let ((code (buffer-substring-no-properties
                            (point) (line-end-position))))
                 (with-current-buffer temp-buffer
                   (goto-char (point-min))
                   (insert code ?\n)
                   (goto-char (point-max))
                   (condition-case err
                       (setq sexp (funcall old-fun)
                             found t)
                     (scan-error (setq error err)))))
               (when (= -1 (forward-line -1))
                 (error "elisp--preceding-sexp@multiline-comment error"))
               (goto-char (line-end-position))))
           (cond (found sexp)
                 (error (signal (car error) (cdr error)))
                 (t (error "elisp--preceding-sexp@multiline-comment error"))))
       (signal (car err) (cdr err))))))
5 个赞

给我的 *scratch* 做做广告

找链接中。。。。

刚才在做测试的时候想到这个帖子,现在可以用 separedit 来实现:

(defun separedit/eval-last-sexp-in-comment ()
  (interactive)
  (let ((separedit-default-mode 'emacs-lisp-mode))
    (with-current-buffer (separedit)
      (prog1 (call-interactively #'eval-last-sexp)
        (execute-kbd-macro (kbd "C-c C-k"))))))
6 个赞

决定把这个函数纳入日常配置:

(keymap!
 :mode emacs-lisp-mode
 :prefix "C-x"
 "C-e" (lambda ()
         (interactive)
         (call-interactively
          (if (separedit--point-at-comment)
              #'separedit/eval-last-sexp-in-comment
            #'eval-last-sexp)))))
1 个赞

好评 :100: www

这个功能还挺有用的,我挖下坟 :grinning:

在 docstring 因为引号需要 escape, 单引号从 Emacs 29 起还需要 \=' ,内含的 example 无法直接 eval,不知道 separedit 有无自动处理的方案?

比如这样:

(defun test ()
  "testing DOC:
  (global-set-key (kbd \"C-c y\") \\='company-yasnippet)"
  #'ignore)
(defun separedit/eval-last-sexp-in-comment ()
  (interactive)
  (let ((separedit-default-mode 'emacs-lisp-mode)
        (separedit-inhibit-edit-window-p t)
        (strp (separedit--point-at-string)))
    (with-current-buffer (separedit)
      (when strp
        (save-excursion
          (while (re-search-backward "\\\\\\{2\\}='" nil t)
            (unless (separedit--point-at-string)
              (replace-match "'" nil t)))))
      (unwind-protect (call-interactively #'eval-last-sexp)
        (edit-indirect-abort)))))
"(apply \\='(1+ 1))"
;; => 2

"(prog1 \"\\='(1+ 1)\")"
;; => "\\='(1+ 1)"
1 个赞

感谢大佬。

合并一下:

  ;; SRC 2023-02-20: https://emacs-china.org/t/7760
  (defun separedit/eval-last-sexp-in-comment ()
    "`eval-last-sexp' in comment."
    ;; test:
    ;; (+ 1 2 3)
    ;; (+ 1
    ;;    2
    ;;    3)
    (interactive)
    (let ((separedit-default-mode 'emacs-lisp-mode)
          (separedit-inhibit-edit-window-p t))
      (with-current-buffer (separedit)
        (unwind-protect (call-interactively #'eval-last-sexp)
          (separedit-abort)))))

  ;; SRC 2023-02-20: https://emacs-china.org/t/7760/8
  (defun separedit/eval-last-sexp-in-docstring ()
    "`eval-last-sexp' in docstring.
    test:
    (apply \\='(1+ 1))
    (prog1 \"\\='(1+ 1)\")"
    (interactive)
    (let ((separedit-default-mode 'emacs-lisp-mode)
          (separedit-inhibit-edit-window-p t)
          (strp (separedit--point-at-string)))
      (with-current-buffer (separedit)
        (when strp
          (save-excursion
            (while (re-search-backward "\\\\\\{2\\}='" nil t)
              (unless (separedit--point-at-string)
                (replace-match "'" nil t)))))
        (unwind-protect (call-interactively #'eval-last-sexp)
          (edit-indirect-abort)))))

  (defun z/eval-last-sexp ()
    "Make eval-last-sexp also work in comment and docstring."
    (interactive)
    (require 'separedit)
    (call-interactively
     (cond ((separedit--point-at-comment) #'separedit/eval-last-sexp-in-comment)
           ((separedit--point-at-string) #'separedit/eval-last-sexp-in-docstring)
           (t #'eval-last-sexp))))

  (define-key emacs-lisp-mode-map (kbd "C-x C-e") #'z/eval-last-sexp)