以下的Emacs配置文件是我多年积累起来的,它们在我历次整理配置文件(.emacs)的过程中幸存了下来,经过了时间考验,所以现在我决定发出来和大家分享一下。虽然某些功能很有可能已经有更好的实现方法了,但是这些例子对读者学习emacs lisp还是会有帮助的。
在一些文本的末尾添加递增的数字
inc-num-region把一段文本中重复出现的数字替换成递增的数字
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
(defun inc-num-region (p m)
"Increments the numbers in a given region"
(interactive"r")
(save-restriction
(save-excursion
(narrow-to-region p m)
(goto-char(point-min))
(forward-line)
(let ((counter 1))
(while(not (eq (point)
(point-max)))
(goto-char(point-at-eol))
(search-backward-regexp"[0-9]+"(point-at-bol) t)
(let* ((this-num (string-to-number (match-string 0)))
(new-num-str (number-to-string (+this-num
counter))))
(replace-matchnew-num-str)
(incf counter)
(forward-line)))))))
|
比如在emacs选中如下的文本区域
1
2
3
4
|
1foo
1foo
1foo
1foo
|
执行该函数,那么上述文本在缓冲区中变成
1
2
3
4
|
1foo
2foo
3foo
4foo
|
再比如选中如下的文本区域
1
2
3
4
|
foo3
foo3
foo3
foo3
|
执行给函数,得到
1
2
3
4
|
foo3
foo4
foo5
foo6
|
给代码做笔记
在我们公司使用reviewboard之前,代码审查都是面对面进行的。我曾经使用下面这个函数来帮助记录意见所对应的源文件和行号。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
(defun add-code-review-note ()
"Add note for current file and line number"
(interactive)
(let ((file-name (buffer-file-name))
(file-line (line-number-at-pos)))
(switch-to-buffer-other-window (get-buffer-create"NOTES"))
(goto-char(point-min))
(when (not (search-forward"-*- mode:compilation-shell-minor"
nil t))
(compilation-shell-minor-mode 1)
(insert"-*- mode:compilation-shell-minor -*-\n\n"))
(goto-char(point-max))
(if(/= (current-column) 0)
(newline))
(insert file-name":"(number-to-string file-line)": ")))
|
使用方法是,光标停在源代码的需要做批注的位置,然后执行该函数,emacs会创建一个新的叫做NOTES的缓冲区,其中记录源代码的路径和光标所在的行号,用户在接下来的区域中输入笔记。这个函数的好处是,该新建的buffer的工作模式是compilation-shell-minor-mode。所以可以直接点击其路径和行号,就可以直接打源文件跳到相应的行上去。比如
1
2
3
4
5
6
7
|
#include
intmain()
{
std::cout <<"Hello Word!"<< std::endl; //光标停在这里
return0;
}
|
执行该函数,在新buffer中得到如下内容,在compilation-shell-minor-mode模式下,笔记前面的内容将呈现出一个链接,可以点击直接打开main.cpp
1
|
/home/iamxuxiao/main.cpp:5: miss spelling "word"
|
在我的.emacs中,我把这个函数和C-c、r做了绑定
自动给C代码头文件的首位添加ifndef和endif
get-include-guard函数在我们要编辑一个新头文件时,自动给文件添加上预处理指示符:ifndef和endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
(defun get-include-guard ()
"Return a string suitable for use in a C/C++ include guard"
(let* ((fname (buffer-file-name (current-buffer)))
(fbasename (replace-regexp-in-string".*/"""fname))
(inc-guard-base (replace-regexp-in-string"[.-]"
"_"
fbasename)))
(concat (upcase inc-guard-base)"_")))
(add-hook 'find-file-not-found-hooks
'(lambda ()
(let ((file-name (buffer-file-name (current-buffer))))
(when (string=".h"(substring file-name -2))
(let ((include-guard (get-include-guard)))
(insert"#ifndef "include-guard)
(newline)
(insert"#define "include-guard)
(newline 4)
(insert"#endif")
(newline)
(previous-line 3)
(set-buffer-modified-p nil))))))
|
如果我们在emacs中要新建一个文件foo.h(C-x,C-f foo.h),emacs新创建的foo.h缓冲区中看上去将是这样的
1
2
3
4
|
#ifndef FOO_H_
#define FOO_H_
#endif
|
在foo.cpp和foo.h之间自动的切换
如果一个文件夹中同时含有foo.h和foo.cpp两个文件的话,下面的函数帮助你在这两个文件之间切换
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
|
(defun next-file-with-basename ()
"Cycles between files with the same basename as the given file.
Usefullforcycling between header .h/.cpp/.hpp files etc."
(interactive)
(let* ((buf-file-name (replace-regexp-in-string
"^.*/"""
(buffer-file-name)))
(current-dir (replace-regexp-in-string
"[a-zA-Z0-9._-]+$"""
(buffer-file-name)))
(no-basename (equal ?. (aref buf-file-name 0)))
(has-extension (find ?. buf-file-name)))
;; If the file is a .dot-file or it doesn't have an
;; extension, then there's nothing todohere.
(unless (or no-basename (not has-extension))
(let* ((basename (replace-regexp-in-string
"\\..*"""
buf-file-name))
(files-with-basename (directory-files
current-dir f
(concat"^"basename"\\."))))
;; If there's only 1 file withthisbasename, nothing to
;;do
(unless (= (length files-with-basename) 1)
;; By making the list circular, we're guaranteed that
;; there will always be a next list element (ie. no
;; needforspecialcasewhen file is at the end of
;; the list).
(setf (cdr (last files-with-basename))
files-with-basename)
(find-file (cadr (member (buffer-file-name)
files-with-basename))))))))
|
在我的.emacs中,我把这个函数和C-c,n做了绑定
注:Reddit网友提出ff-find-other-file实现了非常类似的功能
c-macro模板
我们在写C++代码的时候,经常要键入一些重复的操作,比如历遍容器,try catch等等。而这些代码的特点,可以归结成一个不变的模板+几个变化参数,下面的emacs函数自动帮你扩展这个模板,打印代码。
我们先描述该函数的效果,在C++代码中插入如下待扩展的句子
1
|
(doit std::vector myContainer)
|
然后在该行的末尾执行我们的函数,该行被自动替换成如下的C++代码
1
2
3
4
5
6
|
for(std::vector::iterator it = myContainer.begin();
it != myContainer.end();
++it)
{
// 光标将停在这里 等待具体的编辑
}
|
该c-macro还可以接受变长参数,比如下面的模板接受两个参数
1
|
(doit std::vector myIt myContainer)
|
生成的代码如下:
1
2
3
4
5
6
|
for(std::vector::iterator myIt = myContainer.begin();
myIt != myContainer.end();
++myIt)
{
// 光标将停在这里 等待具体的编辑
}
|
下面的macro将帮助用户自己打印try catch block
1
|
(api-fn)
|
扩展之后将变成
1
2
3
4
5
6
7
8
9
10
|
try
{
// 光标将停在这里 等待具体的编辑
}
catch(conststd::exception& e)
{
TRACE("Unhandled exception in function %s: %s\n",
__func__, e.what());
return-1;
}
|
下面的j-newline-and-indent是以上功能的入口函数,其将寻找光标前是否出现已定义的c-macro.在上面的例子中就是doit和api-fn。
如果出现了macro就做扩展,如果没有出现,j-newline-and-indent等于内置的newline-and-indent函数:加入新行,并且indent
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
|
(defun j-newline-and-indent ()
"Same as \"newline-and-indent\" except it also expands
c-macrosifit sees one."
(interactive)
(if(and (equal (char-before) ?\))
(macro-function (car (preceding-sexp))))
;; This is a c-macro
(expand-c-macro-in-place)
(newline-and-indent)))
(defun macro-function (name)
"Given a name, returns the c-macro-name symbolifit
exists as a function"
(let ((macro-sym (intern (concat"c-macro-"
(symbol-name name)))))
(if(fboundp macro-sym)
macro-sym
nil)))
(defun expand-c-macro-in-place ()
"Given that point is at the end of a c-macro, expands
it in-place"
(let* ((sexp (preceding-sexp))
(macro-name (car sexp))
(replacement-text (apply (macro-function macro-name)
(cdr sexp)))
(jump-to (string-match"!!!BODY!!!;"replacement-text)))
;; Delete macro invocation
(backward-list)
(let ((start-del (point)))
(forward-list)
(kill-region start-del (point))
;; Insert macro expansion and indent appropriately
(insert replacement-text)
(indent-region start-del (point))
(when jump-to
(search-backward"!!!BODY!!!;")
(kill-line))))
(c-indent-command))
|
下面是自定义的两个模板c-macro,读者可以根据需要定义自己的macro
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
|
(defun c-macro-doit (container-type arg1 &optional arg2)
"Emits code for iterating over an stl (or stl-like) structure"
(let ((iterator-name (ifarg2 arg1"it"))
(container-name (ifarg2 arg2 arg1)))
(format (concat"for (%s::iterator %s = %s.begin();\n"
" %s != %s.end();\n"
" ++%s)\n"
"{\n"
" !!!BODY!!!;\n"
"}\n")
container-type
iterator-name
container-name
iterator-name
container-name
iterator-name)))
(defun c-macro-api-fn ()
"Emits code for wrapping an api function in a try/catch block"
(concat"try\n"
"{\n"
" !!!BODY!!!;\n"
"}\n"
"catch(const std::exception& e)\n"
"{\n"
" TRACE(\"Unhandled exception in function %s: %s\\n\",\n"
" __func__, e.what());\n"
" return -1;\n"
"}\n"))
|
原文链接: Justin Davis 翻译: 伯乐在线 - 伯乐在线读者
译文链接: http://blog.jobbole.com/47027/