技术: Emacs环境配置

刚好换新的环境,顺便把emacs环境重新修正一下

最开始(刚开始工作的时候)接触的 IDE 其实是 SourceInsight . 那时候很多 android 框架的源码要看, 要分析, 也一度想要购买正版 SI. 后来机缘巧合单位的老前辈推荐我用 emacs, 从此踏上一条不归路.

本文主要记载我的emacs配置文件, 以及配置方法. 先给一个图吧. 中间可能有一些复杂,但是按照从简单到复杂的步骤其实还好; 想要省心省力, 不折腾的同学, 可以去购买一些商业的IDE, 节省了配置的时间.

(效率上取决于对于IDE的熟悉情况, 个人以为emacs没有上限)

引子

每当别人像我展示它的 vim 的时候, 我都是不屑的.

但是渐渐的, 如果有初学者问我要不要深入emacs, 我都会建议它: 最好不要, vistual studio code, qtcreator等都是不错的选择. 因为这东西会让你投入大量的时间折腾, 而且一旦习惯了快捷键操作, 效率是很高, 不过换一种没有配置的环境, 就会感觉很不习惯的.

Emacs 就是一个坑, 一个永远挖不完坑, 会上瘾的.

闲话不多说, 马上来正文. 效率Up Up Up!

正文

根据自己的用途,一步步配置环境,下面按照顺序叙述:

  • 配置基本faces和主题
  • 配置按键
  • 配置语言支持
  • 配置hightlight
  • 配置ibuffer
  • 配置dropdownlist
  • 配置gdb
  • 配置autosave
  • 配置session
  • 配置showhide
  • 配置tabbar
  • 配置symbol-overlay (f8,f9)
  • 配置cscode
  • 配置autocomplete和yasippet
  • 配置cedet
  • 配置ecb

中途还有一些个人使用习惯,不过多解释,只贴代码

明确用途

我使用emacs, 主要是在远程shell到服务器以及linux环境下做开发的(但不是所有的内容, 什么看网页啊, 看视频啊, 写邮件啊则不是), 归纳起来如下:

  • linux下的终端操作, 即shell中命令操作(所有命令)
  • c/c++开发, 包括代码的编写(各种补全, 提示等), 测试用例的编写, 调试, 日志输出, doc输出
  • 研究源码, 这个需求和上面有一定的区别, 研究源码的时候更加关注架构层次, 模型, 甚至详细的调用关系
  • linux下的shell脚本编写, 可能包括一部分python脚本
  • org-mode 文档的编写
  • git 日志查看以及提交(可能会结合gitk)

(我写 Java 和 Python 应用程序统统使用IDE, JetBrain的开发环境; 一般不写QT代码, 界面无感)

配置

中途的半成品

最终配置差不多就是这个样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;From Merlin deepin15.4;;;;;;;;;;;;;;;;;;;;;;;;;
;;; File name: ` ~/.emacs '
;;; ---------------------
;;;
;;; If you need your own personal ~/.emacs
;;; please make a copy of this file
;;; an placein your changes and/or extension.
;;;
;;; Copyright (c) 2017 deepin 15.4.1
;;;
;;; Author: Merlin Yu, <wizardmerlin945@gmail.com>
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 配置上, 除了基本配置外, 插件包可以采用自动管理;不过我还是采用 手动方式
;; (具体的版本&加强包可能自动下载不到)

;;(load-file "/home/merlin/.emacs.d/lisp/cedet/cedet-1.1/common/cedet.el")


;;自动包管理
;;(require 'merlin-init-autopackage)


;;;;; 手动方式(即下面自动递归添加~/.emacs.d/lisp内的包)
;;(add-to-list 'load-path "~/.emacs.d/lisp")
(defun add-subdirs-to-load-path (dir)
"Recursive add directories to 'load-path'."
(let ((default-directory (file-name-as-directory dir)))
(add-to-list 'load-path dir)
(normal-top-level-add-subdirs-to-load-path)))
(add-subdirs-to-load-path "~/.emacs.d/lisp")


;;;;;;;;;;;;;;;;;;;;基本配置;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;;配置基本的facet和主题
(load "merlin-init-ui")


;;配置基本并且常用的设置
(load "merlin-init-base")


;;配置高效的快捷键(这个要在相关的扩展插件之后)
;;也包括开发语言相关的
(load "merlin-init-key")


;; 配置语言模式
(load "merlin-init-language-mode")

;;;;;;;;;;;;;;;;;;;手动配置的各种插件;;;;;;;;;;;;;;;;;;;;;;;;;;


;;更好的自动保存插件
(load "merlin-init-autosave")


;;配置高亮当前行
(load "merlin-init-hl-line")


;; 配置高亮查找
(load "merlin-init-symbol-overlay")

;;配置自动补全
(load "merlin-init-autocomplete")


;; 代码折叠(本来希望依靠cedet的, 结果它不靠谱)
(load "merlin-init-showhide")


;;配置tab栏(注意只有图形化界面可以使用)
;;(load "merlin-init-tabbar")
;;不要配置tabbar了, 给ecb腾出快捷键(terminal使用场景偏多)

;;配置shell
(load "merlin-init-shell")


;;配置gdb
(load "merlin-init-gdb")


;;配置书签 (我一般不使用书签, 寄存器)
;;(load "merlin-init-bookmark")


;;配置 cscope
(load "merlin-init-cscope")


;;配置git
;;(load "merlin-init-git")


;;配置cedet
(load "merlin-init-cedet")

;;;配置ecb
(load "merlin-init-ecb");


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;not yet---begin::::::::::

;;;ediff
;;;dried
;;;doxygen
;;compile

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;not yet---end::::::::::




;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;;配置浏览器
(load "merlin-init-browser")


;;ibuffer
;;不同于系统自带的buffer, 它会单独打开一个buffer, 作为buffer的管理页面
(load "merlin-init-ibuffer")

;;保存session(下次启动的时候会在最近打开的菜单栏中显示)
;;(load "merlin-init-session")

;;配置desktop.el(每次启动的时候自动加载上次没有关闭的)
;;这个东西必须放在最后, 确保其可以正确辨别文件所需要的模式
(load "merlin-init-desktop")

参考gihub

代码太多了, 我直接上传了 github

补充配置

一些有用的,但是我个人又没有配置的

在配置yasnippet的时候,注意到了他的已经知道的模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
yasnippet:

高效设置:
C++ mode
(c++独有的)
acl cni delete ffo gnn ita mrg operator[] psc rpl std_colon tfm
acm cnt delete[] fil gnr iterator msm operator+ pst rtc sth this
ajf const_[] doc fin gtest ltr mxe operator+= ptc rte sti throw
alo constructor d_operator fixture ignore lwr namespace operator_istream ptn rvc sto trm
ano cout d_operator[] fln ihp lxc nno operator_ostream public rvr str try
assert cpb d_operator[]_const fnd ihu map ns ostream rci shf sts tryw
beginend cpi d_operator_istream fne inline member_function nth pack rmc spt stv ucp
boost_require cpn d_operator_ostream fni io mkh nxp phh rmf srh swr upr
cerr cpp dynamic_casting fori ipr mme oit ppt rmi srn template uqe
cin cpy enum fre ipt mne operator= private rmv srt test case using
class cstd eql friend iss module operator== protected rpc sstream test_main vector
class11 d+= erm fun_declaration isu mpb operator!= prp rpi std test_suite




C mode
(主要是添加一些头文件)
assert compile define malloc packed printf stdio stdlib string union unistd


cc mode
(c,c++共有的)
case else fopen for_n if inc main member_description printf switch typedef
do file_description for function_description ifdef inc.1 math once struct ternary while

备忘

这里记录一下,算是一个我自己的备忘吧.

Emacs中的按键不计其数,也可以自行设定,因为按键表示相应的lisp函数的快捷方式. 改坏了怎么办?, 把.meacs文件删除就好了.

启动参数

emacs的启动参数:

1
2
3
4
5
6
7
emacs -q   (安静启动, 不执行.emacs文件)
emacs -u joe (使用joe用户的.emacs启动)
emacs --debug-init (调试模式启动)
emacs -nw (terminal 中启动)
emacs practice.b --insert myfile (启动practice.b文件, 并把myfile插入到打开的buffer中)
emacs --insert myfile (把myfile插入到临时的buffer中)
emacs +15 practice.b (打开文件,并定位到15行; 注意, 如果是输入的数字长于文件的总长度, 那么就会定位到文件结尾)

手册

C-h t 查看教程, 该教程看完, 用熟悉; 初次之外可以查看 info 手册:

C-h i , 之后使用m+关键词查看, d返回, 回车是选中, 还可以使用u和l:翻页和返回上次浏览的地方)

man

使用man手册

  • M-x woman
  • M-x man
  • 然后输入相关的关键字

我一般使用 M-x man.
对于man手册的支持, emacs的woman还要去解析man文件(/usr/share/man), 感觉比较坑爹;

基本按键

  1. C-x i 插入文件(在已打开的文件内插入文件)
  2. C-x, C-w 另存为
  3. C-x u 撤销(C-/)
  4. C-x h 全部选中 (这个后面详细说)
  5. C-% 查找替换(C-s以及C-r是查找)
  6. C-x b 选中命名的buffer
  7. C-x, C-b 列出所有buffer
  8. M-x, kill-buffer 杀死buffer, 快捷键 C-x k
  9. C-v 向下翻页 (C-u 8 C-v 向下翻8页)
  10. M-v 向上翻页
  11. C-k 删除一行
  12. C-h m 查看当前模式(terminal可能)
  13. C-x C-q 设置或者取消只读模式
  14. 格式代码(缩进)
    C-x h TAB 格式化选中的代码
    C-c,C-q 对整个函数进行缩进 (光标应该在函数内, 并且需要再源码mode以内)
    选中区域, M-x untabify, 将 TAB 字符转换为空格
    选中区域, M-x indent-region, 对齐文本块
  15. 注释:
    M-; 在该行的末尾添加注释符号(当然选中块之后也就可以注释整个block)
    C-c C-c 对一块代码进行注释;
    取消注释用命令 uncomment-region (其实用undo也可以)
  16. 折叠代码(我个人设置的是 M+,)
  17. 标记和调整(我个人设置的是C-c m, C-c j)
  18. 括号间跳转:(括号和函数间跳转)
    括号之间来回跳转的时 候按 C-M-f 和 C-M-b.
    C-M-a 移动到当前函数的开始(如果和系统的快捷键冲突, 那么就多加一个shift键盘)
    C-M-e 移动到当前函数的结尾
    (注意可以把C-M-a, C-M-e合并到C-M-f和C-M-b中, 我通常采取的策略是使用%进行跳转)
  19. 标记移动和全选:
    标记整个页面 c-x,c-p (实际上这里多是整个buffer, 而不是整个frame)
    标记整个缓冲区 c-x h (常用)
    标记整个段落 m-h (通常没有用-而是用c-m-h)
    c-space 设置标记(如果冲突就多加一个shift 或者使用 c-@) 然后移动光标, 就会从标记的位置选中移动选中区域
    c-x c-x 快速返回移动前的光标位置(互换插入点和文本标记的位置)
    c-m-h 快速选中一个函数 (如何按键有冲突, 可以多加一个shift)
  20. 大小写转换(默认是被禁止)–注意是C-x开始
    downcase-region (C-x C-l) ;; 选定区域全部改为小写
    upcase-region (C-x C-u) ;; 选定区域全部改为大写
    可以直接在.emacs文件中设置:

    1
    2
    (put 'downcase-region 'disabled nil)
    (put 'upcase-region 'disabled nil)

    只是转换一个单词的话, 可以用 M-uM-l, 它会把从光标开始的后面一个单词变成大写或者小写.

窗口操作

frame, 缓冲区, 窗口的关系:
整个窗口在Emacs中叫做frame, 图形界面下的Emacs可以打开多个frame. 每个frame从上到下分成3部分,分别是缓冲区,状态栏和回显区(一般工具栏隐藏) 缓冲区是编辑的主区域, 但是在这里操作的还不是真正的文件, 而是文件的一个缓存(buffer)。只有执行写入操作时,才会将buffer的内容写入到文件. 缓冲区可以分成多个区域, 缓冲不同的内容. 这些区域在Emacs中成为”窗口”.

  1. C-x 1 只保留当前窗格
  2. C-x 0 关闭当前窗格
  3. C-x 2 切割为等宽半高的两个窗格
  4. C-x o 切换到另一个窗格 (窗口相关的可以参考, 参考ibuffer.el扩展)

缓冲区之下是状态栏,显示当前的一些状态信息,比如图中从左至右依次为:

1
-UU-:----F1  tmp            Bot L31    (Fundamental) -------

解释:

  • UU: 当前的文件编码是UTF-8, 如果是GBK会显示c
  • **: 文件状态,**表示未保存,–表示可写,%%表示只读(C-x, C-q)
  • tmp: 是当前编辑的文件的名称
  • All: 表示当前缓冲区已经显示文件的所有内容(Bot,表示处于文件的末尾处)
  • L31: 当前光标所在的行数
  • fundamental: 当前的模式

模式

模式:

  • 主要模式: 当前主要模式只有一种
    fundamental-mode: 缺省的 Emacs 模式,拥有最少设置和绑定
    text-mode:编辑文本的基本模式
    c-mode:用于编辑程序源代码 (以及其他源码模式)
    lisp-interaction-mode:用于编辑和编译 Lisp 代码
    ptex-mode:用于编辑 TeX 文档
  • 次要模式 : 可以组合到主要模式中(次要模式可以有多种), 如果输入的模式包含在当前模式中, 对于主要模式, 会清空所有的次要模式
    abbrev-mode:用于生成和使用缩写
    auto-fill-mode:用于自动文字回绕、填充较长的行和段落
    line-number-mode:显示当前行号(默认在状态栏上显示的就是)
    overwrite-mode:覆盖模式, 代替默认的插入模式
    以及其他插件模式

矩阵操作

(C-x r 开头; 和寄存器, 书签有重合)

这里可以补充一个矩形操作:(矩阵操作, 其实就是列模式)
其实我并没有觉得矩形剪切和复制时多么棒的东西, 平时用到的标记剪切其实也不错.

主要就是下面几个命令:(先用C-space或者C-@设一个mark, 移动光标到另一点; 剪切的其实是对角线围成的矩形)

1
2
3
4
5
6
7
8
C-x r k 剪切一个矩形块
C-x r y 粘贴一个矩形块
C-x r d 删除一个矩阵块


C-x r c 清除一个矩形块(使其变成空白)
C-x r o 插入一个矩形块(打开当前的矩形区块,使用空白字符填充整个区域,并将该矩形区块的所有文本移动到右边)
C-x r t 在选定区域的所有列前插入样的字符

(没有复制啊? 我说你傻啊, 先剪切, 然后undo, 然后粘贴)

Keystrokes Command name Action
C-x r k kill-rectangle Delete a rectangle and store it.
C-x r d delete-rectangle Delete a rectangle and do not store it.
C-x r y yank-rectangle Insert the last rectangle killed
C-x r c clear-rectangle Using spaces, blank out the area marked as a rectangle and do not store it.
C-x r o open-rectangle Insert a blank rectangle in the area marked.
C-x r r r copy-rectangle-to-register Copy rectangle to register r (where r is any character) .
C-x r i r insert-register Insert rectangle from register r (where r is any character).
(none) delete-whitespace-rectangle if a rectangle includes initial whitespace, deletes it, narrowing rectangle.
C-x r t string Enter string-rectangle Change contents of marked rectangle to string (if string is narrower
or wider than rectangle, dimensions change accordingly).
(none) string-insert-rectangle Prompts for string and inserts rectangle.


不满意的话, 还可以使用 cua-mode . (我个人不用)

除了emacs本身支持的列模式外,emacs还可以通过cua-mode支持一种可视化的列模式。在cua-mode下,按[C-return]会进入 cua rectangle模式。
在这个模式下可以通过鼠标点击确认起点,然后通过光标键来选中一个rect范围,这个rect会用另外的颜色显示出来。 直接输入字符: 在每行前(或后)都插入这个字符.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[M-a]: 将rect中的文字左对齐  
[M-b]: 用空格(tabs或者spaces)替换所有rect中的字符
[M-c]: 去掉所有行左侧的空格
[M-f]: 用单个字符替换所有rect中的字符(提示输入一个字符)
[M-i]: 对每行中第一个找到的数字进行加1操作(自动把0x开头的当作十六进制数字)
[M-k]: 剪切rect
[M-l]: 把rect中的内容全部转换为小写
[M-m]: 拷贝rect
[M-n]: 用一串自增的数字替换rect中的每一行(这个功能可以用来给每行编号) ### 非常好用的功能
[M-o]: rect的内容右移,选中的rect用空格填充
[M-r]: 用字符串替换符满足正则表达式的字符串
[M-R]: 上下反转
[M-s]: 把rect中的每一行替换为一个字符串(提示输入)
[M-t]: 把rect整个替换为一个字符串(提示输入)
[M-u]: 把rect中的内容全部转换为大写
[M-|]: 对rect执行一个shell命令

我个人一般最多用到矩阵模式.

寄存器

(这里的命令注意和矩阵相区别一下)
在多个文件中逛的时候,我们常常需要快速切换到先前访问的某个位置. 因此, 我们需要把文件及其光标位置暂时存放在某个地方. (一般我们都会根据register进行一定封装, 从而进行来回的跳转) 简单说, 这里的寄存器(register), 就是保存一些位置信息(光标位置, 文件信息等)

最基本的用法(和我定义的C-c,m以及C-c,j比起来作用弱一点儿)
将当前光标所在位置保存入一个register中:
C-x r SPACE + register名 (一个字符, 比如a吧), 然后我们就可以到处瞎逛,若要回到保存的register a位置,我们可以输入: C-x r j a

当然你可以定义好多个寄存器名字(位置)

1
2
M-x view-register    查看某一个register
M-x list-registers 查看所有的register

在进行矩阵操作的时候, 中间有两个命令: 保存到寄存器, 并存寄存器中恢复

1
2
3
C-x s X                 copy-to-register                          将选定区域保存到寄存器 X
C-x r r r copy-rectangle-to-register Copy rectangle to register r (where r is any character) .
C-x r i r insert-register Insert rectangle from register r (where r is any character).

通常来说, 我个人不会使用这么原始的方式(偶尔可能会用), 一般使用其他更加高级的标记方式,比如我自己定义的 C-.以及 C-, .
比如ctags, etags, gtags, 或者csope等都是比较好的选择. (我个人一般使用csope)

比如说 etags:
在代码目录中运行 etags -R
如果要向现有tags表中添加,则运行 etags -a
访问tag文件的话: M-x visit-tags-table , 常用的快捷键如下:

1
2
3
M-.       访问tag 
C-u M-. 访问下一个tag
M-* 返回

书签

保存缓冲区中位置的另一种工具(注意一下和矩阵操作的快捷键)
这些 Emacs 书签 的工作方式与寄存器相同,但是它们的标签可以超过一个字符长,而且它们比寄存器更为持久:如果保存了书签,那么您可以在两个不同的会话之间使用它们。它们将一直保留下来,直到您删除它们。正如它们的名称所表示的,对于保存您在缓冲区中的位置,以便您稍后可以返回到该位置(通常是在以后的 Emacs 会话期间),使用书签是非常方便的。

不过多介绍了:

1
2
3
4
5
C-x r m Bookmark	           bookmark-set	          设置一个名为 Bookmark 的书签
C-x r l bookmarks-bmenu-list 列出所有已保存的书签
C-x r b Bookmark bookmark-jump 跳转至名为 Bookmark 的书签中所设置的位置(实际上是恢复书签)
未定义 bookmark-delete 删除一个书签
未定义 bookmark-save 将所有的书签保存到书签文件中(这样下次启动就可以跨越session了)

注意, 自从emacs24, 退出的时候 书签是自动保存在相关文件中的:

  • In emacs 24.x, bookmark file is at ~/.emacs.d/bookmarks.
  • In emacs 23.x, bookmark file is at ~/.emacs.bmk.
    你也可以自己设置:
    1
    2
    3
    (setq bookmark-save-flag 1) ; everytime bookmark is changed, automatically save it
    (setq bookmark-save-flag t) ; save bookmark when emacs quits
    (setq bookmark-save-flag nil) ; never auto save.

当然你可以设置在每次启动的时候, 自动载入书签:

1
2
3
(require 'bookmark)
(bookmark-bmenu-list)
(switch-to-buffer "*Bookmark List*")

(我个人使用desktop插件, 所以不写该句)

当然你可以进行设置一些快捷键:

1
2
(global-set-key [(f9)] 'list-bookmarks)
(global-set-key [(f10)] 'bookmark-set)

在list bookmark之后(相当于打开了bookmark文件), 可以使用下列命令在列表中操作:

Type d to mark the current item for remove.
Type x to remove all D marked ones. (执行)
Type r to rename current item’s title.
Type s to save the change.

现在有了 desktop, 书签显得比较鸡肋.

cscope

下面说说我cscope的配置: (cscope和ecb功能上有一定重合)
GNU Emacs默认自带etags的支持, 但是不如cscope强大, cscope本身是一个独立软件, (建立索引之后)而且非常强大.

可以独立安装:

1
2
3
4
5
$ which cscope
/usr/local/bin/cscope

$ file cscope/contrib/xcscope/cscope-indexer
cscope/contrib/xcscope/cscope-indexer: POSIX shell script, ASCII text executable

在外部使用:

要使用 cscope, 先建索引, 假设所有的头文件和h都在src下面

1
2
find src/ -type f -iname "*.h" >  cscope.files (这里创建)
find src/ -type f -iname "*.cpp" >> cscope.files (这里是>>追加)

(把所有符号都追加到一个文件可以加快建立索引文件的速度, 但是不利于之后展开文件)

1
2
3
4
#!/bin/sh

find . -name "*.h" -o -name "*.c" -o -name "*.cc" > cscope.files
cscope -Rbkq -i cscope.files

其他选项解释:

  • -R: 在生成索引文件时, 搜索子目录树中的代码
  • -b: 只生成索引文件, 不进入cscope的界面
  • -q: 生成cscope.in.out和cscope.po.out文件,加快cscope的索引速度
  • -k: 在生成索引文件时, 不搜索/usr/include目录
  • -i: 如果保存文件列表的文件名不是cscope.files时,需要加此选项告诉cscope到哪儿去找源文件列表。可以使用“-”,表示由标准输入获得文件列表
  • -I dir: 在-I选项指出的目录中查找头文件
  • -u: 扫描所有文件, 重新生成交叉索引文件
  • -C: 在搜索时忽略大小写
  • -P path: 在以相对路径表示的文件前加上的path, 这样你不用切换到你数据库文件所在的目录也可以使用它了

使用命令 cscope -Rbkq, 产生文件如下:

1
2
3
4
5
6
7
8
9
10
cscope.in.out
cscope.out //基本的符号索引
cscope.po.out
```

注意一下, k在生成索引文件时, 不搜索/usr/include目录; 此时初始化和建立索引已经完成了, 之后就可以利用命令进行查找了.


在emacs里面使用:
当然在emacs程序里, 也可以进行初始化和建立索引(如果所有的源代码以及子目录都是在同一个目录下面的).

;; C-c s a 初始化目录( 设定初始化的目录,一般是你代码的根目录)
;; C-c s I 对目录中的文件建立列表索引(生成 Cscope 的数据库)(cscope-set-initial-directory) 这一步最重要

1
2
3
4
5
6
一般使用 `C-c s I` 就可以了, 对应 `cscope-set-initial-directory` . 如果源代码全部处于同一个目录下面,现在就可以使用了. (个人习惯还是喜欢再外部使用cscope -bkqR)


注意:

如果源代码有多层目录,或者其他地方还有附加的源代码,则需要 `cscope-indexer` 脚本. 把那个脚本拷贝到系统 PATH 里面去(如 /usr/bin/).

$ sudo chmod u+x cscope-indexer

export PATH=$PATH:~/.emacs.d/lisp/cscope/contrib/xcscope

1
2

然后在根目录, 建立索引

$ cscope-indexer -r

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19


只要执行 C-c s I(cscope-index-files), 即`` 就可以生成 Cscope 的数据库, 接下来就可以使用了.
xcscope默认的快捷键都是绑定到 C-c s 的前缀上面, 更详细的使用说明请参见 xcscope.el 文件头部的注释.


我个人使用的是默认绑定: (必须要建立完索引)

* C-c s s Find symbol. 寻找符号(查找C语言符号,即查找函数名、宏、枚举值等出现的地方)
* C-c s d Find global definition. 寻找全局定义---一般用这个来查找具体的定义
* C-c s g Find global definition (alternate binding). 寻找符号, 变量的定义
* C-c s G Find global definition without prompting.
* C-c s c Find functions calling a function. 看看指定函数被哪些函数所调用
* C-c s C Find called functions (list functions called
from a function). 看看指定函数调用了哪些函数
* C-c s t Find text string. 查找字符串
* C-c s e Find egrep pattern. 正则表达式查找
* C-c s f Find a file. 寻找文件
* C-c s i Find files #including a file. 看看指定的文件被哪些文件include

下面是在搜索到的结果之间切换用的快捷键:(一般不用, 直接跳转到窗口)

1
2
3
4
5
6
7
C-c s b         Display *cscope* buffer.
C-c s B Auto display *cscope* buffer toggle.
C-c s n Next symbol.
C-c s N Next file.
C-c s p Previous symbol.
C-c s P Previous file.
C-c s u Pop mark.

将光标停在函数名上, 按C-c s d, 回车, 即可以查询相关的定义.
xcscope.el 原来定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
;; * Keybindings:
;;
;; All keybindings use the "C-c s" prefix, but are usable only while
;; editing a source file, or in the cscope results buffer:
;;
;; C-c s s Find symbol.
;; C-c s d Find global definition.
;; C-c s g Find global definition (alternate binding).
;; C-c s G Find global definition without prompting.
;; C-c s c Find functions calling a function.
;; C-c s C Find called functions (list functions called
;; from a function).
;; C-c s t Find text string.
;; C-c s e Find egrep pattern.
;; C-c s f Find a file.
;; C-c s i Find files #including a file.
;;
;; These pertain to navigation through the search results:
;;
;; C-c s b Display *cscope* buffer.
;; C-c s B Auto display *cscope* buffer toggle.
;; C-c s n Next symbol.
;; C-c s N Next file.
;; C-c s p Previous symbol.
;; C-c s P Previous file.
;; C-c s u Pop mark.
;;
;; These pertain to setting and unsetting the variable,
;; `cscope-initial-directory', (location searched for the cscope database
;; directory):
;;
;; C-c s a Set initial directory.
;; C-c s A Unset initial directory.
;;
;; These pertain to cscope database maintenance:
;;
;; C-c s L Create list of files to index.
;; C-c s I Create list and index.
;; C-c s E Edit list of files to index.
;; C-c s W Locate this buffer's cscope directory
;; ("W" --> "where").
;; C-c s S Locate this buffer's cscope directory.
;; (alternate binding: "S" --> "show").
;; C-c s T Locate this buffer's cscope directory.
;; (alternate binding: "T" --> "tell").
;; C-c s D Dired this buffer's directory.

如果你喜欢自定议案件的话, 提供自定义按键参考:

1
2
3
4
5
6
7
8
9
10
11
12
(define-key global-map [(control f3)]  'cscope-set-initial-directory)
(define-key global-map [(control f4)] 'cscope-unset-initial-directory)
(define-key global-map [(control f5)] 'cscope-find-this-symbol)
(define-key global-map [(control f6)] 'cscope-find-global-definition)
(define-key global-map [(control f7)] 'cscope-find-global-definition-no-prompting)
(define-key global-map [(control f8)] 'cscope-pop-mark)
(define-key global-map [(control f9)] 'cscope-next-symbol)
(define-key global-map [(control f10)] 'cscope-next-file)
(define-key global-map [(control f11)] 'cscope-prev-symbol)
(define-key global-map [(control f12)] 'cscope-prev-file)
(define-key global-map [(meta f9)] 'cscope-display-buffer)
(define-key global-map [(meta f10)] 'cscope-display-buffer-toggle)

搜索和替换

由于我使用了插件 symbol-overlay, 所以一般是借助f8标记和操作标记, 和 M-r重命名, M-s切换到isearch.

下面是可能涉及到的查找:
C-% 查找替换
C-s 以及 C-r是递归查找
M-x grep-find, 已经设置快捷键为f3
find . -type f -exec grep --color -nH -e "content" {} +

下面可以详细说一下技巧, 因为查找&替换实在用的太多了:

Emacs中的搜索包括增量搜索和一般搜索。增加搜索是每次在前一次搜索的结果基础上继续搜索。在增量搜索时,如果上一次搜索之后进行了其他操作,则需要连续按两次快捷键才能召回关键词. 可以选中区块后在区块内进行搜索.

增量查找:
C - s向下查找
C - r向上查找
按下C - s后输入要搜索的词,emacs会即时显示当前光标后第一个搜索到的结果,按C - s会跳到下一个结果,按C - r会跳到上一个结果.
按Enter结束查找或按C - g取消查找回到原来的地方。
按下C - s 或 C - r后, 按M - p显示上一个搜索词, M - n显示下一个搜索词。类似C - p是上一行,C - n下一行.
按下C - s或 C - r后, 输入要查找的词的头几个字, 然后按C - w 会补全当前位置的单词.

查找替换:
按M - %启动查找替换,输入要被替换的词,回车,然后输入要替换的词,再回车。
被替换的词会高亮起来,这时,

  • 输入y替换并跳到下一个
  • ^ 返回上一个替换点
  • 输入n忽略并跳到下一个
  • 输入q结束,输入!替换剩下的全部

C - r 进入修改
(我一般重构的时候, 使用插件, M-r进行全部改名, M-s回归递归查找)

取消搜索
C-g 取消搜索,光标返回搜索前的位置
RET结束搜索,光标停留在当前位置。

Ocuur模式
有时候想列出匹配的全面模式,而不是在文档中浏览,这个可以使用occur这个函数。
例子:M - x occur RET 关键词 RET
这时emacs会新开一个窗口来列出匹配的行
(实际上再 M-s之后, 案件M-s o即可达到同样的效果)

模式

写代码的cc-mode(cc-mode 可以用来写 C/C++/Java/Obj-C)

使用cc-mode:
M-x c-set-style, 然后选择cc-mode

1
2
M-x customize 定制界面等
M-x customize-themes 定制主题等

文件编码

文件编码方面, 一般linux下都采用utf-8即可, 或者设置 (prefer-coding-system 'utf-8) .

但是如你在gbk环境下, 比如你的windows7设置的是gdk, 那么此时应该设置gbk环境编码(否则读入文件时的解释编码和系统编码不一致, 肯定乱码了)

1
2
3
4
5
6
7
8
9
10
11
(setq file-name-coding-system 'gbk)
(set-terminal-coding-system 'gbk)
(set-keyboard-coding-system 'gbk)
(setq locale-coding-system 'gbk)
(set-selection-coding-system 'gbk)
(set-clipboard-coding-system 'ctext)
(set-clipboard-coding-system 'gbk)
(set-terminal-coding-system 'gbk)
(set-buffer-file-coding-system 'gbk)
(modify-coding-system-alist 'process "." 'gbk)
(setq default-process-coding-system '(gbk . gbk))

tabbar

关于 tabbar 的再一次调整:

要达到的效果是什么呢?
是要在一个 buffer 里面移动, 而不会切换到被的分组.
键位的设定时我特意改用了tabbar-backward-tab和tabbar-forward-tab 代替tabbar-backward和tabber-forward.
效果是使用M-left/right的时候移动不会跨组, 也就是只能在当前分组内移动, 这样好很多.
中间一段设置把标签栏左边的那些按钮都取消掉了.

原来是不分组, 最后发现还是要根据主模式分组比较好.(即下面的代码全部作废)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
(require 'tabbar)  

(tabbar-mode)

(global-set-key (kbd "<M-up>") 'tabbar-backward-group)
(global-set-key (kbd "<M-down>") 'tabbar-forward-group)
(global-set-key (kbd "<M-left>") 'tabbar-backward-tab)
(global-set-key (kbd "<M-right>") 'tabbar-forward-tab)

(setq
tabbar-scroll-left-help-function nil ;don't show help information
tabbar-scroll-right-help-function nil
tabbar-help-on-tab-function nil
tabbar-home-help-function nil
tabbar-buffer-home-button (quote (("") "")) ;don't show tabbar button
tabbar-scroll-left-button (quote (("") ""))
tabbar-scroll-right-button (quote (("") "")))


(defun my-tabbar-buffer-groups ()
"Return the list of group names the current buffer belongs to.
Return a list of one element based on major mode."
(list
(cond
((or (get-buffer-process (current-buffer))
;; Check if the major mode derives from `comint-mode' or
;; `compilation-mode'.
(tabbar-buffer-mode-derived-p
major-mode '(comint-mode compilation-mode)))
"Process"
)
((string-equal "*" (substring (buffer-name) 0 1))
"Emacs Buffer"
)
((eq major-mode 'dired-mode)
"Dired"
)
(t
"User Buffer"
))))

(setq tabbar-buffer-groups-function 'my-tabbar-buffer-groups)

gdb

一般都是激活多窗口调试模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
+----------------------------------------------------------------------+
| GDB Toolbar |
+-----------------------------------+----------------------------------+
| GUD buffer (I/O of GDB) | Locals buffer |
| | |
| | |
| | |
+-----------------------------------+----------------------------------+
| Source buffer | I/O buffer (of debugged program) |
| | (comint-mode) |
| | |
| | |
| | |
| | |
| | |
| | |
+-----------------------------------+----------------------------------+
| Stack buffer | Breakpoints buffer |
| RET gdb-frames-select | SPC gdb-toggle-breakpoint |
| | RET gdb-goto-breakpoint |
| | D gdb-delete-breakpoint |
+-----------------------------------+----------------------------------+

窗口说明:

  • GDB Toolbar - GDB 操作Toolbar
  • GUD buffer - 执行操作的buffer
  • Locals buffer - 本地变量名和值的表示buffer
  • Source buffer - 表示sourcecode的buffer
  • IO/ buffer - 表示程序的输入输出的buffer
  • Stack buffer - 运行停止的时候,调用关系的表示buffer
  • Breakpoints buffer - breakpoints断点的表示buffer
  • buffer崩溃的时候、通过’M-x gdb-restore-windows’返回原状态

当然你可以尝试关闭某些窗口.

1
(setq gdb-use-separate-io-buffer t) ; 不需要"IO buffer"时,则设为nil

一般会把 gdb-many-windows 设置为激活状态, 实际上也可以用 gdb-restore-windows 恢复单个布局.

我个人的习惯是, 把所有的其他窗口只是作为观察窗口, 其实还是依赖主窗口, 设置相关命令, 一方面, 这个可以减少快捷键的记忆; 另外一方面, 不和单独在terminal中使用产生冲突, 其实也不错. (gud是主窗口)

快捷键命令

  • 添加断点 gud-break C-x C-a C-b 或 C-x
  • 删除断点 gud-remove C-x C-a C-d
  • 运行/继续程序 gud-go 无
  • 单步执行,无视函数 gud-next C-x C-a C-n
  • 单步执行,进入函数 gud-step C-x C-a C-s
  • 跳出当前函数 gud-finish C-x C-a C-f
  • 运行到光标所在语句 gud-until C-x C-a C-u
  • 继续运行程序 gud-cont C-x C-a C-r

Autocomplete

自动补全的拆建有很多, 但是真正能够TAB键玩的溜的只有 Auotocomplete .

autocomplete在使用上, 不用刻意去按键M-/, 而是在你写代码的时候, 就可以完成提示, 并且选择的话, 一直TAB就行, 之后就可以直接在后面接着写其他的代码. (按回车可以选中所选的)

具体的快捷键:

  • TAB, C-i ac-expand Completion by TAB
  • RET, C-m ac-complete Completion by RET
  • down, M-n ac-next Select next candidate
  • up, M-p ac-previous Select previous candidate
  • C-?, f1 ac-help Show buffer help

To stop completion, simply use C-g.

magit

emacs中使用git虽然很好, 但是, 无疑来说, 又会增加许多负担, 当前还是使用terminal或者gitk吧. 具体可以考虑使用 magit 插件.

speedbar

就是一个文件列表, 一般再ecb中有集成.

M-x speedbar 就可以启动它(相关于一个文件目录, 可以在多个文件中切换), 我个人设置的快捷键是 f4 .

ediff

一个相当于beyond compare的组件: M-x ediff.

dired

emacs下一个强大的文件管理器, 大多数切换到 shell 的场景被它取代: M-x dired.

游戏

M-x tetris, 俄罗斯方块.

hideshow

代码折叠与展开, 这个插件有用, 不过要进行一定的调整.

1
2
3
4
5
6
7
8
9
10
11
12
13
;;配置代码折叠
;(load-library "hideshow")
;(add-hook 'java-mode-hook 'hs-minor-mode)
;(add-hook 'perl-mode-hook 'hs-minor-mode)
;;(add-hook 'php-mode-hook 'hs-minor-mode)
;(add-hook 'emacs-lisp-mode-hook 'hs-minor-mode)

(add-to-list 'hs-special-modes-alist
'(c-mode "[\n\t ]*{" "}" "/[*/]" nil hs-c-like-adjust-block-beginning))
(add-to-list 'hs-special-modes-alist
'(c++-mode "[\n\t ]*{" "}" "/[*/]" nil hs-c-like-adjust-block-beginning))

(define-key global-map (kbd "M-,") 'hs-toggle-hiding)

我一般设置的是 M-, 用于折叠和展开.

窗口恢复

原生没有设置案件对窗口误操作的undo, 所以我设置了一下按键:

1
2
C-x 4 u 窗口undo
C-x 4 r 窗口redo

doxygen

这个设置起来不太麻烦, 但是我还是喜欢在shell中使用, 毕竟它可以生产图, shell操作更加方便.

cedet

给Emacs安装CEDET是最烦恼的过程之一, 由于这个插件太大了, 所以出错的几率也比较打.

CEDET包含以下几个部分:

  • Semantic - Parser Infrastructure for Emacs
  • EDE - File manager/ Makefile generator
  • SRecode - Template manager/ code generator
  • COGRE - Connected Graph Editor

emacs自带的cedet为1.0版本, 不能与ecb配合使用, 所以要下载并安装它的最新版本(LAST) cedet1.1 . 安装的时候, 请仔细阅读源码目录的 INSTALL 文件, 我直接下载的解压缩后的文件.

1
$ git clone https://github.com/emacs-pkg-mirrors/cedet.git

根据它官网的步骤, 一点儿不靠谱. 请仔细阅读源码目录的 INSTALL 文件!!!

  • Step 1 : Download CEDET from the Sourceforge downloads page.
  • Step 2 : unpack CEDET
  • Step 3 : make EMACS=emacs
  • Step 4 : Configure CEDET in your .emacs file by adding code like this.

主要代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
-----------
;; Load CEDET.
;; See cedet/common/cedet.info for configuration details.
;; IMPORTANT: For Emacs >= 23.2, you must place this *before* any
;; CEDET component (including EIEIO) gets activated by another
;; package (Gnus, auth-source, ...).
(load-file "~/cedet-VERSION/common/cedet.el")

;; Enable EDE (Project Management) features
(global-ede-mode 1)

;; Enable EDE for a pre-existing C++ project
;; (ede-cpp-root-project "NAME" :file "~/myproject/Makefile")


;; Enabling Semantic (code-parsing, smart completion) features
;; Select one of the following:

;; * This enables the database and idle reparse engines
(semantic-load-enable-minimum-features)

;; * This enables some tools useful for coding, such as summary mode,
;; imenu support, and the semantic navigator
(semantic-load-enable-code-helpers)

;; * This enables even more coding tools such as intellisense mode,
;; decoration mode, and stickyfunc mode (plus regular code helpers)
;; (semantic-load-enable-gaudy-code-helpers)

;; * This enables the use of Exuberant ctags if you have it installed.
;; If you use C++ templates or boost, you should NOT enable it.
;; (semantic-load-enable-all-exuberent-ctags-support)
;; Or, use one of these two types of support.
;; Add support for new languages only via ctags.
;; (semantic-load-enable-primary-exuberent-ctags-support)
;; Add support for using ctags as a backup parser.
;; (semantic-load-enable-secondary-exuberent-ctags-support)

;; Enable SRecode (Template management) minor-mode.
;; (global-srecode-minor-mode 1)

编译时遇到的问题:

`makeinfo is missing on your systeme, 即makeinfo未找到命令, 此时有两种方案:

  • make EMACS=emacs MAKEINFO=echo 即不编译doc
  • make MAKEINFO=/usr/bin/makeinfo

安装 makeinfo, 它包含再texinfo包中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ search apt-file
apt-file - search for files within Debian packages (command-line interface)
cabal-debian - Create a debianization for a cabal package

#利用apt-file查看某个文件属于哪个包, 再安装包
$ add apt-file
$ sudo apt-updateapt
$ apt-file search bin/makeinfo
texinfo: /usr/bin/makeinfo
$ search exinfo
texinfo - Documentation system for on-line information and printed output

$ add texinfo
$ add install-info

http://sourceforge.net/projects/cedet/ 下载 cedet1.1 .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
In end of data:
semantic-tag-file.el:207:1:Warning: the function ‘ede-toplevel’ is not known
to be defined.
Eager macro-expansion failure: (invalid-function class-p)
Eager macro-expansion failure: (invalid-function class-p)
Eager macro-expansion failure: (invalid-function class-p)
Eager macro-expansion failure: (invalid-function class-p)
Eager macro-expansion failure: (invalid-function class-p)
Eager macro-expansion failure: (invalid-function class-p)
Eager macro-expansion failure: (invalid-function class-p)
Eager macro-expansion failure: (invalid-function class-p)

In toplevel form:
semantic-idle.el:42:1:Error: Invalid function: class-p

In semantic-tag-customize:
semantic-custom.el:210:22:Warning: ‘toggle-read-only’ is an obsolete
function (as of 24.3); use ‘read-only-mode’ instead.
Makefile:61: recipe for target 'semantic' failed

总之编译的时候, 就是会遇到各种各样的问题. 一定要简单安装cedet, 它真的太复杂了(我安装它也只是为了安装ecb).

ecb

这个代码浏览器, 比较实用. 配置好了普通编写代码, 跑测试用例的界面大概是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 
------------------------------------------------------------------
| | |
| Directories | |
| | |
|--------------| |
| | |
| Sources | |
| | |
|--------------| Edit-area |
| | (can be splitted in several edit-windows) |
| Methods | |
| | |
|--------------| |
| | |
| History | |
| | |
------------------------------------------------------------------
| |
| Persistent Compilation-window (optional) |
| |
------------------------------------------------------------------

我一般设置如下快捷键:

  • f5 启用ecb
  • f6 禁用ecb
  • control + f5 显示ecb窗口
  • control + f6 隐藏ecb窗口
  • C-c 0-4 分别是还原,以及最大化某个窗口
  • M-方向键, 切换到某个窗口
  • 选定某个窗口的某个展开标记(即加号减号) 方向键左代表折叠, 方向键右代表展开.

文档

学些emacs, 手册中遇到的(可能)生词:

  • overlap 交叠
  • likewise 相似的
  • opposite 相反的
  • interspersed 穿插
  • parallel 并行
  • repeated 重复
  • paraphrase 释义
  • terminal终端
  • get rid of 摆脱
  • contiguous 邻近的
  • incremental 增量
  • for reference 仅供参考
  • sufficient 足够
  • command 命令
  • scrolling 滚动
  • numeric argument 数字参数
  • yank 召回,剪切
  • echo area 回显区
  • mode line 状态栏
  • incremental search 渐进式搜索

尾巴

有些插件真坑爹…编译安装都烦, 莫名其妙的问题, 浪费了好长时间.

首先 emacs 还是很强大的, 然而不得不说, 由于平台的严谨, 以及开后和后续支持的缺乏, 对于emacs的兼容性造成了很大的调整. 如果和visual studio code相比, 其兼容性明显不好. 而且这种情况如果没有更多的人参与和带动, 不说emacs会死, 至少可以说, 能玩的起来的都是神了.

唯一的希望是, emacs的兼容性可以好起来(各种插件的稳定性慢慢变好一些).


参考资料

  1. 一年成为 Emacs 高手
  2. A Guided Tour of Emacs
  3. emacs按键绑定方式介绍
  4. emacs高亮插件推荐
  5. emacs项目路径设置
  6. woman参考
  7. 查找替换参考
  8. 查找汇总
  9. 书签和寄存器
    (文中有一处错误:删除书签的命令是 M-x bookmark-delete而不是C-x bookmark delete)
  10. gdb参考
  11. autocomplete参考
  12. magit参考
  13. hideshow

这一些列文档差不多一共有7篇, 讲的比较啰嗦, 可以自行参考一下:

文章目录
  1. 1. 引子
  2. 2. 正文
    1. 2.1. 明确用途
    2. 2.2. 配置
      1. 2.2.1. 参考gihub
    3. 2.3. 补充配置
    4. 2.4. 备忘
      1. 2.4.1. 启动参数
      2. 2.4.2. 手册
      3. 2.4.3. man
      4. 2.4.4. 基本按键
      5. 2.4.5. 窗口操作
      6. 2.4.6. 模式
      7. 2.4.7. 矩阵操作
      8. 2.4.8. 寄存器
      9. 2.4.9. 书签
      10. 2.4.10. cscope
      11. 2.4.11. 搜索和替换
      12. 2.4.12. 模式
      13. 2.4.13. 文件编码
      14. 2.4.14. tabbar
      15. 2.4.15. gdb
      16. 2.4.16. Autocomplete
      17. 2.4.17. magit
      18. 2.4.18. speedbar
      19. 2.4.19. ediff
      20. 2.4.20. dired
      21. 2.4.21. 游戏
      22. 2.4.22. hideshow
      23. 2.4.23. 窗口恢复
      24. 2.4.24. doxygen
      25. 2.4.25. cedet
      26. 2.4.26. ecb
    5. 2.5. 文档
  3. 3. 尾巴
  4. 4. 参考资料
|