简体   繁体   English

Emacs可以为elisp字符串常量提供支持吗?

[英]Could Emacs fontify elisp string constants?

The Dilemma: readability or maintainability? 困境:可读性还是可维护性?

Let's look at the following function. 我们来看看以下功能。 It doesn't really matter what it does, the important part is that it's using twice the string "(let\\\\*?[ \\t]*" : 它的作用并不重要,重要的是它使用两次字符串"(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))))))

Since it's a headache having to update the string in two (or more) locations, I'd have to defconst it like so: 因为在两个(或更多)位置更新字符串是一件令人头疼的defconst ,所以我必须像这样defconst它:

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

Although the code became more maintainable, it became less readable as well, because it's hard to see at a glance what regex-let-form really is: 虽然代码变得更易于维护,但它的可读性也降低了,因为很难一眼看出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))))))

The idea: why not both? 这个想法:为什么不两个?

Since it's a constant anyway, why not font-lock it and make regex-let-form appear as if it's "(let\\\\*?[ \\t]*" ? It's a feasable job, since: 既然它是一个常数,为什么不对它进行font-lock并使regex-let-form看起来像是"(let\\\\*?[ \\t]*" ?这是一个可行的工作,因为:

  1. It's possible to font-lock identifiers like so: http://www.emacswiki.org/emacs/PrettyLambda , or even so: rainbow-mode . 可以像这样对字符串进行字体锁定: http//www.emacswiki.org/emacs/PrettyLambda ,甚至是: rainbow-mode

  2. And it's possible to font-lock constants. 并且可以使用字体锁定常量。 It's already done for c++-mode, but not yet for emacs-lisp-mode, as far as I know. 据我所知,它已经完成了c ++模式,但还没有用于emacs-lisp模式。

Then it remains only to connect the two. 然后它仍然只是连接两者。 Unfortunately, I don't know enough of font-lock innards to do it, but maybe someone else does? 不幸的是,我不太了解font-lock内脏,但也许其他人呢? Or is there already a package that does this? 或者已经有一个包这样做?

Tweaking the code from this answer , I've solved the problem: 这个答案调整代码,我已经解决了问题:

(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)

This displays the value of a string constant right after the constant name. 这将在常量名称后面显示字符串常量的值。 It works quite nicely with fontified string constants as well. 它与精确的字符串常量一起运行得非常好。 Speed is a bit of an issue - suggestions to improve are welcome. 速度是一个问题 - 欢迎提出改进建议。

Also, I couldn't find anything better than risky-local-variable-p to determine that it's a constant. 另外,我找不到比risky-local-variable-p更好的东西来确定它是一个常数。 The doc says that defconst marks the variable as special and risky, but nothing else. 该文档说, defconst将变量标记为特殊且有风险,但没有别的。

hl-defined.el (updated today, 2013-10-20) can highlight constant Emacs-Lisp symbols as such, that is, variables whose current value is the symbol itself. hl-defined.el (今天更新,2013-10-20)可以突出显示常量Emacs-Lisp符号,即当前值为符号本身的变量。 If your defconst has been evaluated then this will do what you are requesting. 如果您的defconst已被评估,那么这将按照您的要求进行。

This seems to work (source: http://www.emacswiki.org/emacs/PrettyLambda ): 这似乎有效(来源: 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]*\""))))))

Although I think adding regex-let-form into the existing let block would be a cleaner solution: 虽然我认为在现有的let块中添加regex-let-form将是一个更清晰的解决方案:

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

Perhaps your example is not indicative of the real problem, and you really do want to do some display replacement or font-locking, as you say. 也许你的例子没有表明真正的问题,你真的想做一些显示替换或字体锁定,正如你所说。

But I will answer wrt your example and the problem as posed, regarding maintainability vs readability: Just let -bind your regexp. 但是我会回答你的例子以及所提出的问题,关于可维护性与可读性:只需let你的正则表达式绑定。 The binding, unlike a defconst will be nearby and clearly related to the occurrences of the bound variable. defconst不同,绑定将在附近,并且与绑定变量的出现明显相关。

This is typically what people do. 这通常是人们所做的。 Again, you might have had another use case in mind --- I am responding only to the problem as posed narrowly. 再说一次,你可能还有另一个用例 - 我只是对这个问题作出了狭隘的回应。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM