Tags: emacs

处理Emacs中的行号和空白字符

做过开源项目的童鞋可能会知道,在很多开源项目中会有专门的脚本来检查代码风格。其中包括行尾是否有多余的空白字符,是否使用使用tab,每一行是否超过了指定的宽度等等。这些问题通常在敲代码的时候并不容易看出来,等到要提交代码的时候才发现问题。稍微搜索了一下发现在emacs中有一个很有用的whitespace插件可以解决这个问题。

whitespace可以针对设置的规则对上面提到的问题进行高亮显示,效果如下:

WhiteSpaceOnLight

whitespace具体可以找到的问题包括tab, 空格混用;行尾多余的空白字符;文件结束前的空白字符;超过指定长度的行等等。可以通过设置whitespace-style来指定需要提示哪些问题。在我的配置中使用了如下配置:

1
2
3
4
5
6
7
;; deal with white spaces
(require 'whitespace)
(global-whitespace-mode)
(setq whitespace-style
      '(face trailing tabs lines lines-tail empty
        space-after-tab space-before-tab))
(add-hook 'before-save-hook 'delete-trailing-whitespace)

默认的whitespace-style把整个buffer被搞得有点乱糟糟的,因此我把不是很需要的部分都去掉了,只剩下检测行尾空白、tab等少数几个问题。对于行尾的多余字符而言,一个个删除也是很麻烦的事情。所以我直接用delete-trailing-whitespace在保存的时候把它们都干掉了。

不过在这里whitespace会和行号有一点点冲突。通常为了显示行号都会打开linum-mode,行号会是右边对其的数字。whitespace会认为行号前面的空白字符是问题字符而高亮显示,搞得非常难看。为了解决这个问题可以用下面代码对linum进行设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
;; Show line number
(require 'linum)
(global-linum-mode t)
(setq column-number-mode t)
(setq line-number-node t)
(setq linum-format "%5d ")
(add-hook 'linum-before-numbering-hook
          (lambda ()
            (let ((w (+
                      (length
                       (number-to-string
                        (count-lines (point-min) (point-max))
                        ))
                      2)
                     ))
              (setq linum-format
                    `(lambda (line)
                       (propertize (concat
                                    (truncate-string-to-width
                                     "" (- ,w (length (number-to-string line)))
                                     nil ?x2007)
                                    (number-to-string line))
                                   'face 'linum))))))

这是从emacs wiki上面抄过来的一段代码,用了一个特殊的unicode字符x2007来设置行号的格式,既能达到右端对其的效果又不会被whitespace误杀。

Emacs中处理日志文件

最近写的东西会将信息输出到一个日志文件中去,每次调试的时候log文件内容都会刷新。每次都手动revert-buffer比较烦人,查了一下发现有一个auto-revert-mode可以自动刷新。下面的配置可以实现对于.log文件调用auto-revert-mode:

1
2
3
4
5
6
7
8
9
10
11
;; auto revert log files
(add-hook 'find-file-hook
          (lambda ()
            (if (string-equal
                 ".log"
                 (substring (buffer-file-name)
                            (search "." (buffer-file-name))))
                (progn
                  (setq auto-revert-mode t)
                  (message "Enabling auto-revert-mode for log file")))
            ))

如果是比较长的日志文件,并且日志使用追加内容的方式添加上去的话,可以将上面的(setq auto-revert-mode t)换成(setq auto-revert-tail-mode t)以提高效率。

配置Emacs下的Python开发环境

特性

在Linux论坛上总有人问Python用什么IDE比较好,然后总会有人回答说Emacs。最近开始学Python,也花了点时间研究怎么配置Emacs,发现没有想象中的那么麻烦。这篇文章大致上来自于Lei Chen博客文章的翻译,完成以后的Emacs具有以下特性:

  • 自动完成同一个文件内的变量、函数
  • 自动完成python库中的名称
  • 代码重构
  • 模板展开功能
  • 在线帮助系统
  • 即时语法检测

其他特性还包括自动缩进,括号匹配,语法高亮,代码折叠等等。其中最有用的莫过于自动完成了,貌似很少有python编辑器可以做到这一点。而即时语法检测让emacs下的python代码书写变得像Eclipse一样,一旦有错误立刻就会高亮标记出来。

如何安装

首先你得在home目录下有一个.emacs配置文件,并且有一个用来放插件的文件夹(比如说~/.emacs.d/)

  1. 下载auto-completion.el到.emacs.d,并且在.emacs中添加如下几行:
    (require ‘auto-complete)
    (global-auto-complete-mode t)
    
  2. 下载yasnippet到.emacs.d并且编辑.emacs:
    (require ‘yasnippet) (yas/initialize) (yas/load-directory “~/.emacs.d/snippets”)
  3. 下载python-mode.el并且放到.emacs.d中。我们会在之后的配置中用到它
  4. 设置Rope, Ropemacs 和 Pymacs

    我们需要使用最新的development版的rope和ropemacs,否则在emacs中不能找到rope-completion函数。通过如下步骤安装:

    sudo apt-get install mercurial
    mkdir /tmp/rope && cd /tmp/rope
    hg clone http://bitbucket.org/agr/rope
    hg clone http://bitbucket.org/agr/ropemacs
    hg clone http://bitbucket.org/agr/ropemode
    sudo easy_install rope
    ln -s ../ropemode/ropemode ropemacs/
    sudo easy_install ropemacs
  5. 安装pyflacks以进行自动语法检查:

    sudo apt-get install pyflakes
  6. 把所有东西放到一块儿
  7. 在你的.emacs.d中创建一个init_python.el,并且加入以下内容:

    (autoload 'python-mode "python-mode" "Python Mode." t)
    (add-to-list 'auto-mode-alist '(".py'" . python-mode))
    (add-to-list 'interpreter-mode-alist '("python" . python-mode))
    (require 'python-mode)
    (add-hook 'python-mode-hook
          (lambda ()
    	(set-variable 'py-indent-offset 4)
    	;(set-variable 'py-smart-indentation nil)
    	(set-variable 'indent-tabs-mode nil)
    	(define-key py-mode-map (kbd "RET") 'newline-and-indent)
    	;(define-key py-mode-map [tab] 'yas/expand)
    	;(setq yas/after-exit-snippet-hook 'indent-according-to-mode)
    	(smart-operator-mode-on)
    	))
    ;; pymacs
    (autoload 'pymacs-apply "pymacs")
    (autoload 'pymacs-call "pymacs")
    (autoload 'pymacs-eval "pymacs" nil t)
    (autoload 'pymacs-exec "pymacs" nil t)
    (autoload 'pymacs-load "pymacs" nil t)
    ;;(eval-after-load "pymacs"
    ;;  '(add-to-list 'pymacs-load-path YOUR-PYMACS-DIRECTORY"))
    (pymacs-load "ropemacs" "rope-")
    (setq ropemacs-enable-autoimport t)
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;; Auto-completion
    ;;;  Integrates:
    ;;;   1) Rope
    ;;;   2) Yasnippet
    ;;;   all with AutoComplete.el
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    (defun prefix-list-elements (list prefix)
      (let (value)
        (nreverse
         (dolist (element list value)
          (setq value (cons (format "%s%s" prefix element) value))))))
    (defvar ac-source-rope
      '((candidates
         . (lambda ()
             (prefix-list-elements (rope-completions) ac-target))))
      "Source for Rope")
    (defun ac-python-find ()
      "Python `ac-find-function'."
      (require 'thingatpt)
      (let ((symbol (car-safe (bounds-of-thing-at-point 'symbol))))
        (if (null symbol)
            (if (string= "." (buffer-substring (- (point) 1) (point)))
                (point)
              nil)
          symbol)))
    (defun ac-python-candidate ()
      "Python `ac-candidates-function'"
      (let (candidates)
        (dolist (source ac-sources)
          (if (symbolp source)
              (setq source (symbol-value source)))
          (let* ((ac-limit (or (cdr-safe (assq 'limit source)) ac-limit))
                 (requires (cdr-safe (assq 'requires source)))
                 cand)
            (if (or (null requires)
                    (>= (length ac-target) requires))
                (setq cand
                      (delq nil
                            (mapcar (lambda (candidate)
                                      (propertize candidate 'source source))
                                    (funcall (cdr (assq 'candidates source)))))))
            (if (and (> ac-limit 1)
                     (> (length cand) ac-limit))
                (setcdr (nthcdr (1- ac-limit) cand) nil))
            (setq candidates (append candidates cand))))
        (delete-dups candidates)))
    (add-hook 'python-mode-hook
              (lambda ()
                     (auto-complete-mode 1)
                     (set (make-local-variable 'ac-sources)
                          (append ac-sources '(ac-source-rope) '(ac-source-yasnippet)))
                     (set (make-local-variable 'ac-find-function) 'ac-python-find)
                     (set (make-local-variable 'ac-candidate-function) 'ac-python-candidate)
                     (set (make-local-variable 'ac-auto-start) nil)))
    ;;Ryan's python specific tab completion
    (defun ryan-python-tab ()
      ; Try the following:
      ; 1) Do a yasnippet expansion
      ; 2) Do a Rope code completion
      ; 3) Do an indent
      (interactive)
      (if (eql (ac-start) 0)
          (indent-for-tab-command)))
    (defadvice ac-start (before advice-turn-on-auto-start activate)
      (set (make-local-variable 'ac-auto-start) t))
    (defadvice ac-cleanup (after advice-turn-off-auto-start activate)
      (set (make-local-variable 'ac-auto-start) nil))
    (define-key py-mode-map "t" 'ryan-python-tab)
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;; End Auto Completion
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Auto Syntax Error Hightlight
    (when (load "flymake" t)
      (defun flymake-pyflakes-init ()
        (let* ((temp-file (flymake-init-create-temp-buffer-copy
    		       'flymake-create-temp-inplace))
    	   (local-file (file-relative-name
    			temp-file
    			(file-name-directory buffer-file-name))))
          (list "pyflakes" (list local-file))))
      (add-to-list 'flymake-allowed-file-name-masks
    	       '(".py'" flymake-pyflakes-init)))
    (add-hook 'find-file-hook 'flymake-find-file-hook)
    (provide 'init_python)
    
  8. 在你的.emacs文件中添加:(load-library “init_python”)

使用说明

  1. 代码自动展开。比如说在编辑C/C++头文件的时候键入once然后按tab,则会自动展开#ifndef __FILE_H__这样的宏。这个功能是YASnippet提供的,在emacs的菜单中可以看到它的所有宏。展开以后再按tab键可以在已展开的代码之间切换位置。
  2. 自动完成代码。函数名输入到一半按tab键会调用Rope和Ropemacs的功能自动完成库文件中的函数、变量名
  3. C-c d 显示python的doc string
  4. C-c C-c 运行当前的文件
  5. C-c ! 打开python shell

事实上init_python.el做的事情是通过使用auto_complete.el中的功能,把YASnippet和Rope的功能结合到了一块儿,实现了类似TexMate的自动完成等功能。其中YASnippet可以自定义模板,很是实用。Rope的功能还在研究中。