经验总结:实用的Emacs配置文件搜罗

开发 项目管理
以下的Emacs配置文件是我多年积累起来的,它们在我历次整理配置文件(.emacs)的过程中幸存了下来,经过了时间考验,所以现在我决定发出 来和大家分享一下。虽然某些功能很有可能已经有更好的实现方法了,但是这些例子对读者学习emacs lisp还是会有帮助的。

以下的Emacs配置文件是我多年积累起来的,它们在我历次整理配置文件(.emacs)的过程中幸存了下来,经过了时间考验,所以现在我决定发出 来和大家分享一下。虽然某些功能很有可能已经有更好的实现方法了,但是这些例子对读者学习emacs lisp还是会有帮助的。

在一些文本的末尾添加递增的数字

inc-num-region把一段文本中重复出现的数字替换成递增的数字

  1. (defun inc-num-region (p m) 
  2.   "Increments the numbers in a given region" 
  3.   (interactive "r") 
  4.   (save-restriction 
  5.     (save-excursion 
  6.       (narrow-to-region p m)    
  7.       (goto-char (point-min))   
  8.       (forward-line) 
  9.       (let ((counter 1)) 
  10.         (while (not (eq (point) 
  11.                         (point-max))) 
  12.           (goto-char (point-at-eol)) 
  13.           (search-backward-regexp "[0-9]+" (point-at-bol) t) 
  14.           (let* ((this-num (string-to-number (match-string 0))) 
  15.                  (new-num-str (number-to-string (+ this-num 
  16.                                                    counter)))) 
  17.             (replace-match new-num-str) 
  18.             (incf counter) 
  19.             (forward-line))))))) 

比如在emacs选中如下的文本区域

  1. 1foo 
  2. 1foo 
  3. 1foo 
  4. 1foo 

执行该函数,那么上述文本在缓冲区中变成

  1. 1foo 
  2. 2foo 
  3. 3foo 
  4. 4foo 

再比如选中如下的文本区域

  1. foo3 
  2.  foo3 
  3.  foo3 
  4.  foo3 

执行给函数,得到

  1. foo3 
  2. foo4 
  3. foo5 
  4. foo6 

给代码做笔记

在我们公司使用reviewboard之前,代码审查都是面对面进行的。我曾经使用下面这个函数来帮助记录意见所对应的源文件和行号。

  1. defun add-code-review-note () 
  2.    "Add note for current file and line number" 
  3.    (interactive) 
  4.    (let ((file-name (buffer-file-name)) 
  5.          (file-line (line-number-at-pos))) 
  6.      (switch-to-buffer-other-window (get-buffer-create "NOTES")) 
  7.      (goto-char (point-min)) 
  8.      (when (not (search-forward "-*- mode:compilation-shell-minor" 
  9.                                 nil t)) 
  10.        (compilation-shell-minor-mode 1) 
  11.        (insert "-*- mode:compilation-shell-minor -*-\n\n")) 
  12.      (goto-char (point-max)) 
  13.      (if (/= (current-column) 0) 
  14.          (newline)) 
  15.      (insert file-name ":" (number-to-string file-line) ": "))) 

使用方法是,光标停在源代码的需要做批注的位置,然后执行该函数,emacs会创建一个新的叫做NOTES的缓冲区,其中记录源代码的路径和光标所在的行 号,用户在接下来的区域中输入笔记。这个函数的好处是,该新建的buffer的工作模式是compilation-shell-minor-mode。所 以可以直接点击其路径和行号,就可以直接打源文件跳到相应的行上去。比如

  1. #include 
  2.   
  3.  int main() 
  4.  { 
  5.    std::cout << "Hello Word!" << std::endl;  //光标停在这里 
  6.    return 0; 
  7.  } 

执行该函数,在新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. defun get-include-guard () 
  2.    "Return a string suitable for use in a C/C++ include guard" 
  3.    (let* ((fname (buffer-file-name (current-buffer))) 
  4.           (fbasename (replace-regexp-in-string ".*/" "" fname)) 
  5.           (inc-guard-base (replace-regexp-in-string "[.-]" 
  6.                                                     "_" 
  7.                                                     fbasename))) 
  8.      (concat (upcase inc-guard-base) "_"))) 
  9.   
  10.  (add-hook 'find-file-not-found-hooks 
  11.            '(lambda () 
  12.               (let ((file-name (buffer-file-name (current-buffer)))) 
  13.                 (when (string".h" (substring file-name -2)) 
  14.                   (let ((include-guard (get-include-guard))) 
  15.                     (insert "#ifndef " include-guard) 
  16.                     (newline) 
  17.                     (insert "#define " include-guard) 
  18.                     (newline 4) 
  19.                     (insert "#endif") 
  20.                     (newline) 
  21.                     (previous-line 3) 
  22.                     (set-buffer-modified-p nil)))))) 

如果我们在emacs中要新建一个文件foo.h(C-x,C-f foo.h),emacs新创建的foo.h缓冲区中看上去将是这样的

  1. #ifndef FOO_H_ 
  2.  #define FOO_H_ 
  3.   
  4.  #endif 

在foo.cpp和foo.h之间自动的切换

如果一个文件夹中同时含有foo.h和foo.cpp两个文件的话,下面的函数帮助你在这两个文件之间切换

  1. (defun next-file-with-basename () 
  2.  "Cycles between files with the same basename as the given file. 
  3.   Usefull for cycling between header .h/.cpp/.hpp files etc." 
  4.  (interactive) 
  5.  (let* ((buf-file-name (replace-regexp-in-string 
  6.                         "^.*/" "" 
  7.                         (buffer-file-name))) 
  8.         (current-dir (replace-regexp-in-string 
  9.                       "[a-zA-Z0-9._-]+$" "" 
  10.                       (buffer-file-name))) 
  11.         (no-basename (equal ?. (aref buf-file-name 0))) 
  12.         (has-extension (find ?. buf-file-name))) 
  13.    ;; If the file is a .dot-file or it doesn't have an 
  14.    ;; extension, then there's nothing to do here. 
  15.    (unless (or no-basename (not has-extension)) 
  16.      (let* ((basename (replace-regexp-in-string 
  17.                        "\\..*" "" 
  18.                        buf-file-name)) 
  19.             (files-with-basename (directory-files 
  20.                                   current-dir f 
  21.                                   (concat "^" basename "\\.")))) 
  22.        ;; If there's only 1 file with this basename, nothing to 
  23.        ;; do 
  24.        (unless (= (length files-with-basename) 1) 
  25.          ;; By making the list circular, we're guaranteed that 
  26.          ;; there will always be a next list element (ie. no 
  27.          ;; need for special case when file is at the end of 
  28.          ;; the list). 
  29.          (setf (cdr (last files-with-basename)) 
  30.                files-with-basename) 
  31.          (find-file (cadr (member (buffer-file-name) 
  32.                                   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. for (std::vector::iterator it = myContainer.begin(); 
  2.      it != myContainer.end(); 
  3.      ++it) 
  4.    // 光标将停在这里 等待具体的编辑  

该c-macro还可以接受变长参数,比如下面的模板接受两个参数

  1. (doit std::vector myIt myContainer) 

生成的代码如下:

  1. for (std::vector::iterator myIt = myContainer.begin(); 
  2.       myIt != myContainer.end(); 
  3.       ++myIt) 
  4.  { 
  5.     // 光标将停在这里 等待具体的编辑 
  6.  } 

下面的macro将帮助用户自己打印try catch block

  1. (api-fn) 

扩展之后将变成

  1. try 
  2.     // 光标将停在这里 等待具体的编辑 
  3. catch(const std::exception& e) 
  4.    TRACE("Unhandled exception in function %s: %s\n", 
  5.          __func__, e.what()); 
  6.    return -1; 

下面的j-newline-and-indent是以上功能的入口函数,其将寻找光标前是否出现已定义的c-macro.在上面的例子中就是doit和api-fn。
如果出现了macro就做扩展,如果没有出现,j-newline-and-indent等于内置的newline-and-indent函数:加入新行,并且indent

  1. (defun j-newline-and-indent () 
  2.    "Same as \"newline-and-indent\" except it also expands 
  3.     c-macros if it sees one." 
  4.    (interactive) 
  5.    (if (and (equal (char-before) ?\)) 
  6.             (macro-function (car (preceding-sexp)))) 
  7.        ;; This is a c-macro 
  8.        (expand-c-macro-in-place) 
  9.      (newline-and-indent))) 
  10.   
  11.  (defun macro-function (name) 
  12.    "Given a name, returns the c-macro-name symbol if it 
  13.     exists as a function" 
  14.    (let ((macro-sym (intern (concat "c-macro-" 
  15.                                     (symbol-name name))))) 
  16.      (if (fboundp macro-sym) 
  17.          macro-sym 
  18.        nil))) 
  19.   
  20.  (defun expand-c-macro-in-place () 
  21.    "Given that point is at the end of a c-macro, expands 
  22.     it in-place" 
  23.    (let* ((sexp (preceding-sexp)) 
  24.           (macro-name (car sexp)) 
  25.           (replacement-text (apply (macro-function macro-name) 
  26.                                    (cdr sexp))) 
  27.           (jump-to (string-match "!!!BODY!!!;" replacement-text))) 
  28.      ;; Delete macro invocation 
  29.      (backward-list) 
  30.      (let ((start-del (point))) 
  31.        (forward-list) 
  32.        (kill-region start-del (point)) 
  33.   
  34.       ;; Insert macro expansion and indent appropriately 
  35.       (insert replacement-text) 
  36.       (indent-region start-del (point)) 
  37.       (when jump-to 
  38.         (search-backward "!!!BODY!!!;") 
  39.         (kill-line)))) 
  40.   (c-indent-command)) 
  41.  
  42. 下面是自定义的两个模板c-macro,读者可以根据需要定义自己的macro 
  43. 10 
  44. 11 
  45. 12 
  46. 13 
  47. 14 
  48. 15 
  49. 16 
  50. 17 
  51. 18 
  52. 19 
  53. 20 
  54. 21 
  55. 22 
  56. 23 
  57. 24 
  58. 25 
  59. 26 
  60. 27 
  61. 28 
  62. 29 
  63.      
  64. (defun c-macro-doit (container-type arg1 &optional arg2) 
  65.    "Emits code for iterating over an stl (or stl-like) structure" 
  66.    (let ((iterator-name  (if arg2 arg1 "it")) 
  67.          (container-name (if arg2 arg2 arg1))) 
  68.      (format (concat "for (%s::iterator %s = %s.begin();\n" 
  69.                      "     %s != %s.end();\n" 
  70.                      "     ++%s)\n" 
  71.                      "{\n" 
  72.                      "   !!!BODY!!!;\n" 
  73.                      "}\n") 
  74.              container-type 
  75.              iterator-name 
  76.              container-name 
  77.              iterator-name 
  78.              container-name 
  79.              iterator-name))) 
  80.   
  81.  (defun c-macro-api-fn () 
  82.    "Emits code for wrapping an api function in a try/catch block" 
  83.    (concat "try\n" 
  84.            "{\n" 
  85.            "   !!!BODY!!!;\n" 
  86.            "}\n" 
  87.            "catch(const std::exception& e)\n" 
  88.            "{\n" 
  89.            "   TRACE(\"Unhandled exception in function %s: %s\\n\",\n" 
  90.            "         __func__, e.what());\n" 
  91.            "   return -1;\n" 
  92.            "}\n")) 

原文链接:http://blog.jobbole.com/47027/

责任编辑:陈四芳 来源: 伯乐在线
相关推荐

2009-10-15 09:27:00

2010-07-13 16:07:18

Perl

2010-01-19 18:52:08

VB.NET处理数据行

2009-08-17 14:45:18

VMware虚拟机实用

2009-11-16 10:57:51

PHP上传文件代码

2011-07-21 13:40:17

java

2009-09-16 17:13:54

学习Linq

2009-08-19 09:24:43

AJAX引擎经验总结

2009-09-29 16:32:11

OJB Hiberna

2010-03-25 13:42:14

云计算

2010-03-23 11:39:49

云计算

2009-12-15 17:10:26

路由器配置

2010-05-19 17:24:55

MySQL编码

2010-04-21 14:53:46

Oracle游标

2010-04-28 10:45:24

Oracle10g

2010-01-18 16:49:36

VB.NET基本数据类

2009-11-17 11:24:00

PHP应用技巧

2009-09-11 13:29:31

LINQ查询操作

2009-08-20 17:35:47

Servlet和JSP

2009-09-16 17:44:54

LINQ to SQL
点赞
收藏

51CTO技术栈公众号