首页 > CEDET, IDE, 中级, 自动补全 > 用CEDET浏览和编辑C++代码

用CEDET浏览和编辑C++代码

1 前言

网上关于如何用emacs+cedet做C++ IDE的文章已经很多了,可是大都只列出了配置文件和效果,没有讲清楚具体的配置过程;一篇讲得比较具体的文章(http://alexott.net/en/writings/emacs-devenv/EmacsCedet.html ,这篇文章是被cedet官方推荐的)还是英文的。刚接触cedet的用户经常照抄了别人的配置却发现不能补全,而且配置文件是别人的想改还无法下手。本文中我尽量详细解释各个语句的作用,希望能给初次接触的人提供点帮助。

cedet于2010年2月26日发布了1.0pre7,强烈建议以前用过cedet觉得速度慢而放弃的同学试试1.0pre7,这个版本速度比以前有很大很大提高。

2 简介

cedet是一堆完全用elisp实现的emacs工具的集合,主要有:

2.1 EDE

用来管理项目,它可以把emacs模拟得像一个IDE那样,把一堆文件作为一个project来管理。

2.2 Semantic

Semantic应该是cedet里用得最多的组件了,代码间跳转和自动补全这两大功能都是通过semantic来实现的。

2.3 SRecode

SRecode是一个模板系统,通过一些预定义的模板,可以很快地插入一段代码。个人觉得这个功能跟msf-abbrev和yasnippet的功能有些类似。

2.4 Cogre

全称叫”Connected Graph Editor”,主要和图形相关,比如可以用它来为C++类生成UML图。

2.5 Speedbar

Speedbar可以单独创建一个frame,用于显示目录树,函数列表等等。这个组件已经包含在emacs官方发布包中。

2.6 EIEIO

EIEIO是一个底层库,它为elisp加入了OO支持。cedet的其它组件都依赖于EIEIO。

3 安装

安装就不多说了,这儿详细说明了如何下载安装。

要注意的是通过cvs下载必须要编译后才能用,而官方发布后的包可以直接解压不编译也是能用。

安装完后首先当然要load它(确保安装的路径已经在load-path中了):

(require 'cedet)

4 semantic配置

先介绍一下最常用的semantic。

4.1 功能介绍

一般装插件的思路,都是先load然后enable某个minor mode。cedet基本上也遵循这个规则,不过有点区别是semantic定义了很多个mode,要是挨个去enable,用户可能就要骂娘了,所以cedet的作者Eric定义了几个方便使用的函数,这些函数会自动帮你enable某些minor mode,大概有这么几个:

1
2
3
4
5
(semantic-load-enable-minimum-features)
(semantic-load-enable-code-helpers)
(semantic-load-enable-guady-code-helpers)
(semantic-load-enable-excessive-code-helpers)
(semantic-load-enable-semantic-debugging-helpers)

简单介绍一下各个函数的功能:

4.1.1 semantic-load-enable-minimum-features

这个函数开启了最基本的三个特性:

  • semantic-idle-scheduler-mode

    enable这个mode让cedet在emacs空闲的时候自动分析buffer内容,比如正在编辑的buffer内容改变后。这个mode一般应该是需要enable的,如果没有enable这个mode,那只有手工触发才会让cedet重新分析。

  • semanticdb-minor-mode

    semanticdb是semantic用来保存分析后的内容的,所以也是应该enable的。

  • semanticdb-load-ebrowse-caches

    这个feature我不是很确定,大概的意思好像是semantic可以利用ebrowse的结果。这个feature大概就是把ebrowse生成的文件load给semantic使用。(要是谁了解这个feature具体意义请告诉我下)

4.1.2 semantic-load-enable-code-helpers

这个函数除enable semantic-load-enable-minimum-features外,还包括:

  • imenu

    这个feature可以让imenu显示semantic分析出的类,函数等tags。如图:

    imenu显示semantic分析出的类

  • semantic-idle-summary-mode

    打开这个mode之后,光标停留在一个类/函数等tag上时,会在minibuffer显示出这个函数原型,如图:

    用ssemantic在minibuffer显示函数原型

  • senator-minor-mode

    senator开启之后,会在emacs上增加一个senator的菜单,可以通过菜单在当前文件的各个tag之间前后移动,跳转;还可以在里面方便地打开/关闭某个feature;还有另外一些实用的功能,看看菜单大概就能明白:

    senator菜单

  • semantic-mru-bookmark-mode

    cedet有tag跳转的功能,但是经常跳转完后还需要跳回刚才的位置,这时候就需要mru-bookmark-mode了。打开这个mode之后,每次跳转semantic都会把位置当作书签一样记录下来,以后可以通过M-x semantic-mrub-switch-tags(绑定到按键C-x B上)来选择跳回以前的任意一个位置。

4.1.3 semantic-load-enable-gaudy-code-helpers

这个函数除enable semantic-load-enable-code-helpers之外,还包括:

  • semantic-stickyfunc-mode

    这个mode会根据光标位置把当前函数名显示在buffer顶上,如图:

    在head-line上显示函数名

    这个mode我觉得用处不大,因为基本上可以用which-func-mode代替。而且我习惯打开tabbar-mode,这个mode会覆盖tabbar-mode,所以我是不打开它的。

  • semantic-decoration-mode

    打开这个mode后,semantic会在类/函数等tag上方加一条蓝色的线,源文件很大的时候用它可以提示出哪些是类和函数的头。如图:

    semantic标记函数头

  • semantic-idle-completions-mode

    这个mode打开后,光标在某处停留一段时间后,semantic会自动提示此处可以补全的内容。比如下面这段代码:

    semantic自动补全当前光标内容

    如果把光标停留在”this->”的后面,稍隔一会会提示:

    semantic自动补全当前光标内容

    如果提示的函数不是需要的,按TAB键可以在各个可能的函数之间循环,按回车就可以确定了。

4.1.4 semantic-load-enable-excessive-code-helpers

这个函数除enable semantic-load-enable-gaudy-code-helpers之外,还包括:

  • semantic-highlight-func-mode

    打开这个mode的话,semantic会用灰的底色把光标所在函数名高亮显示,如下图中,函数Delete被高亮了,而LexicalCast没被高亮:

    semantic高亮当前函数

  • semantic-idle-tag-highlight-mode

    用过XCode或eclipse的人应该会喜欢高亮光标处变量的功能:就是在函数内部,光标停留在一个变量上,整个函数内部用这个变量的地方都高亮了。在emacs里只要打开semantic-idle-tag-highlight-mode,光标在变量处停留一会,就会把相同的变量全都高亮,比如下图中的变量mAddr:

    semantic智能高亮当前符号

    semantic的这个tag-highlight虽然智能,可是我感觉它显示得太慢了,所以我是用另一个插件highlight-symbol来高亮的,这儿有它的介绍。

  • semantic-decoration-on-*-members

    把private和protected的函数用颜色标识出来,如图:

    semantic用颜色区分方法的访问权限

  • which-func-mode

    这个其实就是emacs自带的which-function-mode,把光标当前所在的函数名显示在mode-line上。

4.1.5 semantic-load-enable-semantic-debugging-helpers

这个函数会enable几个和调试semantic相关的特性:

  • semantic-highlight-edits-mode

    打开这个mode后,emacs会把最近修改过的内容高亮出来,如下图中begin就是刚输入的,所以用灰底色高亮了:

    semantic高亮最近修改

    隔一段时间后高亮会自动取消,不会一直高亮着让整个buffer看起来混乱。

    其实emacs自带也有高亮修改内容的mode:highlight-changes-mode,它会用红色的字体高亮所有修改的内容,但是不会自动取消,所以修改多了整个buffer就会乱七八糟糕。用semantic这个就好多了。

  • semantic-show-unmatched-syntax-mode

    这个mode会把semantic解析不了的内容用红色下划线标识出来,比如下面这个文件是从emacs源代码中来的:

    semantic用红色下划线标记不匹配的语法

  • semantic-show-parser-state-mode

    打开这个mode,semantic会在modeline上显示出当前解析状态,这是关闭mode的样子:

    这是打开mode的样子:

    能看出modeline上文件名前的横线多了一条,其实倒数第二条就是用来显示当前semantic解析状态的:未解析时显示为”!”,正在解析时显示”@”,解析完后显示”-”,如果buffer修改后未重新解析显示为”^”。

    semantic会在空闲时自动解析,另外可以打开senator-minor-mode,按[C-c , ,]或者在senator菜单中选[Force Tag Refresh]强制它马上解析。

4.2 基本配置

了解了上面这些feature,就可以根据需要配置了,为了使用semantic,至少需要开启semantic-load-enable-minimum-features定义的三个基础feature,其余的feature就可以根据自己的需要开启了。比如我的配置是:

1
2
3
4
5
;; (semantic-load-enable-minimum-features)
(semantic-load-enable-code-helpers)
;; (semantic-load-enable-guady-code-helpers)
;; (semantic-load-enable-excessive-code-helpers)
(semantic-load-enable-semantic-debugging-helpers)

因为imenu,idle-summary-mode,senator-mode,mru-bookmark-mode都是我需要的。特别是senator,有时候我会碰到semantic等很久也不自动解析文件的问题,这时候就需要在senator菜单里[Force Tag Refresh]一下了,并且senator还可以通过菜单方便地打开和关闭某些mode,用起来还是很方便的。

(semantic-load-enable-guady-code-helpers)和(semantic-load-enable-excessive-code-helpers)定义的那些feature,对我来说用处不大,而且我感觉打开的话还会让emacs反应变慢,所以我就不启用了。

(semantic-load-enable-semantic-debugging-helpers)的几个feature我都比较喜欢,所以我也启用了。

有了这些基本配置,在emacs打开C和C++文件的时候,semantic就会自动解析文件。不过有个问题,一个cpp文件中肯定会include很多头文件,要想解析这个cpp的内容,头文件的信息是必要的;但是头文件可能和cpp放在一起,也可能放在系统某个目录下,semantic怎么才能找到这个头文件一起解析呢?

semantic是这样处理的:1、如果当前目录中能找到,就直接在当前文件中读取头文件。2、如果当前目录下没有,就上系统INCLUDE目录中去找(在Linux下,我们一般使用gcc编译器,semantic会自动调用gcc,取得gcc的INCLUDE目录,比如/usr/include,/usr/local/include等,但是Windows下就不行了)。

BTW:很多文档中提到需要load semantic-gcc,不过我没有load它,在Linux下semantic仍然能自动把gcc的INCLUDE目录加进来。

semantic这种找法肯定会造成大量的头文件找不到的(找不到头文件还怎么解析啊),有两个问题需要解决:1、很多工程中都会把头文件和实现文件分开放置,比如头文件放在include(或者inc,public,common等)目录中,实现文件放在src目录中,这些目录semantic是不能自己找的;2、在Windows下怎么能让semantic去找编译器的INCLUDE目录。

既然semantic不能自动查找找,那就只能我们告诉semantic了,办法是调用semantic-add-system-include函数,这个函数会根据mode把路径加入到semantic-dependency-system-include-path里去。下面是我的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
;; (setq semanticdb-project-roots (list (expand-file-name "/")))
(defconst cedet-user-include-dirs
  (list ".." "../include" "../inc" "../common" "../public"
        "../.." "../../include" "../../inc" "../../common" "../../public"))
(defconst cedet-win32-include-dirs
  (list "C:/MinGW/include"
        "C:/MinGW/include/c++/3.4.5"
        "C:/MinGW/include/c++/3.4.5/mingw32"
        "C:/MinGW/include/c++/3.4.5/backward"
        "C:/MinGW/lib/gcc/mingw32/3.4.5/include"
        "C:/Program Files/Microsoft Visual Studio/VC98/MFC/Include"))
(require 'semantic-c nil 'noerror)
(let ((include-dirs cedet-user-include-dirs))
  (when (eq system-type 'windows-nt)
    (setq include-dirs (append include-dirs cedet-win32-include-dirs)))
  (mapc (lambda (dir)
          (semantic-add-system-include dir 'c++-mode)
          (semantic-add-system-include dir 'c-mode))
        include-dirs))

因为我在Windows下可能用MinGW和VC6,所以我把它们的include目录都加进来了,要是你用别的编译器,就改成自己的目录好了。

另外,我找了一些一般项目中经常用到的头文件目录名(include,inc,common,public),把它们也加进来了,这样对于一般的项目来说基本上都能解析正确(比如我们在项目中见到头文件放在include目录实现文件放在src目录的方式,对src目录下一个cpp文件,通过“../include”这个路径就能找到对应的头文件)。如果你的项目中还用了其它一些目录名,也可以配置在这儿。

上面配置中那一行(require ’semantic-c nil ‘noerror)是必须的,因为semantic的大部分功能是autoload的,如果不在这儿load semantic-c,那打开一个c文件时会自动load semantic-c,它会把semantic-dependency-system-include-path重设为/usr/include,结果就造成前面自定义的include路径丢失了。

顺便说一下semanticdb-project-roots的配置,很多地方都说要把它配置成”/”,但是我在Linux/Mac/Windows都试验过,不配这一行并没什么影响。

解析文件是semantic基本高级功能的基础,正确地解析了文件我们才能实现:代码跳转和代码补全。

4.3 代码跳转

有了前面的配置,semantic自动就解析c/c++文件,解析完后跳转就容易了:光标放在函数上,执行M-x semantic-ia-fast-jump,马上就跳转到函数的定义上了。如果跳不过去,那就检查一下前面配置的INCLUDE路径,是不是当前文件include的所有头文件都能在INCLUDE中找到。如果检查了很多遍都不好用,那就换个项目或者别的文件试试,确实存在semantic对某些文件支持不太好的情况,比如boost。

semantic-ia-fast-jump这个功能如此常用,我就把它绑定到f12上去了。

(global-set-key [f12] 'semantic-ia-fast-jump)

另外,前面我们说过跳转过去了我们还需要跳回来,在打开mru-bookmark-mode的情况下,按[C-x B],emacs会提示你跳回到哪个地方,一般默认的就是上一次semantic-ia-fast-jump的位置,所以回车就可以回去了。

不过看代码时候我经常需要跳转后马上就跳回来,要按[C-x B] [RET]这么多键实在有点麻烦,所以我写了个函数不提示直接就跳回上次的位置,并把它绑定到shift+f12上了:

1
2
3
4
5
6
7
8
9
10
11
12
(global-set-key [S-f12]
                (lambda ()
                  (interactive)
                  (if (ring-empty-p (oref semantic-mru-bookmark-ring ring))
                      (error "Semantic Bookmark ring is currently empty"))
                  (let* ((ring (oref semantic-mru-bookmark-ring ring))
                         (alist (semantic-mrub-ring-to-assoc-list ring))
                         (first (cdr (car alist))))
                    (if (semantic-equivalent-tag-p (oref first tag)
                                                   (semantic-current-tag))
                        (setq first (cdr (car (cdr alist)))))
                    (semantic-mrub-switch-tags first))))

除了semantic-ia-fast-jump可以跳转之外,其实semantic中还有两个函数也有类似的功能:

  • semantic-complete-jump-local
  • semantic-complete-jump

看名字很容易看出来,前一个只能在当前buffer内跳转,后一个可以跳转到其它文件。不过这两个命令都需要用户手工输入要跳转的Tag名,不能像semantic-ia-fast-jump那样自动识别当前光标处单词,所以浏览代码时还是semantic-ia-fast-jump舒服。

cedet还有个功能在函数和声明和实现间跳转,一般的,函数声明放在h文件中,函数的实现放在cpp文件中,光标在函数体的时候通过M-x semantic-analyze-proto-impl-toggle可以跳到函数声明去,在声明处再执行的话就会再跳回函数体,我把它绑定到M-S-F12上了:

(define-key c-mode-base-map [M-S-f12] 'semantic-analyze-proto-impl-toggle)

不是这个功能不是十分准确,一般在cpp中函数实现处想跳到函数声明处正常,但是从声明处跳到实现处的话cedet不一定能找到cpp文件的位置。

4.4 代码补全

semantic中有4个用来代码补全的命令:

  • senator-complete-symbol
  • senator-completion-menu-popup
  • semantic-ia-complete-symbol
  • semantic-ia-complete-symbol-menu

senator-complete-symbol和semantic-ia-complete-symbol这两个函数是新开一个buffer提示可能的补全内容;而senator-completion-menu-popup和semantic-ia-complete-symbol-menu会弹出一个补全菜单。

至于功能,以senator开头的两个函数是调用senator补全,另外两个是调用semantic-ia补全。至于senator和semantic-ia的区别,http://alexott.net/en/writings/emacs-devenv/EmacsCedet.html#sec9是这样解释的:

“semantic-ia调用semantic-analyze-possible-completions函数来取得可能的补全内容,它能为用户提供精确的补全列表;而senator用了一个更简单的的函数来获取补全内容,所以有可能会提供错误的结果。”

也就是说semantic-ia的补全更智能一些。

至于semantic-ia这两个补全选哪一样就看各人喜好了,我喜欢用semantic-ia-complete-symbol-menu,因为看起来更直观一些,像这样:

semantic的补全菜单

我喜欢把它绑定到[Alt+n]上:

(define-key c-mode-base-map (kbd "M-n") 'semantic-ia-complete-symbol-menu)

不过semantic-ia-complete-symbol-menu只能用于GUI下,要是在终端下,就只能用semantic-ia-complete-symbol了。(终端下想要semantic-ia-complete-symbol一样的结果可以用别的插件,比如auto-complete或者company-mode)

如果启用了semantic-idle-completions-mode,不用按键只需要光标在.或者->后面停一会semantic就会自动开始补全了。

如果你用cedet不能补全,检查一下semantic是不是已经启用了,我的emacs上经常出现第一次打开c++-mode时semantic没自动启用的情况。看semantic是否正常有个直观的方法就是senator,如果启用了senator-minor-mode,打开c++文件时emacs会出现Senator菜单,如果没有Senator菜单你可以关掉再重新打开试试,要是仍然不出现菜单那就得检查配置是不是有问题。

如果确认semantic启用了仍然不能补全,就需要检查INCLUDE路径的配置,通过C-h v semantic-dependency-system-include-path RET检查INCLUDE路径,确保当前cpp中直接或间接include的头文件都能在INCLUDE路径中找到。

5 EDE配置

EDE是用来管理project的工具,用下面的代码启用它:

(global-ede-mode t)

EDE会在emacs中加一个叫做“Project”的菜单:

EDE的Project菜单

通过菜单可以创建project,往project里添加/移除文件;还可以编译project,不过好像只能通过已有的Makefile编译。

另外EDE还可以通过Speedbar显示整个project的目录树(见右边的Speedbar):

用Speedbar显示project的目录树

EDE可以支持四种类型的project:

  • Automake
  • 手工写的Makefile
  • C++ Root project
  • Simple project

并且EDE能解析Autoconf/Automake,如果打开一个文件时在当前或者上级目录中能找到Makefile.am文件,EDE会自动解析文件(认为这是一个Automake的project),识别出Makefile.am中定义的target和编译需用到的文件;打开目录树的话EDE能由Makefile.am中涉及到的文件生成目录树(上图的目录树就是EDE通过Makefile.am自动生成的)。

为了让semantic找到C/C++的头文件,前面是通过调用semantic-add-system-include把系统中可能出现的INCLUDE目录都告诉semantic的来实现的。其实semantic还可以通过EDE识别project中特定的INCLUDE目录,方法是在.emacs文件中定义C++ Root project,比如:

1
2
3
4
5
6
7
8
9
10
11
(setq libutil-project
      (ede-cpp-root-project "libutil"
                            :file "~/projects/libutil/configure.in"
                            :system-include-path '("/home/meteor1113/projects/include"
                                                   "/home/meteor1113/projects/common"
                                                   "/home/meteor1113/projects/libutil/pub")))
(setq test-project
      (ede-cpp-root-project "test"
                            :file "~/test/Makefile"
                            :system-include-path '("/test/include"
                                                   "/usr/include/boost-1.42")))

上面定义了两个project,并且设定了各个project各自的INCLUDE目录。

不过这种方式有两个缺点:

  • 不能支持常见的Makefile/Makefile.am型project。
  • 我不愿意为每个project都定义这样一个project,对于每天都要自己写代码的项目生成个C++ Root project还可以接受,有时候只是临时阅读一下其它项目,要是还要为它写个EDE的project配置就太麻烦了。

所以这个功能我也一直没用过,有问题的请参考官方文档。我觉得把所有可能的目录都加进system-include里更方便。

6 其它

6.1 可视化书签

emacs有自带的书签功能(c-x r m, c-x r b, c-x r l),不过对于用了多年VC6的我来说还是更习惯让一个书签能高亮显示出来。cedet里就带了一个可视化的书签,通过下面的语句可以启用它:

(enable-visual-studio-bookmarks)

之后就可以通过下面几个按键操作书签了:

  • F2 在当前行设置或取消书签
  • C-F2 查找下一个书签
  • S-F2 查找上一个书签
  • C-S-F2 清空当前文件的所有书签

看这个效果:

cedet的可视化标签

有点遗憾的是这个书签功能只能在当前buffer的书签间跳转。

6.2 pluse

使用semantic-ia-fast-jump跳转时,cedet有个很酷的效果:在跳转到的行上实现一个淡入淡出的效果。具体的分析和使用看这儿

6.3 h/cpp切换

cedet的contrib目录下有一些实用的小功能,比如eassist.el就提供了一个在C++的头文件和实现文件间跳转的小功能。

要使用这个功能首先要load它:

(require 'eassist nil 'noerror)

之后就可以通过命令M-x eassist-switch-h-cpp来切换了,我喜欢把它绑定到M-F12上:

(define-key c-mode-base-map [M-f12] 'eassist-switch-h-cpp)

这个功能是依赖semantic的,也就是说通过cpp找头文件时它也会上配置好的INCLUDE路径中去查找,不过如果通过头文件找cpp文件,好像只能找和头文件所在的同一目录了。

eassist-switch-h-cpp有个BUG:它是通过文件扩展名来匹配的(通过eassist-header-switches可配置),默认它能识别h/hpp/cpp/c/C/H/cc这几个扩展名的文件;但是C++的扩展名还可能会有别的,比如c++,cxx等,对一个扩展名为cxx的文件调用eassist-switch-h-cpp的话,它会创建一个新buffer显示错误信息。所以我把eassist-header-switches配置为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(setq eassist-header-switches
      '(("h" . ("cpp" "cxx" "c++" "CC" "cc" "C" "c" "mm" "m"))
        ("hh" . ("cc" "CC" "cpp" "cxx" "c++" "C"))
        ("hpp" . ("cpp" "cxx" "c++" "cc" "CC" "C"))
        ("hxx" . ("cxx" "cpp" "c++" "cc" "CC" "C"))
        ("h++" . ("c++" "cpp" "cxx" "cc" "CC" "C"))
        ("H" . ("C" "CC" "cc" "cpp" "cxx" "c++" "mm" "m"))
        ("HH" . ("CC" "cc" "C" "cpp" "cxx" "c++"))
        ("cpp" . ("hpp" "hxx" "h++" "HH" "hh" "H" "h"))
        ("cxx" . ("hxx" "hpp" "h++" "HH" "hh" "H" "h"))
        ("c++" . ("h++" "hpp" "hxx" "HH" "hh" "H" "h"))
        ("CC" . ("HH" "hh" "hpp" "hxx" "h++" "H" "h"))
        ("cc" . ("hh" "HH" "hpp" "hxx" "h++" "H" "h"))
        ("C" . ("hpp" "hxx" "h++" "HH" "hh" "H" "h"))
        ("c" . ("h"))
        ("m" . ("h"))
        ("mm" . ("h"))))

基本上所有C/C++的扩展名都包含了,同时ObjectiveC也可以用了。

6.4 代码折叠

6.4.1 semantic-tag-folding

从我开始用emacs开始就听大虾们说hs-minor-mode可以实现代码折叠,所以我的.emacs里一直把hs-minor-mode打开的,可是用了5年之后我发现还是不习惯它的按键,另外也不是很喜欢它显示的样子,5年来Hide/Show这个菜单对我来说基本上是个摆设。

我期待像eclipse那样可以通过鼠标在直接点击就可以打开和折叠代码,这个功能在cedet也实现了(可惜这么长时间一直没发现它),就是semantic-tag-folding.el(也在cedet的contrib目录下)。

1
2
(require 'semantic-tag-folding nil 'noerror)
(global-semantic-tag-folding-mode 1)

看这个图:

cedet的代码折叠

只要用鼠标点击左侧的小三角图标就可以打开或折叠代码了。箭头向下的空心三角表示这段代码可以被折叠,箭头向右的实心三角表示这段代码被打折过了。

为了方便键盘操作,我把按键绑定到了[C-c , -]和[C-c , +]上(绑定这么复杂的
按键主要是为了和senator兼容,后面会讲到senator实现代码折叠):

1
2
(define-key semantic-tag-folding-mode-map (kbd "C-c , -") 'semantic-tag-folding-fold-block)
(define-key semantic-tag-folding-mode-map (kbd "C-c , +") 'semantic-tag-folding-show-block)

同时它还提供了两个函数可以同时打开和折叠整个buffer的所有代码,分别是
semantic-tag-folding-fold-all和semantic-tag-folding-show-all,我把它们
绑定到了[C-_]和[C-+]上:

1
2
(define-key semantic-tag-folding-mode-map (kbd "C-_") 'semantic-tag-folding-fold-all)
(define-key semantic-tag-folding-mode-map (kbd "C-+") 'semantic-tag-folding-show-all))

打开semantic-tag-folding-mode后,用gdb调试时不能点左侧的fringe切换断点了,所以我把C-?定义为semantic-tag-folding-mode的切换键,在gdb调试时临时把semantic-tag-folding关掉:

(global-set-key (kbd "C-?") 'global-semantic-tag-folding-mode)

不过,semantic-tag-folding在终端下会有一点点小问题:终端下semantic-tag-folding在函数前面加了个“+”或“-”号,看下面这个图:

终端下的semantic-tag-folding

虽然功能不受影响(除了不能用鼠标操作外,快捷键和GUI下是一样的),不过代码不能对齐了还是令我有些不爽,所以终端下我是禁用semantic-tag-folding的,最终我的配置如下:

1
2
3
4
5
6
7
(when (and window-system (require 'semantic-tag-folding nil 'noerror))
  (global-semantic-tag-folding-mode 1)
  (global-set-key (kbd "C-?") 'global-semantic-tag-folding-mode)
  (define-key semantic-tag-folding-mode-map (kbd "C-c , -") 'semantic-tag-folding-fold-block)
  (define-key semantic-tag-folding-mode-map (kbd "C-c , +") 'semantic-tag-folding-show-block)
  (define-key semantic-tag-folding-mode-map (kbd "C-_") 'semantic-tag-folding-fold-all)
  (define-key semantic-tag-folding-mode-map (kbd "C-+") 'semantic-tag-folding-show-all))

需要注意的是,semantic-tag-folding依赖于语法解析,也就是说必须等semantic解析完文件之后才能使用。如果找开文件在fringe处找不到空心三角,可以[Force Tag Refresh]下,或者检查下semantic是否配置正确。

6.4.2 senator-fold-tag

终端下不用semantic-tag-folding了,最好能有替代方案吧:首先可以用回hs-minor-mode,此外cedet的senator也提供了一种代码折叠方案。

只要启用了senator-minor-mode(emacs中会出现Senator菜单),就可以通过M-x senator-fold-tag和M-x senator-unfold-tag来折叠和打开代码了,GUI和终端下都可以使用。

默认地,senator-fold-tag绑定到[C-c , -],senator-unfold-tag绑定到[C-c , +]上(所以前面我把semantic折叠的快捷键也绑定到这两个键上,这样GUI和终端下快捷键就一致了)。不过senator里好像没有对应的fold-all和show-all方法。

7 Todo

以上只是cedet里我用到的一些功能,其实cedet还有很多优秀的功能,比如通过模板自动生成代码(SRecode);通过代码画UML图以及通过UML图生成代码(Cogre)等;另外semantic除了可以自己解析代码外还可以借助ctags,global,ebrowse来解析。更多的功能需要进一步发掘。

最后,欢迎参观我的cedet配置: http://github.com/meteor1113/dotemacs/blob/master/init-site.el

分享家:Addthis中国
GD Star Rating
loading...
用CEDET浏览和编辑C++代码, 9.7 out of 10 based on 40 ratings 标签:autoload, C/C++, calc, CEDET, cogre, company-mode, eclipse, ede, editor, eieio, Emacs, emacser, emacser.com, gdb, highlight, highlight-symbol, IDE, imenu, lambda, meteor, meteor1113, mode-line, org, pulse, screenshot, semantic, senator, snippet, speedbar, srecode, text, windows, yasnippet, 代码折叠, 代码补全, 光标, 插件, 淡入淡出, 补全, 配色, 颜色, 鼠标

相关日志

  1. 2010年12月10日13:19 | #1

    本人对eassist.el进行了强化,可以在当打开的所有文件或目录中找寻.h/.cpp,大家可以试用一下:
    http://blog.csdn.net/DelphiNew/archive/2010/12/10/6067494.aspx

    [回复]

  2. 雪狼湖
    2010年12月29日02:48 | #2

    cedet使用后的体会,这段日子一直使用ahei的配置,也把自己的代码的头文件路径都包含进去了。
    觉得其实我们要的功能很简单,如果有这个功能我宁愿把cedet给换掉了,我是作计算力学的,所以我的程序中引用进来的程序包很多,用cedet semantic每当要跳转代码的时候都要等好久,它在那里一直分析呀,分析头文件,minibuffer上一直进行分析,无奈只能取消。如果程序规模大一点,包含的头文件比较多时,特别不爽。并且主要是它不能分析源文件。
    我也曾经尝试过Qt-Creator,语义解析一点问题没有,代码跳转,以及输入.或者->后的代码补全也非常好用,主要问题是这个东西不能解析源文件,只能分析头文件,它内置了vim编辑器。是个很完整的开发环境非常不错。但是我还是喜欢ahei配置的emacs风格来写程序。我的编码量也大,主要是要看懂好多代码的实现,然后自己增加自己的功能.
    大学时代一直是用VC6+Visual Assitant X,这个是我迄今为止发现的最好用的代码助手,如果每个那个助手VC6太朴素了。其实这个助手大家都很熟悉了,其中几个功能是我们最需要的比如:
    1. 它通过预先指定头文件以及源文件的路径,来建议索引,这样不管多大项目都不存在效率问题。
    2. 代码提示,补全功能超级好用,提示和补全信息也是针对h和cpp文件的全部信息,这样就非常好用了。
    3. 能够对任何一个函数直接跳转到函数声明处,并且重载函数时也提供了选择来决定跳转哪一个。
    4.在h文件和cpp文件之间切换
    我已经好多年不用vc了,但是很怀念助手,确实作的比较优秀。我现在用的程序库在linux下兼容最好,我也不是很喜欢vc6的环境,所以还是喜欢emacs。
    cedet的问题我觉得就是大而全,想弄出来一个能解析所有语言的东西,就导致它的不好用,哈哈,别丢鸡蛋。确实不是很好用,或许我没用好。因为它貌似好多东西都能作,功能强大,说它不好用,它还能将就用,但是一用还不太爽,比如大家说有几个人真的用他的代码提示的功能,代码跳转还面临效率问题。
    我也曾经找了好多文章,发现真的用好cedet的人太少了。
    也看到有人向我这样呼唤Emacs版的Visual Assisant X。
    如果你有好的解决方案,喜耳恭听。

    [回复]

    雪狼湖 回复:

    @雪狼湖,
    自己回复一下吧,可以看看下面这个别人的帖子,我和他有相同的感受。但是我更加喜欢在emacs下工作。好多我的同学都不愿意切换到linux原因也是因为下面那个帖子。emacs虽然强大,但是在代码助手这个块,我觉得Visual Assitant X 比cedet和cscope更加好用稳定。

    http://doomkeeper.com/archives/30

    [回复]

    Meteor Liu 回复:

    @雪狼湖,
    1.不可否认Visual Assisant好用,不过有得必有失,你在linux下怎么用它?
    2.emacs os本质还是从editor起家的,它本身没有工程的概念,想指望它解析整个工程是不太现实的。
    3.我倒觉得cedet慢不是因为它想解析所有语言(它针对每个语言都相当于是一个插件而已,不会因为多加一个语言就更慢一些),而是因为它用lisp来实现。想想C++的编译速度,用C写的编译器编译C++已经那么慢了,何况lisp。所以我觉得要更好地补全和跳转,确实应该通过另外的方式,clang应该是一个值得期待的东东。
    4.承认cedet慢的前提下来考虑不停解析头文件的问题,即使对很大的工程,大多数情况下都是因为图省事把不该包含的头文件都包含进来造成的。当然你所说的包含了很多第三方包,那可能这些包对头文件包含也没怎么规划好,这种情况下确实没啥好办法了。
    5.其实我也只能无奈地承认,cedet确实问题多,实在是没有别的选择了才只能选择它。

    [回复]

    雪狼湖 回复:

    @Meteor Liu,
    多谢这么快回帖。

    关于3点,我的意思是cedet不仅仅针对C++,对于其他语言也要适用,
    所以感觉它照顾到更多语言特性,所以是很通用的解决方案,不是专门针对C++
    所以没有vc助手那么专注。

    我对lisp语言不懂,如果感觉实现一个东西的时候发现lisp太慢时
    能考虑用c或者c++写api给lisp吗。
    我也相信会有更多的优秀插件出现。

    多谢你的这些建议,有空我也看看clang。

    [回复]

    ahei 回复:

    @雪狼湖, 首先我没有用CEDET解析过很大的项目, 但是小项目解析都还挺快的, 跳转都挺不错,我也基本不用它的补全, 我用auto complete补全当前buffer单词我觉得基本够用了, 不知道你是不是就是因为项目太大cedet搞不定而导致的? 如果真这样的话, 去尝试一下clang吧, 我最近也在试用

    [回复]

    雪狼湖 回复:

    @ahei,
    项目也不是大,关键我要用比如superlu, petsc, taucs,lapack,blas这些软件包,
    加上一些图像处理分割itk等软件包,FEM有限元程序包,所以总是在那里不停的解析着,
    每当一条转就开始等待,所以最后还是无法忍受,只能ctrl+g,等了好久发现代码跳转也没
    问题,但是一些需要等.有个别文件估计包含文件比较少时跳转比较顺利,其它文件就
    傻眼了,他好多时候也是在解析boost,std,c++头文件。
    并且感觉一开启cedet我的浏览代码都慢了,感觉有点一卡一卡,去掉之后就超级清爽了。
    所以最后还是没有开启。或许clang更值得期待,还好emacs正如日中天的发展中,我很
    期待新的插件出来。

    [回复]

    Meteor Liu 回复:

    @雪狼湖,
    boost这样的东西估计cedet是吃不消

    [回复]

  3. guoxx_
    2011年2月21日22:50 | #3

    目前正在mac上使用的emacsforosx,安装了cedet1.1beta
    没有对自动补全做特别的配置,每次semantic-ia-complete-symbol-menu补全的菜单出现的位置都“不是在当前光标所在处”,而是在鼠标停留的地方。
    折腾了半天实在无解,经常逛这里,希望请教有什么方法解决吗?还是semantic的bug?

    [回复]

    Meteor Liu 回复:

    @guoxx_,
    换1.0试试呢。因为用于gui菜单,所以我的菜单也是出现在鼠标的地方。不过我的会自动移动鼠标到靠近光标的地方。
    不知道会不会是1.1beta哪个地方改出bug了。

    [回复]

    guoxx_ 回复:

    @Meteor Liu,
    换回1.0 stable还是一样
    senator-completion-menu-point-as-event打了日志发现补全的时候这个函数被调用了
    其中有一段set-mouse-position的函数 可能是这个的问题吧
    不知道和我下面这段配置是不是有关系
    (add-to-list ‘default-frame-alist ‘(height . 999))
    (add-to-list ‘default-frame-alist ‘(width . 999))
    暂时还不知道怎么调试lisp程序,先写在TODO里了。
    感谢Meteor Liu的帮助!

    还有一个疑问 emacs是基于lisp于c的,那可以用c做扩展吗?可能是由于先入为主的原因,还没习惯用lisp的思维方式来思考,所以想直接用c来修改些东西。

    [回复]

    Meteor Liu 回复:

    @guoxx_,
    我也不太清楚你这个具体是什么原因,lisp调试可以另找资料,不过像你这个怀疑某个地方出问题的话,那直接注释的方式调试就可以了吧。
    另外,我也只是用用emacs干活,对于emacs内部的机制也不是很了解。但是好像没有听说过emacs直接支持用c来扩展的。好在emacs开源,想用c的话可以自己修改啊。

    [回复]

  4. hj_18
    2011年4月21日18:10 | #4

    我发现在新的 cedet 中 semantic-load-enable-code-helpers 只是 semantic-load-enable-guady-code-helpers 的别名而已。

    [回复]

  5. werther
    2011年4月21日20:19 | #5

    Possible metatype recursion for
    请问,这个错误是怎么回事?
    如何解决?

    [回复]

  6. 大海无量
    2011年5月23日15:00 | #6

    能否介绍下, 从C++生成UML图的方法? :lol:

    [回复]

    Meteor Liu 回复:

    @大海无量,
    没怎么研究过。
    打开cpp文件后,你可以看看cedet的senator菜单下的UML子菜单

    [回复]

  7. 小冰
    2011年5月27日08:10 | #7

    请问,我用ede创建了一个“Make”的project,接下来创建的target有哪些类型?分别代表什么意思?我26个字母输了一遍,补全出来的好多。随便选了一个后,添加cpp文件,居然报 不支持的文件类型。还有 如果要自己写个target类型该怎么写?希望点击build后能自动创建qt的makefile文件。

    [回复]

  8. 会飞的鱼
    2011年8月3日17:25 | #8

    文章写得真好。

    [回复]

  9. 匿名
    2011年9月8日19:20 | #9

    为什么我在RHEL5下无法找到strcpy, memchr等等函数补全, string/vector等stl的组件…
    按理说只要require ’semantic-gcc再#include就行了, 照别人的配置都试过了, 用company也没用. 郁闷!

    [回复]

    Meteor Liu 回复:

    @,
    你不会是没装gcc和g++吧

    [回复]

    匿名 回复:

    @Meteor Liu,
    装了, 能用了, 谢谢!
    不过做cstring cstdlib这样的头文件, 是不是得不到原型, 只能得到函数名.
    如memset, 在minibuf只显示了cstdlib memset, 而不是cstdlib void memset(xxx*, xxx, xxx)

    [回复]

    匿名 回复:

    @, 我的意思是本来就装了, 不知怎么的就能用了

    [回复]

  10. 楼兰
    2011年10月1日16:39 | #10

    @Meteor Liu
    我按照你的这篇文章配置的cedet自动补全include目录,但是发现相对路径根本不行啊,还是找不到头文件的,要写绝对路径才可以。你配置的相对路径是怎么工作的?谢谢。

    [回复]

    Meteor Liu 回复:

    @楼兰,
    描述一下你的相对路径怎么配的吧,以及你要补全的文件路径在哪。
    需要注意的是相对路径是相对打开的c++文件的,而不是相对.emacs的路径。
    比如你的.emacs在~/.emacs,include在~/include,但是想要打开/aaa/bbb/ccc/ddd/eee.cpp文件来补全,肯定不能相对到../include这样的路径去

    [回复]

    楼兰 回复:

    @Meteor Liu,
    相关路径配置为:
    ;;; CEDET include path
    ;; (setq semanticdb-project-roots (list (expand-file-name “/”)))
    (defconst user-include-dirs
    (list “..”
    “./common” “../commom”
    “./gate” “../gate”
    “./chat” “../chat”))
    (defconst win32-include-dirs
    (list “C:/MinGW/include”
    “C:/MinGW/include/c++/3.4.5″
    “C:/MinGW/include/c++/3.4.5/mingw32″
    “C:/MinGW/include/c++/3.4.5/backward”
    “C:/MinGW/lib/gcc/mingw32/3.4.5/include”
    “C:/Program Files/Microsoft Visual Studio/VC98/MFC/Include”))
    (require ’semantic-c nil ‘noerror)

    (let ((include-dirs user-include-dirs))
    (when (eq system-type ‘windows-nt)
    (setq include-dirs (append include-dirs win32-include-dirs)))
    (mapc (lambda (dir)
    (semantic-add-system-include dir ‘c++-mode)
    (semantic-add-system-include dir ‘c-mode))
    include-dirs))

    假设项目结构为:
    |
    |–common
    | |–tools.h
    |–gate
    | |–gate_service.c
    |–main.c
    我的测试结果是 在main.c中是可以解析tools.h的,而在gate/gate_service.c中是无法解析tools.h的,也就是说这个
    相对路径只能向子目录,而不能向父目录。

    用ede-cpp-root倒是很方便,只是这个相对路径查找的行为很奇怪,故探讨之。

    btw:Qt的补全按照Alex Ott的配置
    (setq qt4-base-dir “/usr/lib64/qt/include”)
    (add-to-list ‘auto-mode-alist (cons qt4-base-dir ‘c++-mode))
    (semantic-add-system-include qt4-base-dir ‘c++-mode)
    (add-to-list ’semantic-lex-c-preprocessor-symbol-file (concat qt4-base-dir “/Qt/qconfig.h”))
    (add-to-list ’semantic-lex-c-preprocessor-symbol-file (concat qt4-base-dir “/Qt/qconfig-large.h”))
    (add-to-list ’semantic-lex-c-preprocessor-symbol-file (concat qt4-base-dir “/Qt/qglobal.h”))
    后头文件可以解析但是在.和>(定义为semantic-complete-self-insert)后提示:
    Possible metatype recursion for “QApplication”
    不知如何解决?
    环境slackware 13.37 x64, emacs 23.3, 单独下载的CEDET 1.0

    [回复]

    Meteor Liu 回复:

    @楼兰,
    我试了你这样的目录结构,用”../common”这样的相对路径是可以解析补全的。
    你打开gate/gate_service.c后查看一下semantic-dependency-system-include-path变量。
    另外,检查一下是不是和其它配置冲突了。

    qt我没用过,不清楚。

    [回复]

评论分页
1 ... 3 4 5 6 40990
  1. 2010年3月19日05:34 | #1
  2. 2010年9月19日22:26 | #2
  3. 2011年10月4日03:28 | #3
  4. 2011年12月9日12:05 | #4
  5. 2011年12月9日12:05 | #5
:wink: :-| :-x :twisted: :) 8-O :( :roll: :-P :oops: :-o :mrgreen: :lol: :idea: :-D :evil: :cry: 8) :arrow: :-? :?: :!: