【问题标题】:Could Emacs fontify elisp string constants?Emacs 可以字体化 elisp 字符串常量吗?
【发布时间】:2013-10-21 02:10:23
【问题描述】:

困境:可读性还是可维护性?

让我们看看下面的函数。 它做什么并不重要,重要的是 它使用了两倍的字符串"(let\\*?[ \t]*":

(defun setq-expression-or-sexp ()
  "Return the smallest list that contains point.
If inside VARLIST part of `let' form,
return the corresponding `setq' expression."
  (interactive)
  (ignore-errors
    (save-excursion
      (up-list)
      (let ((sexp (preceding-sexp)))
        (backward-list 1)
        (cond
         ((looking-back "(let\\*?[ \t]*")
          (cons 'setq
                (if (= (length sexp) 1)
                    (car sexp)
                  (cl-mapcan
                   (lambda (x) (unless (listp x) (list x nil)))
                   sexp))))
         ((progn
            (up-list)
            (backward-list 1)
            (looking-back "(let\\*?[ \t]*"))
          (cons 'setq sexp))
         (t
          sexp))))))

由于必须在两个(或更多)位置更新字符串是一件令人头疼的事情, 我必须像这样defconst

(defconst regex-let-form "(let\\*?[ \t]*")

虽然代码变得更易于维护,但它的可读性也降低了, 因为很难一眼看出regex-let-form到底是什么:

(defun setq-expression-or-sexp ()
  "Return the smallest list that contains point.
If inside VARLIST part of `let' form,
return the corresponding `setq' expression."
  (interactive)
  (ignore-errors
    (save-excursion
      (up-list)
      (let ((sexp (preceding-sexp)))
        (backward-list 1)
        (cond
         ((looking-back regex-let-form)
          (cons 'setq
                (if (= (length sexp) 1)
                    (car sexp)
                  (cl-mapcan
                   (lambda (x) (unless (listp x) (list x nil)))
                   sexp))))
         ((progn
            (up-list)
            (backward-list 1)
            (looking-back regex-let-form))
          (cons 'setq sexp))
         (t
          sexp))))))

想法:为什么不两者兼而有之?

既然它是一个常数,为什么不font-lock它 并使regex-let-form 看起来好像是"(let\\*?[ \t]*"? 这是一项可行的工作,因为:

  1. 可以像这样对标识符进行字体锁定:http://www.emacswiki.org/emacs/PrettyLambda, 甚至是:rainbow-mode

  2. 并且可以对常量进行字体锁定。它已经为 c++-mode 完成了, 但据我所知,emacs-lisp-mode 还没有。

那么剩下的只是将两者联系起来。不幸的是,我不知道 足够的font-lock innards 可以做到这一点,但也许其他人会这样做? 还是已经有一个包可以做到这一点?

【问题讨论】:

    标签: emacs elisp


    【解决方案1】:

    this answer 调整代码, 我已经解决了这个问题:

    (font-lock-add-keywords 
     'emacs-lisp-mode
     '((fl-string-constant . 'font-lock-constant-face)) 'append)
    
    (defun fl-string-constant (_limit)
      (while (not 
              (ignore-errors
                (save-excursion
                  (skip-chars-forward "'")
                  (let ((opoint (point))
                        (obj (read (current-buffer)))
                        obj-val)
                    (and (symbolp obj)
                         (risky-local-variable-p obj)
                         (special-variable-p obj)
                         (stringp (setq obj-val (eval obj)))
                         (progn
                           (put-text-property 
                            (1- (point)) (point) 'display
                            (format "%c\"%s\"" (char-before) obj-val))
                           (set-match-data (list opoint (point))) 
                           t))))))
        (if (looking-at "\\(\\sw\\|\\s_\\)")
            (forward-sexp 1)
          (forward-char 1)))
      t)
    

    这会在常量名称之后显示字符串常量的值。 它也适用于字体化字符串常量。 速度有点问题 - 欢迎提出改进建议。

    另外,我找不到比 risky-local-variable-p 更好的东西来确定 这是一个常数。文档说defconst 标记了变量 特殊和冒险,但仅此而已。

    【讨论】:

    • @Drew,我使用了另一个答案中的代码。谢谢。您能建议如何加快该功能吗?
    • 还没有真正看过它,但我的其他答案应该可以满足您的需求。在上面的代码中,您正在读取每个引用的 Lisp 对象等。如果您首先检查您是否正在查看一个符号,那么您不需要读取每个对象,甚至任何对象——只需检查符号值。但正如我所说,我还没有真正研究过你的代码等。试试hl-defined.el——我想它会做你想做的。
    【解决方案2】:

    hl-defined.el(今天更新,2013-10-20)可以像这样突出显示常量 Emacs-Lisp 符号,即当前值为符号本身的变量。如果您的 defconst 已被评估,那么这将满足您的要求。

    【讨论】:

    • 它只是改变了脸,对吧?不替换常量的值吗?
    • 对不起,我的错。我已经恢复了我今天对hl-defined.el 所做的更改。它们只是使突出显示 self-evaluation 常量成为可能,这没有用。如果不是Emacs bug #15666,那么处理使用defconst 定义的常量会很容易。唉,这种回归意味着人们不能再建议特殊的形式。如果您在此处回复您已看到此撤消评论,我将删除此答案。
    • 太糟糕了。保留链接-修复错误的更多动力:)
    【解决方案3】:

    这似乎有效(来源:http://www.emacswiki.org/emacs/PrettyLambda):

    (font-lock-add-keywords 'emacs-lisp-mode
      `(("\\<\\(regex-let-form\\)\\>" (0 (prog1 nil
                                           (compose-region (match-beginning 1)
                                                           (match-end 1)
                                                           "\"(let\\\\*?[ \\t]*\""))))))
    

    虽然我认为将regex-let-form 添加到现有的let 块中会是一个更清洁的解决方案:

    (let ((sexp (preceding-sexp))
          (regex-let-form "(let\\*?[ \t]*"))
        ...
    

    【讨论】:

    • 我试过了,它给了我一个奇怪的 unicode char 而不是 regex-let-form。
    • 可能是版本问题。我正在使用 v24.2。
    • 我不认为这是一个奇怪的 unicode 字符。更有可能只是该字符串中的每个字符,彼此堆叠。
    【解决方案4】:

    也许你的例子并不能说明真正的问题,你确实想要做一些显示替换或字体锁定,正如你所说的。

    但我会回答你的例子和提出的问题,关于可维护性与可读性:只需let-bind 你的正则表达式。与defconst 不同,绑定将在附近,并且与绑定变量的出现明显相关。

    这通常是人们所做的。同样,您可能已经想到了另一个用例 --- 我只是对提出的问题做出狭隘的回应。

    【讨论】:

    • let-bind,是显而易见的答案,但显然是有缺陷的。正则表达式可以跨函数使用,它也是let 不反映的常量。最后也是最重要的是,可读性丢失了。当我读到looking-back 的一行时,我真的很想看看我在看/there/,而不是高出10 行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-17
    相关资源
    最近更新 更多