admin 管理员组

文章数量: 887007

在Emacs中使用gdb调试程序

http://emacser/emacs-gdb.htm

 

  • 1 引言
  • 2 准备工作:将调试信息编译在程序中
  • 3 开始:开启Emacs的调试模式GUD
    • 3.1 运行gdb
    • 3.2 gdb界面
    • 3.3 小结
  • 4 调试:设置断点,控制程序流程
    • 4.1 设置、删除断点
    • 4.2 运行程序
    • 4.3 单步执行、运行到光标处
    • 4.4 继续运行程序
    • 4.5 小结
  • 5 察看变量的值
    • 5.1 本地变量buffer
    • 5.2 察看变量值
    • 5.3 用工具提示显示变量值
    • 5.4 小结
  • 6 输入输出
    • 6.1 单独的IO buffer
    • 6.2 输入数据
    • 6.3 重定向到文件
  • 7 按键绑定
  • 8 结尾

1 引言

Emacs除了具有强大的编辑功能,还可以作为调试工具gdb的前端,对程序进行调试。使用Emacs进行调试,可以将程序的编写与调试统一到Emacs中,并利用Emacs强大的功能辅助调试,是将Emacs作为IDE使用的一项必备功能。

本文假定读者具有基本的程序调试知识,希望知道在Emacs下进行基本调试的对应操作。水平有限,欢迎拍砖。

2 准备工作:将调试信息编译在程序中

要使程序能被gdb调试,需要在编译时加入调试所需的信息。如果使用gcc/g++进行编译,需要使用参数-g,如:

? View Code BASH
gcc prog.c -g -o prog

如果使用 ./confiugre && make 的编译流程,可以将使用如下方式引入-g参数:

? View Code BASH
1
2
CFLAGS="-g" ./configuremake

注意:不要加入任何优化参数(例如-O、-O2),不然调试时会有很灵异的现象发生……

3 开始:开启Emacs的调试模式GUD

3.1 运行gdb

在编译好程序后,就可以开始调试了。直接运行gdb命令M-x gdb RET 在minibuffer中会出现需要执行的gdb命令。例如:gdb –annotate=3 prog 如果当前目录下有可执行文件(通常恰好是需要调试的文件),gdb会在其后自动补上可执行文件,否则需要在minibuffer中补上要调试的程序文件名。

继续回车,Emacs的GUD(Grand Unified Debugger)就会关联到gdb并加载要调试的程序了。

3.2 gdb界面

启动gdb后,Emacs的界面会变成下面两种之一:

GDB单窗格模式

GDB多窗格模式

可以通过gdb-many-windows来切换这两种界面布局。

如果界面被打乱了(例如,在minibuffer中使用补全,查看帮助,重新编译程序),可以使用gdb-restore-windows来恢复界面布局。

3.3 小结
命令功能
gdb启动gdb进行调试
gdb-many-windows切换单窗格/多窗格模式
gdb-restore-windows恢复窗格布局

接下来就要开始调试程序了。

4 调试:设置断点,控制程序流程

4.1 设置、删除断点

首先将断点设置在要调试的地方。有两种方法:

第一种,在要设置断点的行左边的fringe上单击一下(就是文本左边与滚动条之间空出的那一块)。隐藏了fringe的朋友可以M-x fringe-mode显示它。

第二种,使用默认快捷键C-x C-a C-b, 或者 C-x <SPC>。它们都关联到命令gud-break。

无论使用哪种方法,fringe上都会在设置了断点的行上显示一个红点,表示这行设了断点:

同时,在断点buffer中也会显示已有的断点信息:

断点buffer

要删除断点,同样有两种对应的方法:在fringe的断点上单击一下,或者使用快捷键C-x C-a C-d(对应命令gud-remove)。

可以在断点buffer上单击某个断点切换到断点所在位置。将光标移动到断点处回车也有同样的效果。

在断点buffer上按空格键可以切换断点的激活和禁用状态。

4.2 运行程序

设置好断点后就可以运行程序了。单击工具栏上的就开始运行了。也可以使用gud-go命令来运行。奇怪的是没有任何默认快捷键绑定。

当程序运行到断点时,程序会在断点处停下来,并自动打开停下的语句所在的代码文件。同时在fringe上在停下的语句处有三角形的指示器。

现在,我们来一步步运行程序。

4.3 单步执行、运行到光标处

在调试中最常用的功能就是单步执行了。单步执行有两种:将函数调用作为一条语句执行(Next)和遇到函数时进入函数中进行调试(Step)。

要使用第一种方式,默认快捷键是C-x C-a C-n,对应命令为gud-next。也可以单击工具栏上的 。

第二种方式的默认快捷键是C-x C-a C-s,对应命令为gud-step。也可以单击工具栏上的 。

如果想跳出当前函数,可以使用命令gud-finish,默认快捷键为C-x C-a C-f,工具栏上有 可用。

在Emacs中还可以运行到光标所在的行。使用命令gud-until即可,默认快捷键为C-x C-a C-u。1

也可以直接把当前语句指示器拖到任意一行,程序会运行到那一行再停下来。

4.4 继续运行程序

在程序中断后要继续运行程序,依然是使用gud-go命令或 ,也可以使用命令gud-cont,对应快捷键为C-x C-a C-r。

4.5 小结
功能命令默认快捷键
添加断点gud-breakC-x C-a C-b 或 C-x <SPC>
删除断点gud-removeC-x C-a C-d
运行/继续程序gud-go
单步执行,无视函数gud-nextC-x C-a C-n
单步执行,进入函数gud-stepC-x C-a C-s
跳出当前函数gud-finishC-x C-a C-f
运行到光标所在语句gud-untilC-x C-a C-u
继续运行程序gud-contC-x C-a C-r

5 察看变量的值

调试的过程中免不了要查看变更的值。Emacs提供了方便地功能让我们查看变量的值。

5.1 本地变量buffer

如果打开了gdb-many-windows,在右上角会显示Locals buffer,其中显示了当前局部变量的值。如果显示的是寄存器(Register)buffer,单击左边的Locals就可以切换到Locals buffer了。在其中可以方便地观察到各变量的值。

如果没有打开gdb-many-windows,也可以使用gdb-display-locals-buffer来显示该buffer。

5.2 察看变量值

遇到一些Locals里没有显示的变量,或者比较复杂的结构,就需要用到观察变量的功能了。

将光标停留在要观察的变量上,执行命令gud-watch,可以将变量加入观察列表中。默认的快捷键是C-x C-a C-w。也可以使用工具栏上的 。

被观察的变量将在Speedbar中显示。对于复杂结构,可以单击Speedbar上的+号将其展开或收缩。在+号上按空格键也有相同的效果。2

有时候Emacs观察的变量不是你所想要的,一般是a->b这类的情况。这时可以将要观察的部分选中,再使用上述方法即可。

5.3 用工具提示显示变量值

可以用gud-tooltip-mode开启或关闭工具提示。开启后将鼠标指针停留在变量名上时将在工具提示中显示变量的值。

5.4 小结
功能命令默认快捷键
观察变量gud-watchC-x C-a C-w
展开/收缩变量<SPC>
开启/关闭工具提示gud-tooltip-mode

6 输入输出

如果程序需要与标准输入/输出交互,那么你很可能需要用到下面要介绍的功能。

6.1 单独的IO buffer

默认来说,程序的输入输出是在gdb buffer里显示的。这样输出信息和gdb信息混合在一起,阅读起来非常不便。这时候,你需要把输入输出单独显示在一个buffer里,方便查看。

使用gdb-use-separate-io-buffer,可以在程序代码buffer右侧新建一个IO buffer,程序对标准输入输出的操作都会重定向到这里。再执行一次该命令则会隐藏。

6.2 输入数据

需要输入数据的时候,只需要在IO buffer中输入数据回车即可。已经输入的数据会被加粗,以和输出信息区分开来。

6.3 重定向到文件

有时候我们已经准备好了用于输入的数据在文件中,以避免调试时烦琐的输入。这时候就需要在调试时进行输入输出重定向了。

要进行重定向,只能使用gdb自带的功能。在gdb buffer中输入 run < data.in > data.out 就可以将标准输入重定向到data.in,将标准输出重定向到data.out了。

7 按键绑定

说实话,gud自带的按键绑定实在是麻烦,使用一个功能要三次组合键才行,小姆指按Ctrl都按酸了。所以一般将常用的按键绑定在方便的位置,这样才能有和另的IDE一样的快感。

以下是将F5、F7、F8分别绑定到gud-go、gud-step和gud-next的代码:

? View Code LISP
1
2
3
4
(add-hook 'gdb-mode-hook '(lambda ()                             (define-key c-mode-base-map [(f5)] 'gud-go)                             (define-key c-mode-base-map [(f7)] 'gud-step)                             (define-key c-mode-base-map [(f8)] 'gud-next)))

之所以绑定到c-mode-base-map上,是因为我基本上在代码buffer中调试。如果要在gdb-buffer中使用的话,需要使用gud-mode-map。如果想在所有buffer上使用的话,可以绑定到全局按键中:

? View Code LISP
(global-set-key [(f5)] 'gud-go)

8 结尾

有了调试功能,Emacs作为一个IDE才算是完整了。本文介绍了在Emacs下使用gdb调试的基本方法,Emacs的调试功能还远不止这些,进一步学习可参阅 Emacs Manual 的 Debuggers 一节。

由于我也是边学边写,一定有许多不足或者错误,还请各位多多指教。

Footnotes:

1 注:我在使用时只有光标所在的行在当前行之后并且位于同一函数内才行,否则会跳到很奇怪的地方,还请高手指教。

2 我在使用过程中经常出现展开没反应,或者加入新元素后才展开,运行几步才展开的情况,求高人讲解。

 

 

 

对初学者学习vim的建议

网上有不少人一方面对Vim很是好奇,另一方面又对Vim表示出不屑一顾的鄙夷,更有甚者认为使用Vim的人只是装逼

我不知道这种意识怎么出现的,只觉得这种人很傻很天真,我也不期望这种人学会Vim,假使学会,那他们可就真是用来“装逼”的了 初学者只需谨记以下几点建议:

  1. 刚开始不要纠结于配置,网上找个配置好的版本先用上再说
  2. 初学不要看Vim枯燥的文档,找几篇清晰的入门教程学会基本的几个操作就够用了
  3. 或许你会觉得Vim某些地方用着不爽,那你可以试着找找改变的方法,尝试修改自己的配置文件
  4. Vim不是用来学的,学只是一个基础,最终还是得用,最好是天天都用
  5. 不要奢望有哪个编辑器能比Vim更强大(Emacs例外),目前来说是不可能的,用好你的Vim就是
  6. 不要以为用Vim就很厉害,不要将Vim用作充门面的工具,要知道,这是一种很白痴的行为
  7. 适合的就是最好的,如果你觉得现有的编辑器或IDE用起来很舒服,那你没必要非要来学什么Vim,感觉不够舒服了再来学吧
  8. 尽量不要转入编辑器的宗教之争,这没有任何意义,有时间还不如写点使用心得,造福后来者
  9. 自己有什么新的发现和感悟、自己的配置文件有什么高明之处,分享出来吧,Vim社区历来有崇尚分享的传统

Vim的学习曲线还是比较陡峭的,一般来说,一周小成一月入门半年熟练一年掌握,至少也要用上近一个月才能体会到Vim的妙处。慢慢来,无需着急,随着熟练度的越来越高,使用起来也会越来越顺手,你会发现,自己无形中已经爱上了Vim,一旦你发现自己没事干时老喜欢按jk这两个键,那么恭喜你,你已经入门

 

 

有趣或者有用的网友回复:

 

普通人的编辑利器Vim

  •  

1.免费

用户再也不用去网上辛苦的下载D版了!

2.提高输入速度

这个相信也没有什么好争论的,纯键盘操作的速度是鼠标无法比拟的,虽然会带来一定的学习成本,但是相信还是值得的。

3.完美支持中文,并支持多种文件编码

很多编辑器会有处理多字节编码的问题,而vim完美解决了这一点。(如果你的vim没解决,请参考linux下vim的编译以及终端乱码的最终解决方案)

4.文本笔记管理

这要归功于vim的一款插件:voom,详情可以参考善用佳软的这篇博文--VOoM(原VOOF):vim实现带折叠双栏树状文本管理

  • 如果你是一名程序员,那就千万不要错过vim,它真的为程序员提供了太多贴心的功能。

1.支持几乎所有语言类型

不夸张的说,vim真的是将这一点做到了极致,只要是大家知道的语言,大到c++,python,小到bash,sql,vim全部都默认支持了这些语言的语法高亮,自动缩进,等等。一个编辑器搞定所有源码编写,不用为每门语言学习他的编辑器,降低学习成本,何乐而不为?

2.支持各种语言的代码自动补全和智能提示

详见:vim所支持的自动完成详解,把VIM打造成一个真正的IDE(3)

3.快速查找函数定义功能及显示函数列表功能

vim+tags+taglist即可轻松实现上面的功能,功能上完全可以替代si或者vs.详见:把VIM打造成一个真正的IDE(2),在Vim中使用ctags

4.支持类似textmate的代码片段功能

我目前使用的是snipMate,当然提供这种功能的插件可不只这一个,你可以有更多选择~

5.集成编译调试功能,快速定位编译错误

vim提供了quickfix的功能,可以集成gcc,g++,java,c#,python,php等各种语言的错误定位功能,极大提高了代码编译调试时的错误定位。参见:VIM-一键编译单个源文件

6.开源

如果发现vim有bug,那么大可以自己去研究代码修复~

7.跨平台

在linux,windows,mac等多平台都有相应的版本,不用担心跨平台开发的问题!

8.支持插件,无限扩展自己想要的功能

vim本身有自己的脚本语言,如果你真的不想再多学一门语言,那也不是什么大事,gvim7.3已经默认编译支持了python,ruby,lua,perl等等脚本语言,用你喜欢的脚本语言开发去吧!(我爱python!),与emacs的lisp相比,vim自身的脚本语言确实有所欠缺,但是python较之lisp也算是旗鼓相当了

  • vim更像是一个超级终端,vim已经支持用python,lua,perl,ruby等多种脚本语言进行脚本编写,只要你愿意,你可以用脚本语言实现任何你想要的功能,然后通过vim展示出来。
    简单来说,即
vim替你实现了一个通用的界面,而你只需要编写你的逻辑代码,而展现到屏幕这一层,就调用vim的接口,由vim来完成就可以了~
这就是我所说的超级终端的定义,如果还是不能理解,那我们就用实例来说明吧!


 

 

编辑上的功能基本都一样,你觉得vim或者emacs没有那一样功能只是你不知道,实际上是内置或者可通过某个插件来完成,我觉得不同就在于vim注重的是编辑,编辑文件时按键方面比emacs方便,而emacs注重的是功能,各种功能集成在一起,管理项目这些比vim方便

有一个著名的把用户当作傻瓜的软件,总是把句子的行首单词Capitalize,而且自动把他认为错了的单词改过来,每次我都要费尽心机想办法,怎么把这个功能关掉。这个软件应该是office,我也常常为这个功能头痛

 

本质上来说,vim的哲学是浏览器+编辑器,所以浏览功能应该比emacs好些, 剩下的编辑功能,emacs靠lisp,vim靠python,都能实现那些你能想到的功能。
user experience来说, 插件管理,窗口管理, 其实两者都有待加强。

 

我用EMACS而不用VIM因为我不喜欢ESC,不喜欢模式切换。
但我会基本的VIM,我觉得VIM绝对不比EMACS差。
我觉得通过组合键,不停的编辑与移动,非快非快…… 而VIM要切来切去,切来切去…… 疯

你不喜欢切换模式?不会是你的键盘Esc键没有和占用着好地盘的无用按键Capslock交换吧?改过键后,左手小指Esc切换模式使用vim,要多爽有多爽,爽到难以用语言来形容。而且不只vim中爽,其他应用程序中,使用Esc换掉CapsLock也很爽。

 

 

@,
其实说到底还是看个人习惯,如果习惯轻量级,专业,和击键轻松,vim比较好;
如果喜欢all in one,按组合键,emacs好。

而且emacs还可以实现vim模式,最新的叫evil,名字还很有意思,
evil邪恶的,同时也是emas vim layer

 

 

刚刚从vim转过来,vim的输入效率确实很高,发现emacs的快捷键太复杂太麻烦,启动也要比vim慢的多。
但是vim有几个缺点:
1、插件比较挫,本身用的插件语言的表达能力就不强,有点不伦不类,感觉执行效率也不高,功能也比较弱,应该换成一个更通用的脚本语言,这样持续性才强。断断续续用了一年,我还是没有找到一个能正确格式化html文件的插件。我写代码的种类比较多,vim不能满足。
2、非常陡峭的学习曲线加上与众不同的模式转换机制,使得很多ide都不能很好的和vim integrate。eclipse和intellij都带有emacs的快捷键集,但是没有vim的(虽然有插件,但是都不太好用),因为操作方式差得太远。
vim本身比较适合快速的输入,但扩展性上确实有待提高。

 

在嵌入式平台下, 有时候需要ssh进去配置. 编辑文件的只能是vim.
所以有时候在vim编辑的时候, 不知不觉会按到crtl键

 

source in sight 的自动搜索一个函数的 caller 这个功能我认为 cscope的c-x s s, 是可以实现的。我一直这么用。

或者我还有个很笨的办法,用doxyen生成程序的文档,然后在文档内可以清晰的看到一个函数calling了哪些函数,有哪些caller,类图,集成图,协作图,还有…很多。

令:emacs里如果使用doxymacs,那么可以非常方便的生成doxyen格式的注。

cscope的确是使用tag做基础的,而且符号位置和行号相关;大量修改之后的确需要重新更新cscope.files和cscope.out。但是,如果只是微量修改,xcscope.el可以检查cscope数据库的更新状态,需要更新时会自动再次索引部分源文件,然后查找,结果还是相当准确的。

 对,效果很不错,我就是用的xcscope.el,我写了一个大量修改后更新cscope的脚本,找的结果却是很是精准。
ctags也很好用。

 

@negatlov, 哈,我也有这个原因,不过我主要还是刚用emacs就觉得好用,但是就是一直用不惯vi,而且还曾经专门花时间学vi,还是用不惯

[回复]

negatlov 回复:
四月 27th, 2010 at 1:22 下午

@ahei,

我比较讨厌VI中按ESC键。这个设计让我感觉不太爽

[回复]

ahei 回复:
四月 27th, 2010 at 2:09 下午

呵呵,我也是,那么远的键,不过还好,C-c基本上99%的情况下能代替ESC,我碰到的只有C-v I的情况下不能代替

[回复]

chapter7 回复:
五月 1st, 2010 at 10:18 上午

@ahei,
VIM中 C-[ 是跟ESC等价的

[回复]

ahei 回复:
五月 1st, 2010 at 12:30 下午

呵呵,没有C-c好按

 

 

 

曾经vim对emacs最大的优势就是长期使用emacs会导致左手小拇指健康状况恶化,甚至肌肉萎缩(众所周知vim是esc到死,而emacs是ctrl到死),以至于许多用emacs的大牛现在只能单手coding。

如果按作者的方法把Esc改为c+]的话,只怕vimer也会在不久的将来面临严重的亚健康问题。

目前相对合理的做法还是交换caps和esc的键值(for vimer)或者交换caps和ctrl的键值(for emacser),具体的交换方法网上有很多,搜索就是。

另外一个折中的办法对苹果用户比较适用,设置苹果的command键为ctrl,这样的话,小拇指的压力就转移到肌肉发达的大拇指上了。如此无疑会好上许多。

我已经告别 ESC 了.现在已经习惯了ctrl+[
(其实也就适应了不到一周)
我还看到了朋友说 将 capslock 与 esc 互换.这样做也很好.
但是…
如果你在本机习惯了 capslock 与 esc 互换.换了其它机器 … 就不方便了 .

 

把emacs的ctrl映射到windows键盘的windows键就可以用大拇指按ctrl了。本来emacs设计的时候,ctrl键就是用大拇指按的。用默认ctrl位置,用手掌压,而不要用小拇指按也是省力和一种保护

有个高人专门写文章研究了ctrl的问题,考证出 emacs发明时代的键盘布局和现代键盘不同,当时的键盘ctrl键在大拇指可及的位置。最后作者建议,ctrl键正确的使用方法是使用手掌去按

 

 

事实上 Vim 在 Insert 模式下也支持 Emacs 的很多按键,如果你只是想要一个编辑器的话,Emacs 反而很罗嗦。当然如果你需要学 Lisp 编程或者研究理想化的操作系统,Emacs 才是有必要用的。

 

vim中 ctrl+[ 可以代替esc... 我也习惯用ctrl+[

lz错了,emacs不需要用小指。改下键盘vim和emacs都可以非常舒服,并且可以同时使用。我就是这么干的,有时用vim,有时用emacs。
说下我的做法:
1.esc和capslock兑换
2.左边的control和alt兑换。
因为原来的emacs发明者用的键盘就类似这样的。
至于兑换的方法,网上有很多,可以搜下。
按键不是emacs和vim的根本区别

 

vim ESC Ctrl-[  替换为CapsLock的方法

在 ~/.Xmodmap 中加入以下几行,没有这个文件的话就创建一个:
remove Lock = Caps_Lock
keysym Escape = Caps_Lock
keysym Caps_Lock = Escape
add Lock = Caps_Lock

仅适用于 Unix.
注:如果想要 CapsLock 的功能,除了按 Escape 外,还可以先全部小写,然后 gU

图形界面下,还可以

  1. System -> Preferences -> Keyboard
  2. Select Layouts tab, then Layout Options
  3. Click on 'CapsLock key behavior'
  4. Click on 'Swap ESC and CapsLock'

http://wwwblogs/sunza/archive/2011/08/20/2146935.html

 

 

 

#############################

我是如何从vim转向Emacs的

http://emacser/from-vi-to-emacs.htm

以前,我多次试图从 vim 转变到 emacs 都失败了。原因很多,主要原因是 vim 的确一个很强大的 editor 不愧为 emacs 的主要竞争对手,vim 很多强大的功能,很难在 emacs 中找到相应的功能,那个时候还没有水木Emacs版,如果有的话,情况会好一些, 还有一个原因就是我周围几乎没有人用 emacs ,现在也是。

emacs 的入门比较 vim 要困难很多,我指的入门不是指简单的使用,而是高效的使用。vim 的很多操作几乎不需要配置就可以完成了,但是如果离开我那些复杂的 .emacs 文件,我认为 emacs 并不比 vim好。有了我的 .emacs , emacs 绝对是最好的 platform (not only an editor) ,因为他是我的软件,一些我常用的功能,都是按照我的方式工作的,而且我可以随时实现我喜欢的功能,也许别人不喜欢这样的操作,但是我喜欢,我可以让 emacs 让我喜欢的方式操作。几乎没有其它软件可以做到这一点。

我转变到 emacs 是一个痛苦和偶然的过程。我曾经煞有介事的“学习” emacs , 写了很多笔记,现在看来,很傻,emacs 不是学出来的,是用出来的,很多功能不是用脑子记住的,而是用手记住的,我们不得不承认,有的时候肌肉的记忆能力,要比脑子的记忆能力强,而且快速,实用。很多体育运动员不就是用大量的训练提高肌肉对动作的记忆吗?

一次偶然的机会我发现了几个 vim 无法代替的几个功能,于是我开始真正喜欢 emacs 了。(注:笔者做此文时,vim版本为6.2)

首先是编写 TeX 文件的时候,很多数学符号可以快速的输入,可以方便的生成 dvi 文件,可以快速的输入 TeX 特有的特殊符号。

然后是 Python mode ,他是非常好的 Python 的 IDE ,可以很容易的编写 Python 程序,尤其是 Python 程序中用缩进来表示语法结构,在Emacs中很容易处理缩进。很容易把一段代码放入到Python 的解释器中执行,然后在解释器中交互的测试程序。参见 我的Python 学习笔记

一个编辑 C or C++ 的时候的一些功能,例如 auto insert 功能,hungry delete, M-SPC(M-x just-on-space) indent 功能 (TAB) ,在 emacs 中写出来的程序,是最漂亮的格式。最重要的是还可以选择自己喜欢的 c style 。 当然 vim 中也可以,但是默认的是用 tab 键格式化 ,这样的缺点就是不能保证程序在所有的编辑器中都是一样的效果,但是我以前也用 TAB 来 indent ,原因是我不愿意输入很多 space 。而且 vim 对重新 indent 一段代码的功能也不是很好,尽管 g= 等等键也可以工作,但是不如 emacs 中的好。

vim 中的很多编辑命令要比 emacs 快的多,只要输入很少的键。

但是, 我认为中 emacs 的理念是,有很多工作,做好不要让用户记住那些快键,按照用户的习惯,猜想用户(主人)的意思,默默的做好(讨好用户)就行了。

例如,在 C 中,我们习惯用 tab 键来 indent 程序。那么就用 tab 键来 完成 M-x indent-line 的功能。我们经常在输入分号之后, 输入一个回车,那好,emacs 就根据上下文猜测应该回车的地方,自动回车。你想删除很多空格,那好,emacs 就会根据上下文,只留下一个空格(M-x just-on-line) 或者空行(M-x delete-blank-line) ,如果你在只有一个空行下还要 M-x delete-blank-line ,那么就把single blank line 也删掉。

还有的功能也很好用。

? View Code LISP
1
2
C-c C-c (M-x comment-region) C-u C-c C-c uncomment-region

还有自动补齐右边括号,补齐引号的功能,还有补齐大花括号的功能。 参见Emacs 中自动添加有半边括号的功能和写C程序,输入左大花扩号自动补齐很多东西

还有 abbrivate 扩展的功能。

还有 fly spell 的功能。 emacs 知道主人是个粗心的人自动提示主人错了,但是 emacs 从来不把主人当傻瓜,不会自作聪明的改动,只有主人真的承认说,我错了 (按M-$)(注:我记不住具体的M-x 命令了,可以用C-h k 查一个键的bind 的函数,记做 help key bind , 也可以用 C-h w 记做 help where, 查找一个函数的快键是什么 ) ,emacs 就提示很多可选的单词,供主人选择。 我记得有一个著名的把用户当作傻瓜的软件,总是把句子的行首单词Capitalize,而且自动把他认为错了的单词改过来,每次我都要费尽心机想办法,怎么把这个功能关掉。

还有 auto fill 的功能, M-q 也总能干正确的事情。

还有 version control 的功能,emacs 几乎不用主人亲自备份文件了,他知道应该在合适的时候,备份文件。

还有 auto insert 的功能,他会自动根据环境,把当前的文件增加可执行权限,参见在保存文件的时候,会自动给脚本增加可执行权限

还可以自动增加

#!/bin/bash or #!/bin/perl #!/bin/python

参见Emacs 在创建文件的时候,自动添加 template 的内容

header.el的扩展还可以自动插入和更新 C 文件中的开头的一些信息,包括文件名称,创建日期,改动次数,纪录谁在什么时候为什么做了修改。还有公司信息,作者信息,版权信息等等。参见轻轻松松为源 程序增加文件头信息

还有 emacs 有很多程序无缝结合 ,如 python, perl,octave(matlab), gdb, pydb 等等,emacs 是最好的 IDE 了。

总之,驯化了的 emacs 是你的忠实的奴仆,他会按照主人的方式工作,猜想主人的意图。

然而,有的时候 emacs 是奔放的野马,总是难以驾驭,要想emacs按照主人的意志工作,那么主人就有义务详细的告诉他该如何工作。

有两种办法,一种是向其他主人学习,请教,按照他的办法驯化emacs 。

另一种办法是掌握 emacs 的高级用法,只有了解他的语言,Elisp (Emacs Lisp) ,明白了 emacs 的语言,才能够和 emacs 很好的交流,沟通, emacs 是一个很听话的孩子。

如果要融入 emacs 的文化,或者叫理念,甚至叫宗教, 那么就要 Hack 他的程序,了解他的五脏六腑,做一个 hacker。(注: hacker is NOT cracker ) 除了安装emacs-x-xx.rpm 还要安装 emacs-el.x-xx.rpm , 然后用 M-x find-function 看任何一个函数是如何实现的

浏览了一会儿,你会感到什么是自由软件,什么是真正的自由。只要你愿意,你可以探索任何一个功能是怎么实现的。

使用其他的软件的时候,我总是在想办法发现软件提供给我什么功能,我怎样按照软件定义好的方式工作,我怎么去适应软件。

如果运气不好,碰到一个自以为是的软件,我会觉得被软件的作者愚弄。

如果运气再差一点,我会觉得被软件的作者侮辱,因为他把我当成傻瓜。

当然,也有运气好的时候,例如 vim,他的编辑方式是最快的方式。

但是 emacs 不同,使用 emacs 的时候,我会想我喜欢用什么样的方式完成某个功能,大多数的情况,都可以简单的用global-set-key, or local-set-key 解决掉。如果功能很复杂,就查找一下是不是有人实现过了, emacs 的社区中有很多好心人, www.emacswiki 是一个好地方。如果没有人实现,那么就自己写一个,然后贴到网上去。

开放,自由,这就是 emacs 。

emacs 总是想办法如何适用用户的方式,当然,缺点就是,初看上去 , emacs 不是很友好, 用户需要学习如何配置emacs 。

现在我很喜欢摆弄 emacs , 我知道,只要我想得到,emacs 一般是可以做得到, 当然我想的要合理,我想买彩票中500万,emacs 肯定做不到 ,而且还不能和其他你需要的功能冲突。

有一项功能我一直没有想办法办到,就是类似source in sight 的自动搜索一个函数的 caller ,自动成员变量补全,class browser 等等。这个功能很难,因为他要包含语言的语法语义分析。C++ 中的 template , typedef , #define #if , inline function, inner class (or structure, enum) , name space等等的语言特性,增加了其难度

尽管 semantic 号称可以,但是和 source in sight 还有差距。我用了几次,不好用, ECB 也是不伦不类。

我认为 semantic 的目标太大了,他要容纳所有语言的语法语义模糊分析的功能,这个很难,尽管他的作者是大名鼎鼎的 eric 。

我还是很喜欢 semantic ,因为我相信他行,于是我试图hack semantic ,实现那些功能,尽管自动补全成员变量的功能,勉强可以了,但是其他复杂的功能,还是很难实现。semantic 太复杂了。

但是一有时间,我还是喜欢看上 semantic 几眼。

我这篇文章是用 notepad 编辑的,于是顺便列举一些 emacs 和 notepad 类似的功能,参见类似Notepad 基本操作快速入门

 

 

用CEDET浏览和编辑C++代码(续) – 使用Emacs 23.2内置的CEDET

http://emacser/built-in-cedet.htm

 

  • 1 前言
  • 2 semantic配置
    • 2.1 基本配置
    • 2.2 代码跳转
    • 2.3 代码补全
  • 3 EDE配置
  • 4 其它
    • 4.1 可视化书签
    • 4.2 pulse
    • 4.3 h/cpp切换
    • 4.4 代码折叠

1 前言

今天,emacs-23.2发布了,最大的改变就是集成进了CEDET,所以有了这个续, 介绍下build in CEDET和offical CEDET的区别,以及内置CEDET缺少某些功能的替代方案。

PS1:虽然现在官方release版本是1.0pre7,内置的CEDET用cedet-version命令看输入也是1.0pre7,可我总感觉内置的CEDET用起来比官方版本慢很多,我猜想内置的CEDET可能没升级到1.0pre7的release。

PS2:内置CEDET不支持emacs-lisp语言了,没想明白是为什么。

2 semantic配置

2.1 基本配置

官方的CEDET通过semantic-load-enable-minimum-features等几个函数来启动,而内置的CEDET增加了一个单独的minor mode,即semantic-mode,可以通过(semantic-mode)命令来Enable或Disable。

(semantic-mode)是通过semantic-default-submodes这个变量来决定启用哪些minor mode,默认的semantic-default-submodes包含了下面两个minor mode:

  • global-semantic-idle-scheduler-mode
  • global-semanticdb-minor-mode

根据(semantic-mode)的文档,semantic-default-submodes里可以设置下面这些minor mode:

  • global-semanticdb-minor-mode
  • global-semantic-idle-scheduler-mode
  • global-semantic-idle-summary-mode
  • global-semantic-idle-completions-mode
  • global-semantic-decoration-mode
  • global-semantic-highlight-func-mode
  • global-semantic-stickyfunc-mode
  • global-semantic-mru-bookmark-mode

可以根据自己的需要设置,比如我开启了下面4个minor mode:

? View Code LISP
1
2
3
4
5
(setq semantic-default-submodes '(global-semantic-idle-scheduler-mode
                                  global-semanticdb-minor-mode
                                  global-semantic-idle-summary-mode
                                  global-semantic-mru-bookmark-mode)) (semantic-mode 1)

另外,emacs-23.2的Tools菜单下下新增了”Source Code Parsers (Semantic)”菜单项,可以通过这个菜单项来Enable和Disable semantic-mode,和命令(semantic-mode)的功能是一样的。

此外,官方CEDET里还有其它一些minor mode,现在基本上都还可以用,比如我还打开了下面几个:

? View Code LISP
1
2
3
(global-semantic-highlight-edits-mode (if window-system 1 -1)) (global-semantic-show-unmatched-syntax-mode 1) (global-semantic-show-parser-state-mode 1)

关于system-include-dir的设置,还和以前一样:

? View Code LISP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
(defconst user-include-dirs
  (list ".." "../include" "../inc" "../common" "../public"         "../.." "../../include" "../../inc" "../../common" "../../public")) (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")) (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))
2.2 代码跳转

代码跳转和官方版本一样还是用semantic-ia-fast-jump命令,不过在emacs-23.2里直接用这个命令可能会报下面的错误:

? View Code TEXT
semantic-ia--fast-jump-helper: Symbol's function definition is void: semantic-analyze-tag-references

这可能是emacs的bug,semantic-analyze-tag-references这个函数是定义在semantic/analyze/refs.el这个文件中的,而semantic/ia.el里写的是(eval-when-compile (require ’semantic/analyze/refs)),所以运行时这个feature没被load进来,我们需要自己load一下:

? View Code LISP
(require 'semantic/analyze/refs)

另外,官方CEDET里semantic-ia-fast-jump后可以通过命令semantic-mrub-switch-tags来回到曾经跳转过的地方,不过在emacs-23.2里会提示:

Semantic Bookmark ring is currently empty

这是因为semantic-ia-fast-jump会用函数push-mark把跳过的地方放到mark ring里去,官方CEDET通过定义push-mark的advice把它也放到了semantic-mru-bookmark-ring里去,semantic-mrub-switch-tags就是从semantic-mru-bookmark-ring来找位置的,但build in的CEDET里把push-mark的advice去掉了,所以semantic-mru-bookmark-ring总是空的,我的办法是把官方CEDET里对push-mark的device拷贝到我的.emacs中来:

? View Code LISP
1
2
3
4
5
6
7
8
(defadvice push-mark (around semantic-mru-bookmark activate)   "Push a mark at LOCATION with NOMSG and ACTIVATE passed to `push-mark'.
If `semantic-mru-bookmark-mode' is active, also push a tag onto
the mru bookmark stack."   (semantic-mrub-push semantic-mru-bookmark-ring
                      (point)                       'mark)   ad-do-it)

这样,我以前写的semantic-ia-fast-jump-back函数也能用了:

? View Code LISP
1
2
3
4
5
6
7
8
9
10
(defun semantic-ia-fast-jump-back ()   (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)))

对这个函数需要说明一下:网友fangzhzh提过可以用C-u C-space来跳回原来的mark,ahei说可以用C-x C-x来跳回,可我测试这两个按键好像跳得都有点乱,不能和semantic-ia-fast-jump的位置对应。我估计是这两个key是跳回push-mark函数mark的位置,而push-mark不光CEDET用。我的需求是只跳回semantic-ia-fast-jump曾经到过的地方,所以仍然保留了这个函数。

我的习惯还是绑定到F12上:

? View Code LISP
1
2
3
4
5
6
7
8
(defun semantic-ia-fast-jump-or-back (&optional back)   (interactive "P")   (if back
      (semantic-ia-fast-jump-back)     (semantic-ia-fast-jump (point)))) (define-key semantic-mode-map [f12] 'semantic-ia-fast-jump-or-back) (define-key semantic-mode-map [C-f12] 'semantic-ia-fast-jump-or-back) (define-key semantic-mode-map [S-f12] 'semantic-ia-fast-jump-back)

这儿多出来个semantic-ia-fast-jump-or-back函数,是因为我有时候在putty里操作远程的emacs,putty里用不了S-f12这个key,所以我把f12绑定到semantic-ia-fast-jump-or-back上,这样我可以在putty里通过C-u f12来跳回。

以前的semantic-analyze-proto-impl-toggle命令还能用:

? View Code LISP
(define-key semantic-mode-map [M-S-f12] 'semantic-analyze-proto-impl-toggle)
2.3 代码补全

官方版本里可以用命令semantic-ia-complete-symbol-menu弹出semantic的补全菜单,不过这个命令在内置的CEDET里不存在了(可能是因为emacs官方版本认为这个命令只在GUI下能用,不够通用吧)。

不过,内置的CEDET倒是可以通过命令complete-symbol(默认绑定到ESC-TAB)在另一个buffer里显示可能补全的内容,像这样:

semantic的complete-symbol

如果还希望能使用补全菜单,可以使用其它插件,比如auto-complete或company-mode:company-mode-0.5已经可以支持emacs内置的CEDET了;auto-complete-1.2对内置CEDET的支持还有些问题,关于如何配置auto-complete-1.2让它支持内置的CEDET,我准备另外写文章介绍。

3 EDE配置

ede和官方版本没有区别,仍然用(global-ede-mode t)启用就行了;不过emacs-23.3的Tools菜单下新增了”Project support (EDE)”菜单项,可以完成global-ede-mode一样的功能。

4 其它

4.1 可视化书签

官方CEDET里的visual-studio-bookmarks在内置的CEDET里没有了,所以我现在使用bm了。

4.2 pulse

pulse的功能在内置CEDET里还存在,不过官方CEDET里可以用pulse-toggle-integration-advice函数来切换pulse,在内置CEDET里这个函数消失了,现在的办法是设置pulse-command-advice-flag变量来切换:

? View Code LISP
(setq pulse-command-advice-flag (if window-system 1 nil))

另外,官方版本里对下面这些函数设置了pulse的device:

  • goto-line
  • exchange-point-and-mark
  • find-tag
  • tags-search
  • tags-loop-continue
  • pop-tag-mark
  • imenu-default-goto-function

内置版本里这些device都没了,所以我直接把官方版本里的advice拷贝过来了:

? View Code LISP
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
(defadvice goto-line (after pulse-advice activate)   "Cause the line that is `goto'd to pulse when the cursor gets there."   (when (and pulse-command-advice-flag (interactive-p))     (pulse-momentary-highlight-one-line (point)))) (defadvice exchange-point-and-mark (after pulse-advice activate)   "Cause the line that is `goto'd to pulse when the cursor gets there."   (when (and pulse-command-advice-flag (interactive-p)              (> (abs (- (point) (mark))) 400))     (pulse-momentary-highlight-one-line (point)))) (defadvice find-tag (after pulse-advice activate)   "After going to a tag, pulse the line the cursor lands on."   (when (and pulse-command-advice-flag (interactive-p))     (pulse-momentary-highlight-one-line (point)))) (defadvice tags-search (after pulse-advice activate)   "After going to a hit, pulse the line the cursor lands on."   (when (and pulse-command-advice-flag (interactive-p))     (pulse-momentary-highlight-one-line (point)))) (defadvice tags-loop-continue (after pulse-advice activate)   "After going to a hit, pulse the line the cursor lands on."   (when (and pulse-command-advice-flag (interactive-p))     (pulse-momentary-highlight-one-line (point)))) (defadvice pop-tag-mark (after pulse-advice activate)   "After going to a hit, pulse the line the cursor lands on."   (when (and pulse-command-advice-flag (interactive-p))     (pulse-momentary-highlight-one-line (point)))) (defadvice imenu-default-goto-function (after pulse-advice activate)   "After going to a tag, pulse the line the cursor lands on."   (when pulse-command-advice-flag
    (pulse-momentary-highlight-one-line (point))))

另外,我还喜欢对下面这些函数定义pulse:

? View Code LISP
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
(defadvice cua-exchange-point-and-mark (after pulse-advice activate)   "Cause the line that is `goto'd to pulse when the cursor gets there."   (when (and pulse-command-advice-flag (interactive-p)              (> (abs (- (point) (mark))) 400))     (pulse-momentary-highlight-one-line (point)))) (defadvice switch-to-buffer (after pulse-advice activate)   "After switch-to-buffer, pulse the line the cursor lands on."   (when (and pulse-command-advice-flag (interactive-p))     (pulse-momentary-highlight-one-line (point)))) (defadvice previous-buffer (after pulse-advice activate)   "After previous-buffer, pulse the line the cursor lands on."   (when (and pulse-command-advice-flag (interactive-p))     (pulse-momentary-highlight-one-line (point)))) (defadvice next-buffer (after pulse-advice activate)   "After next-buffer, pulse the line the cursor lands on."   (when (and pulse-command-advice-flag (interactive-p))     (pulse-momentary-highlight-one-line (point)))) (defadvice ido-switch-buffer (after pulse-advice activate)   "After ido-switch-buffer, pulse the line the cursor lands on."   (when (and pulse-command-advice-flag (interactive-p))     (pulse-momentary-highlight-one-line (point)))) (defadvice beginning-of-buffer (after pulse-advice activate)   "After beginning-of-buffer, pulse the line the cursor lands on."   (when (and pulse-command-advice-flag (interactive-p))     (pulse-momentary-highlight-one-line (point))))
4.3 h/cpp切换

官方CEDET里的eassist.el没有了,所以eassist-switch-h-cpp也没了,现在我用sourcepair代替,sourcepair比eassist-switch-h-cpp更好用。

4.4 代码折叠

semantic-tag-folding.el没有了,可我没找到其它更好的替代方案,所以我把官方CEDET里的semantic-tag-folding.el拷过来了,只需要把文件中(require ’semantic-decorate-mode)替换成(require ’semantic/decorate/mode)就能像以前一样用了。

以前的senator-fold-tag功能还可以使用。

最后插播个广告,我关于内置CEDET的配置(最后那部分):http://github/meteor1113/dotemacs/blob/master/init-basic.el

 

 

 

在Emacs下用C/C++编程

http://emacser/emacs-cpp-dev.htm

 

  • 1 参考文献
  • 2 序
  • 3 基本流程
  • 4 基本环境设置
    • 4.1 编辑环境配置
    • 4.2 自动补齐
  • 5 编译和调试程序
  • 6 阅读代码

1 参考文献

按照惯例,我写的文章在最开始处放参考文献。

  • hhuu @ newsmth 的《Emacs的日常生活》
  • emacs 的文档
  • emacs 相关插件的文档

2 序

用emacs写程序也有5个年头了,深切地体会到Emacs的强大。程序员有三种,一种是用vi的,一种是用emacs的,还有一种是其他。或许有些夸张,但也颇能体现出emacs在程序员中的地位。

emacs最大的问题在于入门门槛较高。它看起来和多数人想象中的IDE相差甚远,很多人看到emacs的第一眼就觉得它是个记事本(还是个非常难用的记事本),稍微好些的往往觉得emacs也就是个ultraEditor而已,真是暴殄天物了。

我是个懒人,不喜欢记太多的快捷键,相信很多人和我一样。所以从我后面的叙述可以看出来,除了常用的命令都是快捷键外,其他命令多数都是用M-x执行或者用鼠标点菜单。这仅仅是个人风格问题,先说明一下。

我的基本编程环境是:

  • Debian GNU/Linux sid 操作系统
  • Gnome 2.10.0 桌面环境
  • GUN Emacs 23.0.0.1 for debian
  • 使用 Gnu tool chains(gcc,make,gdb等等)

后面的叙述都基于上述环境。另外,本文主要针对C/C++程序开发,对其他语言有些也适用,从难度上说,本文主要针对入门者。

本文肯定会有很多错误,请指正, 谢谢。

3 基本流程

写C++程序基本上是这么几个步骤:

  1. 编辑代码
  2. 编写Makefile
  3. 编译代码,修改编译错误
  4. 调试代码,修改逻辑错误

当然,往往还需要阅读别人的代码。

根据上述步骤,本文主要针对以下几个方面:

  • 配置Emacs,建立便利的代码编辑环境和Makefile编写环境。
  • 在Emacs中编译代码,并修改编译错误。
  • 在Emacs中配合GDB调试程序。
  • 利用cscope和ecb在emacs中阅读代码

4 基本环境设置

4.1 编辑环境配置

要写C++程序,当然要用到cc-mode插件。CC-Mode原本是支持C语言的,但现在也能支持很多语言,比如 C++,Java,Objective-C,CORBA,AWK,Pike等等。CC-Mode是gnu-emacs的标准插件。如果您要求不高,那么默认的配置或许就能满足。CC-Mode的各种行为都可以自由地定制,您可以参考这里的文档:CC-Mode参考文档

这里是我的.emacs文件中关于CC-Mode配置的部分,仅供参考:

?[Copy to clipboard] View Code LISP
1
2
3
4
5
;;;; CC-mode配置  http://cc-mode.sourceforge/ (require 'cc-mode) (c-set-offset 'inline-open 0) (c-set-offset 'friend '-) (c-set-offset 'substatement-open 0)
?[Copy to clipboard] View Code LISP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
;;;;我的C/C++语言编辑策略  (defun my-c-mode-common-hook()   (setq tab-width 4 indent-tabs-mode nil)   ;;; hungry-delete and auto-newline   (c-toggle-auto-hungry-state 1)   ;;按键定义   (define-key c-mode-base-map [(control \`)] 'hs-toggle-hiding)   (define-key c-mode-base-map [(return)] 'newline-and-indent)   (define-key c-mode-base-map [(f7)] 'compile)   (define-key c-mode-base-map [(meta \`)] 'c-indent-command)   ;;  (define-key c-mode-base-map [(tab)] 'hippie-expand)   (define-key c-mode-base-map [(tab)] 'my-indent-or-complete)   (define-key c-mode-base-map [(meta ?/)] 'semantic-ia-complete-symbol-menu)

注意一下,上面最后两行是代码自动补齐的快捷键。后面我会提到代码自动补齐。

?[Copy to clipboard] View Code LISP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
;;预处理设置 (setq c-macro-shrink-window-flag t) (setq c-macro-preprocessor "cpp") (setq c-macro-cppflags " ") (setq c-macro-prompt-flag t) (setq hs-minor-mode t) (setq abbrev-mode t) ) (add-hook 'c-mode-common-hook 'my-c-mode-common-hook)  ;;;;我的C++语言编辑策略 (defun my-c++-mode-hook() (setq tab-width 4 indent-tabs-mode nil) (c-set-style "stroustrup") ;;  (define-key c++-mode-map [f3] 'replace-regexp) )

4.2 自动补齐

自动补齐通常用的都是hippie-expand,我也用了很长时间。不过有时候会觉得这个自动补齐“傻”了一点,常会补齐出一些毫不相干的东西,因为hippie-expand是根据你敲过的词和kill-ring等进行判断的,并不对程序语法进行分析。

所以你还需要安装一个代码分析工具,然后把它加进hippie-expand的扩展策略里去。我们可以用semantic。实际上,hippie-expand+semantic是我所发现的最好的选择了,如果您有更好的,请您也告诉我一声:)

Semantic是CEDET 中的一个工具,CEDET是Collection of Emacs Development Environment Tools的缩写,它包含了好几个工具,都挺不错的。可惜我只会用其中两个。

您可以在.emacs中对Semantic进行配置,下面是我的.emacs相关的配置,仅供参考:

导入cedet:

?[Copy to clipboard] View Code LISP
(load-file "~/lib/emacs-lisp/cedet-1.0pre3/common/cedet.el")

配置Semantic的检索范围:

?[Copy to clipboard] View Code LISP
1
2
3
(setq semanticdb-project-roots (list (expand-file-name "/")))

自定义自动补齐命令,这部分是抄hhuu的,如果在单词中间就补齐,否则就是tab。

?[Copy to clipboard] View Code LISP
1
2
3
4
5
6
7
8
(defun my-indent-or-complete ()   (interactive)   (if (looking-at "\\>")       (hippie-expand nil)     (indent-for-tab-command))   )  (global-set-key [(control tab)] 'my-indent-or-complete)

hippie的自动补齐策略,优先调用了senator的分析结果:

?[Copy to clipboard] View Code LISP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(autoload 'senator-try-expand-semantic "senator")  (setq hippie-expand-try-functions-list       '(         senator-try-expand-semantic
        try-expand-dabbrev
        try-expand-dabbrev-visible
        try-expand-dabbrev-all-buffers
        try-expand-dabbrev-from-kill
        try-expand-list         try-expand-list-all-buffers
        try-expand-line
        try-expand-line-all-buffers
        try-complete-file-name-partially
        try-complete-file-name         try-expand-whole-kill
        )       )

注意一下我前面CC-Mode配置中有这么两行:

?[Copy to clipboard] View Code LISP
1
2
(define-key c-mode-base-map [(tab)] 'my-indent-or-complete) (define-key c-mode-base-map [(meta ?/)] 'semantic-ia-complete-symbol-menu)

这样,我们在CC-Mode中就可以调用自定义的hippie补全了,快捷键是Tab。

另外,我还把快捷键“Alt + / ”绑定到了semantic-ia-complete-symbol-menu命令上,这是semantic的命令,它会根据分析结果弹出补齐的菜单,效果如图显示:

自动补齐效果图

CEDET中还有一个不错的工具是speedbar,你可以用它在多个文件中快速切换。在我的.emacs配置文件里,我把speedbar关联到了F5上:

?[Copy to clipboard] View Code LISP
(global-set-key [(f5)] 'speedbar)

这样用F5就可以调出speedbar,效果如下:

speedbar

不过说实话,我自己很少用到speedbar,我通常都是用dired配合bookmark使用:)

5 编译和调试程序

按上面的配置,写完程序和Makefile文件后,在Emacs源代码窗口中按F7就可以进行编译。因为在my-c-mode-common-hook()函数里,有这么一行:

?[Copy to clipboard] View Code LISP
(define-key c-mode-base-map [(f7)] 'compile)

默认情况下,emacs的compile命令是调用make -k,我把它改成了make。你也可以把它改成其他的,比如gcc之类的。改下面的“make”就行了。

?[Copy to clipboard] View Code LISP
'(compile-command "make")

Emacs会划分一个窗格显示编译的消息,在编译结束后,emacs会自动将编译器的输出和程序关联起来,告诉你第几行的程序有问题。直接在出错的行号上按Enter,就可以跳转到相应文件的相应行。其实我通常都是用鼠标中键去点出错行号:)

搞定了编译错误后,接着要和逻辑错误斗争了。其实对简单的程序来说,把中间结果打印到终端是最简单好用的调试办法:)不过稍微复杂点的程序就会晕菜了,这时我们就需要拿gdb跟踪程序流程了。

你用下面的命令就可以启动gdb了。

M-x gdb

通常我喜欢进入gdb-many-windows模式,这样就会把一个Frame划分为5个窗格,同时显示:gdb命令窗口,当前局部变量,程序文本,调用栈和断点。

gdb的命令就不在这里说了,它的文档几乎到处都是。emacs把gdb的命令和快捷键做了绑定,对于常用的命令,还是输入快捷键比较方便。比如,C-c C-n是Next line,C-c C-s是step in,其实用的最多的快捷键也就是这两个。

下面是我的gdb效果图:

GDB

6 阅读代码

在emacs下读代码通常有三种工具,最简单的是etags,最复杂的是ecb(emacs code browser),位于中间的是cscope。

etags和ctags一样,只不过前者是用于emacs的,后者是用于vi的。我个人觉得etags功能稍稍显得不够用一点,当然,也可能是我用的不好:) 欢迎大牛指导。

使用tags之前要先对源代码分析建立tags文件,在代码所在目录中运行:etags -R 即可。

我常用的就这几个命令和快捷键:

M-x visit-tags-table <RET> FILE <RET>   选择tags文件
M-. [TAG] <RET>                         访问标签
M-*                                     返回
C-u M-.                                 寻找标签的下一个定义

ecb据说功能强大,但是太复杂了,我懒得折腾它。谁搞定了教教我吧:) 下面是一张ecb的效果图。

cscope是我感觉比较合适的一个工具。它其实是一个独立的软件,完全可以脱离vi和emacs使用。但是结合emacs的强大功能,cscope就显得更加方便了。GNU Emacs默认自带cscope的支持。在使用之前,cscope也需要对代码进行索引。在emacs中可以这样做:

C-c s a             设定初始化的目录,一般是你代码的根目录
C-s s I             对目录中的相关文件建立列表并进行索引

建完索引之后,你就可以用cscope在代码里游荡了。常用的一些命令如下:

C-c s s             序找符号
C-c s g             寻找全局的定义
C-c s c             看看指定函数被哪些函数所调用
C-c s C             看看指定函数调用了哪些函数
C-c s e             寻找正则表达式
C-c s f             寻找文件
C-c s i             看看指定的文件被哪些文件include

上面这些快捷键其实我自己也常常记不全,没关系,抬头看看上面的菜单栏,有一栏就是Cscope,这些命令里头都有:)

贴一个cscope的效果图吧:

cscope

写完了。希望这篇文章对您能有一些用处。有问题或建议可以和我 联系。

 

 

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

http://emacser/cedet.htm

 

  • 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/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中了):

?[Copy to clipboard] View Code LISP
(require 'cedet)

4 semantic配置

先介绍一下最常用的semantic。

4.1 功能介绍

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

?[Copy to clipboard] 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的样子:

    这是打开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就可以根据自己的需要开启了。比如我的配置是:

?[Copy to clipboard] 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 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里去。下面是我的配置:

?[Copy to clipboard] 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的,如果不在这儿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上去了。

?[Copy to clipboard] 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上了:

?[Copy to clipboard] 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-x semantic-analyze-proto-impl-toggle可以跳到函数声明去,在声明处再执行的话就会再跳回函数体,我把它绑定到M-S-F12上了:

?[Copy to clipboard] 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/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]上:

?[Copy to clipboard] 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 v semantic-dependency-system-include-path RET检查INCLUDE路径,确保当前cpp中直接或间接include的头文件都能在INCLUDE路径中找到。

5 EDE配置

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

?[Copy to clipboard] 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,比如:

?[Copy to clipboard] 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里就带了一个可视化的书签,通过下面的语句可以启用它:

?[Copy to clipboard] 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它:

?[Copy to clipboard] View Code LISP
(require 'eassist nil 'noerror)

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

?[Copy to clipboard] 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配置为:

?[Copy to clipboard] 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目录下)。

?[Copy to clipboard] View Code LISP
1
2
(require 'semantic-tag-folding nil 'noerror) (global-semantic-tag-folding-mode 1)

看这个图:

cedet的代码折叠

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

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

?[Copy to clipboard] 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-+]上:

?[Copy to clipboard] 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关掉:

?[Copy to clipboard] 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的,最终我的配置如下:

?[Copy to clipboard] 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/meteor1113/dotemacs/blob/master/init-site.el

 

 

 

 

Emacs配置文件 - DEA

http://emacser/dea.htm

 

转载于:https://my.oschina/alphajay/blog/56142

本文标签: 文章 vim Emacs emacser