YAMAGUCHI::weblog

海水パンツとゴーグルで、巨万の富を築きました。カリブの怪物、フリーアルバイター瞳です。

Python用にauto-complete.elを設定する

動機

開発にはずーっとEmacsを使っているけれど、最近M-/の補完を不便に感じるようになったので、補完窓が出るような拡張を入れてみたくなった。

方法

下記サイトの方法をそのまま実行しただけです。

まずMercurialをインストールして、rope, ropemacs, ropemodeをDLし、ropeとropemacsをインストール。

$ sudo ports install mercurial
---> Installing bzip2 @1.0.5_1
---> Installing py25-bz2 @2.5.4_0
---> Installing mercurial @1.1.2_0
$ mkdir ~/pkg/rope && cd ~/pkg/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
$ mv ./ropemode/ropemode ./ropemacs
$ sudo easy_install ropemacs

次にPymacsとyasnippetをインストール。

$ mkdir -p ~/.emacs.d/vendor && cd ~/.emacs.d/vendor
$ wget http://pymacs.progiciels-bpi.ca/archives/Pymacs-0.23.tar.gz
$ tar xzf Pymacs-0.23.tar.gz
$ cd Pymacs-0.23
$ make
$ sudo easy_install .
$ cd ..
$ wget http://yasnippet.googlecode.com/files/yasnippet-0.5.9.tar.bz2
$ bzip2 -dc yasnippet-0.5.9.tar.bz2 | tar x
$ cd ~/.emacs.d
$ ln -s vendor/yasnippet-0.5.9/snippets snippets

AutoComplete.elを導入。

$ cd ~/.emacs.d/
$ wget http://www.emacswiki.org/emacs/download/auto-complete.el

.emacsに追加。

(require 'python)
(require 'auto-complete)
(require 'yasnippet)
 
(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))
 
;; Initialize 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)
;; Initialize Rope                                                                                             
(pymacs-load "ropemacs" "rope-")
(setq ropemacs-enable-autoimport t)
 
;; Initialize Yasnippet                                                                                        
;Don't map TAB to yasnippet                                                                                    
;In fact, set it to something we'll never use because                                                          
;we'll only ever trigger it indirectly.                                                                        
(setq yas/trigger-key (kbd "C-c <kp-multiply>"))
(yas/initialize)
(yas/load-directory "~/.emacs.d/snippets")
 
 
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;                                         
;;; 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 python-mode-map "\t" 'ryan-python-tab)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;                                         
;;; End Auto Completion                                                                                        
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

するとこんな感じになります。
f:id:ymotongpoo:20090213082048p:image

導入したのはいいものの、個人的に不満なのは

  • 呼び出している変数に対応したメソッドの補完ができない
  • Emacsキーバインドで候補を選択できない(矢印のみ、C-n,C-pで選択したい)

らへんかなあ。まあ後者に関しては上のLispをちょっと書き換えればいいんだろうけど。