在Emacs的C/C++模式中,将#if 0...#endif块中的代码面改变为注释面。

12
我正在尝试将其他代码编辑器中的功能添加到我的Emacs配置中,即在#if 0…#endif块内的C/C++代码自动设置为注释面/字体。根据我的测试,cpp-highlight-mode做了类似于我想要的事情,但需要用户操作。似乎将其与font-lock功能结合起来是使行为自动化的正确选项。
我已经成功地按照GNU文档中的示例更改了单行正则表达式的面貌。例如:
(add-hook 'c-mode-common-hook
  (lambda ()
    (font-lock-add-keywords nil
      '(("\\<\\(FIXME\\|TODO\\|HACK\\|fixme\\|todo\\|hack\\)" 1 
        font-lock-warning-face t)))))

该工具可以在文件中任何位置突出显示与调试相关的关键字,但是我在使用多行正则表达式匹配#if 0...#endif时遇到了问题。我在这篇文章(How to compose region like "<?php foo; bar; ?>")中找到了一些有用的信息,建议Emacs必须明确指定允许多行匹配。但是这段代码:

(add-hook 'c-mode-common-hook
  (lambda ()
    '(progn
      (setq font-lock-multiline t)
      (font-lock-add-keywords nil
        '(("#if 0\\(.\\|\n\\)*?#endif" 1
          font-lock-comment-face t))))))

仍然对我不起作用。也许我的正则表达式有误(尽管使用 M-x re-builder 看似有效),我搞错了语法,或者完全走了错误的方向。我正在使用基于 GNU Emacs 23.2.50.1 的 Aquamacs 2.1,在 OS X 10.6.5 上运行,如果这有所区别,请知悉。
如有帮助将不胜感激!
1个回答

15
即使您成功使用了多行正则表达式,仍然会在嵌套的#ifdef / #endif中出现问题,因为它会在第一个#endif处停止字体锁定。尽管我不确定对于大文件是否会有明显的减速,但这段代码是有效的:

即使你使用多行正则表达式也无济于事,因为它无法处理嵌套的 #ifdef / #endif,并且会在第一个#endif处停止语法高亮。这段代码可以解决问题,但对于大文件可能会稍微慢一些:

(defun my-c-mode-font-lock-if0 (limit)
  (save-restriction
    (widen)
    (save-excursion
      (goto-char (point-min))
      (let ((depth 0) str start start-depth)
        (while (re-search-forward "^\\s-*#\\s-*\\(if\\|else\\|endif\\)" limit 'move)
          (setq str (match-string 1))
          (if (string= str "if")
              (progn
                (setq depth (1+ depth))
                (when (and (null start) (looking-at "\\s-+0"))
                  (setq start (match-end 0)
                        start-depth depth)))
            (when (and start (= depth start-depth))
              (c-put-font-lock-face start (match-beginning 0) 'font-lock-comment-face)
              (setq start nil))
            (when (string= str "endif")
              (setq depth (1- depth)))))
        (when (and start (> depth 0))
          (c-put-font-lock-face start (point) 'font-lock-comment-face)))))
  nil)

(defun my-c-mode-common-hook ()
  (font-lock-add-keywords
   nil
   '((my-c-mode-font-lock-if0 (0 font-lock-comment-face prepend))) 'add-to-end))

(add-hook 'c-mode-common-hook 'my-c-mode-common-hook)

编辑: 考虑到#else

编辑 #2: 更巧妙的代码处理if / else / endif 的任意嵌套。


我将“else”添加为另一个关键字来停止注释。因此,一行变成了:(while (and (> depth 0) (re-search-forward "^\s-*#\s-*\(if\|endif\|else\)" limit 'move))。此外,对于小文件(<100-200k),减速很小。在较大的文件(4.3MB)上,它是明显的 - 但我不经常编辑那么大的文件。 - pogopop77
@pogopop77:处理#else比你所做的要棘手一些,因此我更新了代码。它不能处理在#if 0内部的#else中的#if 0,但我可能会稍后更新它。另外:200k是一个小文件吗?;) - scottfrazer
更新以支持任意嵌套。而且更简单,这总是一个积极的信号 :) - scottfrazer
运行得非常好。我想我认为我正在处理的任何代码都是“小”的 :). 我使用了SQLite合并的C文件作为我的折磨测试。 - pogopop77
这个非常好用!有没有 elisp 经验,可以让它处理 #if 1 #else #endif 代码? - Joseph Lisee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接