openoffice使用手册:配置CEDET浏览和编辑C 代码 | Emacs中文网

来源:百度文库 编辑:中财网 时间:2024/05/06 00:05:47
用CEDET浏览和编辑C++代码
2010年3月18日meteor1113发表评论阅读评论
作者:Meteor Liu

1 前言
2 简介 2.1 EDE
2.2 Semantic
2.3 SRecode
2.4 Cogre
2.5 Speedbar
2.6 EIEIO
3 安装
4 semantic配置 4.1 功能介绍 4.1.1 semantic-load-enable-minimum-features
4.1.2 semantic-load-enable-code-helpers
4.1.3 semantic-load-enable-gaudy-code-helpers
4.1.4 semantic-load-enable-excessive-code-helpers
4.1.5 semantic-load-enable-semantic-debugging-helpers
4.2 基本配置
4.3 代码跳转
4.4 代码补全
5 EDE配置
6 其它 6.1 可视化书签
6.2 pluse
6.3 h/cpp切换
6.4 代码折叠 6.4.1 semantic-tag-folding
6.4.2 senator-fold-tag
7 Todo
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中了):
View Code LISP
(require 'cedet)
4 semantic配置
先介绍一下最常用的semantic。
4.1 功能介绍
一般装插件的思路,都是先load然后enable某个minormode。cedet基本上也遵循这个规则,不过有点区别是semantic定义了很多个mode,要是挨个去enable,用户可能就要骂娘了,所以cedet的作者Eric定义了几个方便使用的函数,这些函数会自动帮你enable某些minor mode,大概有这么几个:
View Code LISP
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的样子:
semantic在mode-line上显示解析状态
这是打开mode的样子:
semantic在mode-line上显示解析状态
能看出modeline上文件名前的横线多了一条,其实倒数第二条就是用来显示当前semantic解析状态的:未解析时显示为”!”,正在解析时显示”@”,解析完后显示”-”,如果buffer修改后未重新解析显示为”^”。
semantic会在空闲时自动解析,另外可以打开senator-minor-mode,按[C-c , ,]或者在senator菜单中选[Force Tag Refresh]强制它马上解析。
4.2 基本配置
了解了上面这些feature,就可以根据需要配置了,为了使用semantic,至少需要开启semantic-load-enable-minimum-features定义的三个基础feature,其余的feature就可以根据自己的需要开启了。比如我的配置是:
View Code LISP
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 TagRefresh]一下了,并且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里去。下面是我的配置:
View Code LISP
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的,如果不在这儿loadsemantic-c,那打开一个c文件时会自动loadsemantic-c,它会把semantic-dependency-system-include-path重设为/usr/include,结果就造成前面自定义的include路径丢失了。
顺便说一下semanticdb-project-roots的配置,很多地方都说要把它配置成”/”,但是我在Linux/Mac/Windows都试验过,不配这一行并没什么影响。
解析文件是semantic基本高级功能的基础,正确地解析了文件我们才能实现:代码跳转和代码补全。
4.3 代码跳转
有了前面的配置,semantic自动就解析c/c++文件,解析完后跳转就容易了:光标放在函数上,执行M-xsemantic-ia-fast-jump,马上就跳转到函数的定义上了。如果跳不过去,那就检查一下前面配置的INCLUDE路径,是不是当前文件include的所有头文件都能在INCLUDE中找到。如果检查了很多遍都不好用,那就换个项目或者别的文件试试,确实存在semantic对某些文件支持不太好的情况,比如boost。
semantic-ia-fast-jump这个功能如此常用,我就把它绑定到f12上去了。
View Code LISP
(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上了:
View Code LISP
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-xsemantic-analyze-proto-impl-toggle可以跳到函数声明去,在声明处再执行的话就会再跳回函数体,我把它绑定到M-S-F12上了:
View Code LISP
(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]上:
View Code LISP
(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 vsemantic-dependency-system-include-pathRET检查INCLUDE路径,确保当前cpp中直接或间接include的头文件都能在INCLUDE路径中找到。
5 EDE配置
EDE是用来管理project的工具,用下面的代码启用它:
View Code LISP
(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,比如:
View Code LISP
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里就带了一个可视化的书签,通过下面的语句可以启用它:
View Code LISP
(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它:
View Code LISP
(require 'eassist nil 'noerror)
之后就可以通过命令M-x eassist-switch-h-cpp来切换了,我喜欢把它绑定到M-F12上:
View Code LISP
(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配置为:
View Code LISP
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目录下)。
View Code LISP
1 2 (require 'semantic-tag-folding nil 'noerror) (global-semantic-tag-folding-mode 1)
看这个图:
cedet的代码折叠
只要用鼠标点击左侧的小三角图标就可以打开或折叠代码了。箭头向下的空心三角表示这段代码可以被折叠,箭头向右的实心三角表示这段代码被打折过了。
为了方便键盘操作,我把按键绑定到了[C-c , -]和[C-c , +]上(绑定这么复杂的
按键主要是为了和senator兼容,后面会讲到senator实现代码折叠):
View Code LISP
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-+]上:
View Code LISP
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关掉:
View Code LISP
(global-set-key (kbd "C-?") 'global-semantic-tag-folding-mode)
不过,semantic-tag-folding在终端下会有一点点小问题:终端下semantic-tag-folding在函数前面加了个“+”或“-”号,看下面这个图:
终端下的semantic-tag-folding
虽然功能不受影响(除了不能用鼠标操作外,快捷键和GUI下是一样的),不过代码不能对齐了还是令我有些不爽,所以终端下我是禁用semantic-tag-folding的,最终我的配置如下:
View Code LISP
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
VN:F [1.9.0_1079]
评分: 9.8 (24次投票)
用CEDET浏览和编辑C++代码, 9.8 out of 10 based on 24 ratings 标签:autoload,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,代码折叠,代码补全,光标,插件,淡入淡出,补全,配色,配色,颜色,鼠标
相关日志
Emacs长啥样 (3)
用CEDET浏览和编辑C++代码(续) – 使用Emacs 23.2内置的CEDET (69)
我的Emacs配置文件 - DEA (181)
在Emacs下用C/C++编程 (26)
Emacs才是世界上最强的IDE - 高亮光标处单词 (19)
分类:CEDET,IDE,中级,自动补全
评论 (37)Trackbacks (2)发表评论Trackback

cs007
2010年8月26日18:21 |#1
回复 |引用
你好,
我cedet 老是如下错误是你知道是为什么么?
Idle Service Error semantic-idle-summary-idle-function: “# – Wrong type argument: stringp, nil”
Idle Work Including Error: “# – Wrong type argument: stringp, nil”
多谢来
[回复]

Meteor Liu 回复:
八月 27th, 2010 at 9:57 上午
@cs007,
好像见过,但当时没深究,也没仔细去查哪种情况下会出。
你不会用的emacs-21吧。
[回复]

cs007 回复:
八月 27th, 2010 at 11:30 上午
@Meteor Liu,
我用的是GNU Emacs 23.1.1 好纠结
[回复]

LieMi
2010年9月5日22:06 |#2
回复 |引用
你好,在你的网站上学到不少东西,谢谢你。我有一个问题:我的是emacs 23.1+cedet-1.0pre7, 在配置cedet的时候没有选择(semantic-load-enable-guady-code-helpers),但是semantic- stickyfunc-mode自动启动了,即使在.emacs中设置了(setq semantic-stickyfunc-mode nil)与(setq global-semantic-stickyfunc-mode nil)还是启动了,覆盖了tabbar, 手动取消后,在新的buffer中还是一样,也就是说每个新buffer都要手动取消,希望在配置文件中一次性搞定,能象emacs 23.2自带的cedet那样灵活的选择要启动的mode更好
另外,shell-mode下tabbar的操作完全失效,可以设置semantic不解析shell-mode的buffer吗?
[回复]

Meteor Liu 回复:
九月 6th, 2010 at 2:42 下午
@LieMi,
文中我对每个features分别启动了哪些minor-mode都写了,比如(semantic-load-enable-guady-code- helpers)你可以打开它源码看看他里面都做了什么,再检查一下什么原因导致stickyfunc-mode被打开的吧,我没有碰到过无故启动的情 况。ahei曾经写过个二分法调试的方法,你可以试试,我觉得启动了它没有道理没配置自动就启动了。
[回复]

LieMi 回复:
九月 6th, 2010 at 5:35 下午
@Meteor Liu, 这个问题解决了原来是(semantic-load-enable-excessive-code-helpers) 包含了(semantic-load-enable-gaudy-code-helpers),于是我改为这样启动相应的mode:
(semantic-decoration-mode)
(semantic-idle-completions-mode)
启动时报错:
Debugger entered–Lisp error: (error “Buffer *scratch* was not set up for parsing”)
signal(error (“Buffer *scratch* was not set up for parsing”))
error(“Buffer %s was not set up for parsing” “*scratch*”)
semantic-decoration-mode-setup()
semantic-decoration-mode()
eval-buffer(# nil “/home/sword/emacs/esword/init-site.el” nil t) ; Reading at buffer position 478
load-with-code-conversion(“/home/sword/emacs/esword/init-site.el” “/home/sword/emacs/esword/init-site.el” t nil)
load(“init-site” noerror)
[回复]

LieMi 回复:
九月 6th, 2010 at 7:51 下午
这个问题也解决了,按照你的配置,再加入(global-semantic-stickyfunc-mode nil) 就好了
但是在编辑shell-script-mode的文件”xxx”时就有问题,情况如下:
1.tabbar的tab被覆盖,
2.查看semantic-stickyfunc-mode 的值是nil,
3.执行semantic-stickyfunc-mode 返回了 “Buffer xxx was not set up for parsing,”
4.执行tabbar-backward和 tabbar-forward 无效,且返回“Args out of range: “xxx” 0,5”
打开shell-scripte-mode的文件之后再打开其它.cpp 文件的情况:
1.它的tab也被覆盖了
2.查看该buffer的semantic-stickyfunc-mode的值也是nil
3.修改了值为1后还是不能显示tab
4.执行tabbar-backward和 tabbar-forward 无效,且返回“Args out of range: “xxx” 0,5”
请教这是什么原因? 怎么解决? 谢谢
[回复]

Meteor Liu 回复:
九月 7th, 2010 at 10:05 上午
@LieMi,
1.默认cedet是不会解檄shell的,只有在(semantic-load-enable-primary-exuberent-ctags-support)并且还安装了exuberent ctags时,才会调用ctags.exe来解析shell。
2.但你的semantic-stickyfunc-mode 的值是nil,并且执行semantic-stickyfunc-mode 返回了 “Buffer xxx was not set up for parsing,”,说明cedet应该没有解析shell。
3.所以你能不能确认sh-mode下tabbar被覆盖是被semantic-stickyfunc-mode覆盖的吗?会不会是别的插件造成的?
4.检查一下你的sh-mode有什么hook设置吗?打开sh-mode再打开cpp出现tab被覆盖会不会是因为打开sh-mode时把某个插件启用了?
5.tabbar-backward的问题,可以toggle-debug-on-error看看栈,找找是哪出的错。
[回复]

LieMi 回复:
九月 7th, 2010 at 10:15 下午
@Meteor Liu,
非常感谢你的回复,用了你介绍的二分法调试,终于找到问题的原因了,是tabbar的设置问题,我的tabbar分组条件中,有一个条件是根据 buffer的名称来分组的,即如果buffer的名称的(0,5)位是“*term” 则分到“Multi-Term”组,有的buffer名称的长度小于5个字符,超出了边界,导致了这个错误, 我把它改为如果(0,2)是“*t”,则分到”Multi-Term“组,这样就可以了,代码如下:
((string-equal “*t” (substring (buffer-name) 0 2))
‘(“Multi-Term”)
)
但这样依然有问题,如果buffer名称只有一个字符,就会出现同样的错误,改进如下:
((and (> (length (buffer-name)) 10)
(string-equal “*terminal<" (substring (buffer-name) 0 10)))
'("Multi-Term")
)
运行正常,第一次摸索elisp,谢谢你
[回复]

ahei 回复:
九月 8th, 2010 at 9:19 上午
@LieMi, 哈哈,我的二分调试法不错吧
[回复]

LieMi 回复:
九月 9th, 2010 at 12:46 上午
@ahei, very good!~~
[回复]

ahei 回复:
九月 8th, 2010 at 9:19 上午
@Meteor Liu, 赞回复精神
[回复]

LieMi
2010年9月9日09:47 |#3
回复 |引用
高手,我又来了,以下是我的测试程序,启用了debugging-helpers , 用emacs打开总个class都是红的下划线,搞不明白…..,请高手帮忙
#ifndef TEST_H
#define TEST_H
class Test
{
public:
Test();
String xxx;
String yyy;
int aaa;
int bbb;
int ccc;
}
#endif
[回复]

LieMi 回复:
九月 9th, 2010 at 9:49 上午
@LieMi, “}”后加上了”;” ,还是一样,重启emacs还是一样
[回复]

LieMi 回复:
九月 9th, 2010 at 10:15 上午
@LieMi,
test.h:
class Test
{
public:
Test();
int aaa;
int bbb;
int ccc;
};
还是一样
[回复]

Meteor Liu 回复:
九月 9th, 2010 at 10:35 上午
@LieMi,
不至于吧,语法正确的啊,我拿你这段也没出红线。
在senator菜单中选[Force Tag Refresh]试试呢。看看左下角指示parse状态的!还在吗?
另外,找个编译器编译下你的代码试试,会不会中间有全角字符之类。
[回复]

LieMi 回复:
九月 9th, 2010 at 6:07 下午
@Meteor Liu,
哈哈,搞好了,是因为没有设定include_dirs的原因,
把要用到的系统头文件目录加进配置文件就OK了
关键是C++的头文件路径,C语言不会出红线
[回复]

shangwen
2010年9月22日20:58 |#4
回复 |引用
为什么在我这里下列几个快捷键会报错,emacs23.2版本
(define-key c-mode-base-map [M-S-f12] ’semantic-analyze-proto-impl-toggle)
(define-key c-mode-base-map (kbd “M-n”) ’semantic-ia-complete-symbol-menu)
(define-key c-mode-base-map [M-f12] ‘eassist-switch-h-cpp)
谢谢
[回复]

ahei 回复:
九月 24th, 2010 at 3:02 下午
@shangwen, (require ‘cc-mode)
[回复]

shangwen
2010年9月22日21:00 |#5
回复 |引用
忘了补充,cedet是官网下载的1.0 不带pre
[回复]

zhugewei
2010年9月25日01:51 |#6
回复 |引用
在设置快捷键的时候,出现这个问题
Symbol’s value as variable is void: c-mode-base-map
请问这个问题怎么解决
[回复]

ahei 回复:
九月 25th, 2010 at 9:27 上午
@zhugewei, (require ‘cc-mode)
[回复]

shangwen
2010年9月25日13:28 |#7
回复 |引用
shangwen :
为什么在我这里下列几个快捷键会报错,emacs23.2版本
(define-key c-mode-base-map [M-S-f12] ’semantic-analyze-proto-impl-toggle)
(define-key c-mode-base-map (kbd “M-n”) ’semantic-ia-complete-symbol-menu)
(define-key c-mode-base-map [M-f12] ‘eassist-switch-h-cpp)
谢谢
[回复]ahei 回复:九月 24th, 2010 at 3:02 下午@shangwen, (require ‘cc-mode)
[回复]
我本来注释掉了这几个快捷键
(require ‘cc-mode)后,会报这行错误
[回复]

ahei 回复:
九月 25th, 2010 at 1:35 下午
报哪行错误?
[回复]

shangwen 回复:
九月 25th, 2010 at 1:52 下午
@shangwen,
(add-to-list ‘load-path “~/.emacs.d/cc-mode-5.31.3″)
(require ‘cc-mode)
以上
[回复]

ahei 回复:
九月 25th, 2010 at 1:57 下午
@shangwen, cc-mode是emacs内置的,(require ‘cc-mode)怎么会报错
[回复]

shangwen 回复:
九月 25th, 2010 at 1:59 下午
@shangwen,
疯了。。。
单引号是全角字符!!!报错了!!!
眼睛不好呵~~~~
[回复]