首页 > C/C++, Elisp, 自动补全, 配置, 高级 > Emacs 自动填充头文件

Emacs 自动填充头文件

作者: 杨英超

目标

  1. 写 C/C++ 代码时候,可以根据自动补全头文件。注意,是补全,也就是说至少我们需要输入几个字符让它帮忙补全。
  2. 如果头文件存放在搜索路径的子目录中,可以自动列出子目录中的文件,并将这些文件添加到用于补齐的候选名单中。
  3. 补齐完成之后,可以自动判断到底应该使用 #include 还是 #include “FILE” 。

方案

emacser.org 上有一篇文章,其中提到了这个解决方法,主要是利用了 abbrev-mode 和 skeleton-mode 来实现,通过这个方法,我们输入 inc, 然后按空格, 会提示输入文件名称。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
;; 安装 abbrev
(mapc
 (lambda (mode)
   (define-abbrev-table mode '(
                               ("inc" "" skeleton-include 1)
                               )))
 '(c-mode-abbrev-table c++-mode-abbrev-table))
 
;; 输入 inc , 可以自动提示输入文件名称,可以自动补全.
(define-skeleton skeleton-include
  "generate include<>" ""
  > "#include <"
  (completing-read "Include File:"
                   (mapcar #'(lambda (f) (list f ))
                           (apply 'append
                                  (mapcar
                                   #'(lambda (dir)
                                       (directory-files dir))
                                   (list "/usr/include"
                                         "/usr/local/include"
                                         "/usr/include/g++-3")))))
  ">")

该方法有若干局限性:

  • 头文件的搜索路径是写死的,如果某个目录不存在,上面的代码会报错,不能补全。
  • 无法补全搜索路径的子目录下的文件 ( 即前面的 AIM 2)。
  • 没有判断在 #include 一个文件的时候,是应该使用符号 <> 还是符号 “” (即前面的 AIM 3)

解决方法并不复杂,对应如下:

  • 通过某种方法来从系统中自动获取 include 的搜索路径。比如 CEDET 提供的: semantic-gcc-get-include-paths 函数。
  • 重定义 minibuffer-mode 下的按键 “/”。将其绑定到一个用于搜索和展开某个目录,并更新 minibuffer-completion-table 的函数(minibuffer-completion-table 是 minibuffer-mode 中补全的候选 list)。
  • 在 skeleton-include 中不使用 <> 或者 “。我们可以使用一个特殊的标记,然后在 skeleton-include 的结尾,根据头文件的路径判断到底应该使用什么符号。

整理以后的代码实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
;; 输入 inc , 可以自动提示输入文件名称,可以自动补全.
;; Provided by yangyingchao@gmail.com
(mapc
 (lambda (mode)
   (define-abbrev-table mode '(
                               ("inc" "" skeleton-include 1)
                               )))
 '(c-mode-abbrev-table c++-mode-abbrev-table))
 
(defconst yc/inc-dir-list
  (append (semantic-gcc-get-include-paths "c++") '("./")) "nil")
(defvar inc-minibuffer-compl-list nil "nil")
 
(defun yc/update-minibuffer-complete-table ( )
  "Complete minibuffer"
  (interactive)
  (let ((prompt (minibuffer-prompt))
        (comp-part (minibuffer-contents-no-properties)))
    (when (and (string= "Include File:" prompt)
               (> (length comp-part) 0))
      (setq minibuffer-completion-table
            (append minibuffer-completion-table
                    (let ((inc-files nil)
                          (dirname nil)
                          (tmp-name nil))
                      (mapc
                       (lambda (d)
                         (setq dirname (format "%s/%s" d comp-part))
                         (when (file-exists-p dirname)
                           (mapc
                            (lambda (x)
                              (when (not (or (string= "." x)
                                             (string= ".." x)))
                                (setq tmp-name (format "%s/%s" comp-part x))
                                (add-to-list 'inc-files tmp-name)))
                            (directory-files dirname))))
                       yc/inc-dir-list)
                      inc-files)))))
  (insert "/"))
 
(let ((map minibuffer-local-completion-map))
  (define-key map "/" 'yc/update-minibuffer-complete-table))
 
(defun yc/update-inc-marks ( )
  "description"
    (let ((statement (buffer-substring-no-properties
                      (point-at-bol) (point-at-eol)))
          (inc-file nil)
          (to-begin nil)
          (to-end nil)
          (yc/re-include
           (rx "#include" (+ ascii) "|XXX|" (group (+ ascii)) "|XXX|")))
      (when (string-match yc/re-include statement)
        (setq inc-file (match-string 1 statement))
        (if (file-exists-p (format "./%s" inc-file))
            (setq to-begin "\"" to-end "\"")
          (setq to-begin "<" to-end ">")
          )
        (move-beginning-of-line 1)
        (kill-line)
        (insert (format "#include %s%s%s" to-begin inc-file to-end))
        (move-end-of-line 1))))
 
(define-skeleton skeleton-include
  "generate include<>" ""
  > "#include |XXX|"
  (completing-read
   "Include File:"
   (mapcar
    (lambda (f) (list f ))
    (apply
     'append
     (mapcar
      (lambda (dir)
        (directory-files
         dir nil
         "\\(\\.h\\)?"))
      yc/inc-dir-list))))
  "|XXX|"
  (yc/update-inc-marks))

使用和效果

使用方法很简单:

  1. 将上述的代码添加到 Emacs 的配置文件后,打开一个 C/C++ 程序,
  2. 输入 inc 然后按下空格,然后在 minibuffer 中输入部分头文件的名字,并通过 TAB 来补全。
  3. 如果头文件位于子目录中,则输入目录名后输入 “/” 。这样子目录中的内容也会添加到补齐的候选名单中,然后就又可以继续他过 TAB 补全了。
  4. 确认 minibuffer 中填写的内容无误后,回车, skeleton-include 将自动更新标记符号。

截图:

下面是几张截图:

Include 系统文件

Include 自定义文件

全部完成后截图


分享家:Addthis中国
GD Star Rating
loading...
Emacs 自动填充头文件, 7.3 out of 10 based on 30 ratings 标签:C/C++, Elisp, Emacs, 配置, 高级

相关日志

  1. 2012年4月3日23:51 | #1

    先顶,明天看

    [回复]

  2. Meteor Liu
    2012年4月4日15:24 | #2

    顶。
    不过依赖于gcc,可以考虑把yc/inc-dir-list换成(mode-local-value ‘c++-mode ’semantic-dependency-system-include-path)。
    还有个bug,如果提示输入”Include File:”时,如果啥也不输入直接回车,变成了“#include |XXX||XXX|”。

    [回复]

    leonard 回复:

    @Meteor Liu,
    支持楼上 支持楼主0.0

    [回复]

  3.  tubo 
    2012年4月5日07:38 | #3

    @Meteor Liu
    确实依赖gcc,因为我平时用的就是 gcc,所以没考虑别的,然后又觉得可能会对别人有些帮助,就发过来了,没考虑太多别的,呵呵。

    [回复]

  4. seagle0128
    2012年4月11日16:58 | #4

    这个就不如用yasnippet,或者autoheader、template之类的了

    [回复]

     tubo  回复:

    @seagle0128, 这几个插件我也在用,但这几个插件能自动补齐头文件么?要是能的话麻烦分享下怎么设置吧? :wink:

    [回复]

  5. water
    2012年4月21日23:28 | #5

    请问 modeline 左边的小绿点 是怎么来的 :lol:

    [回复]

     tubo  回复:

    @water, subversion 的状态,可以参考一下 psvn ,本站也有相关介绍:http://emacser.com/svn-status-mode-line.htm

    [回复]

     tubo  回复:

    @ tubo ,
    “psvn用一个带有颜色的圆表示文件的状态,绿色的圆表示未修改过,淡红色的圆表示已经修改过。”

    [回复]

  6. xxo|oxx
    2012年4月21日23:57 | #6

    “…”和不错, 但对于多目录文件,makefile works better !

    [回复]

  7. 2012年4月22日10:38 | #7

    写的很好,我收藏了。

    [回复]

  8. 我自横刀对天笑
    2012年5月17日06:17 | #8

    怎么说呢 作者做事情缺乏严格的标准
    第一使用环境 例如操作系统
    第二使用软件以及插件代码什么都没写清楚
    第三目的你介绍的目的
    都没在首位写出来 感觉是一种什么呢 不好说了

    [回复]

  9. tubo
    2012年5月18日19:40 | #9

    1. any os installed emacs.
    2. None.
    3. refer to the first few lines of this article.

    [回复]

  10. fenghelong
    2012年5月31日21:26 | #10

    怎么报错了呢,要事先装什么东西么?

    [回复]

    tubo 回复:

    @fenghelong, 啥错?

    [回复]

评论分页
1 2 41916
  1. 本文目前尚无任何 trackbacks 和 pingbacks.
:wink: :-| :-x :twisted: :) 8-O :( :roll: :-P :oops: :-o :mrgreen: :lol: :idea: :-D :evil: :cry: 8) :arrow: :-? :?: :!: