一些实用的Emacs配置文件

以下的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/

转载于:https://my.oschina.net/Rayn/blog/159027

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值