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

Posted

技术标签:

【中文标题】在 Emacs 的 C/C++ 模式下,将 #if 0...#endif 块中的代码面更改为注释面【英文标题】:In C/C++ mode in Emacs, change face of code in #if 0...#endif block to comment face 【发布时间】:2011-05-31 17:53:04 【问题描述】:

我正在尝试将其他一些代码编辑器中的功能添加到我的 Emacs 配置中,从而 #if 0...#endif 块中的 C/C++ 代码自动设置为注释面/字体。根据我的测试,cpp-highlight-mode 做了我想要的,但需要用户操作。似乎绑定字体锁定功能是使行为自动化的正确选择。

我已经成功地按照 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),我搞砸了我的语法,或者我完全遵循了错误的方法。我在 OS X 10.6.5 上使用 Aquamacs 2.1(基于 GNU Emacs 23.2.50.1),如果这会有所不同的话。

任何帮助将不胜感激!

【问题讨论】:

【参考方案1】:

即使你让多行正则表达式工作,你仍然会遇到嵌套#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 的任意嵌套的 Niftier 代码

【讨论】:

我添加了“else”作为另一个关键字来停止评论。所以一行变成: (while (and (> depth 0) (re-search-forward "^\\s-*#\\s-*\(if\\|endif\\|else\\)" limit 'move)) 。此外,对于小文件( @pogopop77:处理#else 比你做的要复杂一些,所以我更新了代码。它不适用于#else 内的#if 0 #if 0,但我稍后可能会更新它。另外:200k是一个小文件吗? ;) 针对任意嵌套进行了更新。而且它更简单,这总是一个积极的信号:) 效果很好。我想我考虑过我正在处理的任何代码“小”:)。我使用 SQLite 合并的 C 文件作为我的折磨测试。 这太棒了!任何人都有 elisp 经验来处理 #if 1 #else #endif 代码?

以上是关于在 Emacs 的 C/C++ 模式下,将 #if 0...#endif 块中的代码面更改为注释面的主要内容,如果未能解决你的问题,请参考以下文章

bash,emacs绑定的数字前缀?

.emacs 编辑以始终在终端模式下启动 Emacs?

如何将 emacs 设置为在 verilog 模式下使用 3 个空格而不是制表符?

emacs终端连接下backspace无法工作

您可以在emacs的邪恶模式下使用vim脚本和插件吗?邪恶模式的局限性是什么?

Emacs文件命令