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

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

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

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

(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-match new-num-str) 


            (incf counter) 


            (forward-line))))))) 

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

1foo 


1foo 


1foo 


1foo 

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

1foo 


2foo 


3foo 


4foo 

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

foo3 


 foo3 


 foo3 


 foo3 

执行给函数,得到

foo3 


foo4 


foo5 


foo6 

给代码做笔记

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

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。所 以可以直接点击其路径和行号,就可以直接打源文件跳到相应的行上去。比如

#include 


  


 int main() 


 { 


   std::cout << "Hello Word!" << std::endl;  //光标停在这里 


   return 0; 


 } 

执行该函数,在新buffer中得到如下内容,在compilation-shell-minor-mode模式下,笔记前面的内容将呈现出一个链接,可以点击直接打开main.cpp

/home/iamxuxiao/main.cpp:5: miss spelling "word" 

在我的.emacs中,我把这个函数和C-c、r做了绑定

自动给C代码头文件的首位添加ifndef和endif

get-include-guard函数在我们要编辑一个新头文件时,自动给文件添加上预处理指示符:ifndef和endif

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缓冲区中看上去将是这样的

#ifndef FOO_H_ 


 #define FOO_H_ 


  


 #endif 

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

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

(defun next-file-with-basename () 


 "Cycles between files with the same basename as the given file. 


  Usefull for cycling 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 to do here. 


   (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 with this basename, 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 


         ;; need for special case when 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++代码中插入如下待扩展的句子

(doit std::vector myContainer) 

然后在该行的末尾执行我们的函数,该行被自动替换成如下的C++代码

for (std::vector::iterator it = myContainer.begin(); 


     it != myContainer.end(); 


     ++it) 


{ 


   // 光标将停在这里 等待具体的编辑  


} 

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

(doit std::vector myIt myContainer) 

生成的代码如下:

for (std::vector::iterator myIt = myContainer.begin(); 


      myIt != myContainer.end(); 


      ++myIt) 


 { 


    // 光标将停在这里 等待具体的编辑 


 } 

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

(api-fn) 

扩展之后将变成

try 


{ 


    // 光标将停在这里 等待具体的编辑 


} 


catch(const std::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

相关推荐