86f8d114af1731090b804562b6c626b809723da4
[emacs-init.git] / python / init_python.el
1 ;(autoload 'python-mode "python-mode" "Python Mode." t)
2 ;(add-to-list 'auto-mode-alist '("\\.py\\'" . python-mode))
3 ;(add-to-list 'interpreter-mode-alist '("python" . python-mode))
4 ;(require 'python-mode)
5 ;(provide 'python)
6 ;(provide 'python21)
7
8 ;; pymacs
9 (autoload 'pymacs-apply "pymacs")
10 (autoload 'pymacs-call "pymacs")
11 (autoload 'pymacs-eval "pymacs" nil t)
12 (autoload 'pymacs-exec "pymacs" nil t)
13 (autoload 'pymacs-load "pymacs" nil t)
14 ;;(eval-after-load "pymacs"
15 ;;  '(add-to-list 'pymacs-load-path YOUR-PYMACS-DIRECTORY"))
16 (pymacs-load "ropemacs" "rope-")
17 (setq ropemacs-enable-autoimport t)
18
19 ;; M-/               rope-code-assist
20 ;; C-c g             rope-goto-definition
21 ;; C-c d             rope-show-doc
22 ;; C-c f             rope-find-occurrences
23 ;; M-?               rope-lucky-assist
24
25 (define-key ropemacs-local-keymap "\M-/" 'hippie-expand)
26
27 (defun write-file-py-cleanup-imports ()
28   (save-excursion
29     (condition-case nil
30         (py-cleanup-imports t)
31       (error . nil)))
32   nil)
33
34 (defun python-init-auto-cleanup-imports-on-save ()
35   (add-hook 'write-file-functions 'write-file-py-cleanup-imports nil t))
36
37 (defun my-flymake-error-at-point ()
38   (condition-case nil
39       (flymake-ler-text (car (nth 0 (flymake-find-err-info flymake-err-info
40                                                            (flymake-current-line-no)))))
41     (error (error "no flymake error at point"))))
42
43 (defun my-flymake-show-error ()
44   (interactive)
45   (message (my-flymake-error-at-point)))
46
47 (defun my-pyflymake-add-import-from-error ()
48   (interactive)
49   (let ((err (my-flymake-error-at-point)))
50     (if (string-match "undefined name '\\(.*\\)'" err)
51         (py-add-import (match-string 1 err))
52       (error "no missing symbol at point"))))
53
54 (defun py-add-import (import)
55   (interactive "sModule to import: ")
56   (save-window-excursion
57     (save-excursion
58       (goto-char (car (py-imports-region)))
59       (insert "import " import "\n")
60       (py-cleanup-imports t)
61       (sit-for 2))))
62
63 (defun my-flymake-check-and-wait ()
64   (if (boundp flymake-is-running)
65       (progn
66         (while flymake-is-running (sit-for .1))
67         (flymake-start-syntax-check)
68         (while flymake-is-running (sit-for .1)))))
69
70 (defun my-flymake-goto-next-error ()
71   (interactive)
72   (my-flymake-check-and-wait)
73   (flymake-goto-next-error)
74   (my-flymake-show-error))
75
76 (defun py-find-file (errormark filename defaultdir)
77   (let ((fullname (expand-file-name filename defaultdir)))
78     (or (and (not (file-exists-p fullname))
79              (let* ((name (loop for name = fullname then (directory-file-name
80                                                           (file-name-directory name))
81                                 if (file-exists-p name) return name))
82                     (fmt (and name (with-temp-buffer
83                                      (insert-file-contents name nil 0 1024 t) (archive-find-type))))
84                     (tail (and fmt (substring fullname (1+ (length name))))))
85                (if fmt
86                    (with-current-buffer (find-file-noselect name)
87                      (goto-char (point-min))
88                      (re-search-forward (concat " " (regexp-quote tail) "$"))
89                      (save-window-excursion
90                        (archive-extract)
91                        (current-buffer))))))
92         (compilation-find-file errormark filename defaultdir))))
93
94 (require 'arc-mode)
95
96 (defun py-eshell-goto-error (&optional back)
97   (interactive "P")
98   (display-buffer "*eshell*" t)
99   (let (here end start dir file line errmk example)
100     (with-current-buffer "*eshell*"
101       (save-excursion
102         ;; Find and validate last traceback
103         (goto-char (point-max))
104         (re-search-backward "^\\(.*\\)Traceback \\|^Failed example:")
105         (beginning-of-line)
106         (if (looking-at "Failed example:")
107             (progn
108               (forward-line -2)
109               (setq example t)))
110         (if (or (not (and (boundp 'py-eshell-last-error)
111                           (boundp 'py-eshell-last-point)))
112                 (null py-eshell-last-error)
113                 (null py-eshell-last-point)
114                 (null py-eshell-last-dir)
115                 (not (= py-eshell-last-error (point))))
116             (progn
117               (set (make-local-variable 'py-eshell-last-error) (point))
118               (set (make-local-variable 'py-eshell-prefix) (or (match-string 1) ""))
119               (if example
120                   (forward-line 2)
121                 (while (and (< (forward-line 1) 1) (looking-at (concat py-eshell-prefix "  ")))))
122               (set (make-local-variable 'py-eshell-last-point) (point))
123               (set (make-local-variable 'py-eshell-last-dir) default-directory)
124               (while
125                   (and (< (forward-line 1) 1)
126                        (not (if (looking-at ".*Leaving directory ")
127                                 (progn
128                                   (goto-char (match-end 0))
129                                   (skip-chars-forward "'\"`")
130                                   (let ((dir (current-word)))
131                                     (and dir (setq py-eshell-last-dir
132                                                    (file-name-as-directory dir)))))))))))
133         (goto-char py-eshell-last-point)
134
135         ;; Nove to next / prev frame in traceback
136         (if back
137             (progn
138               (while (and (looking-at (concat py-eshell-prefix "  "))
139                           (< (forward-line 1) 1)
140                           (not (looking-at (concat py-eshell-prefix "  File ")))))
141               (setq start (point))
142               (while (and (looking-at (concat py-eshell-prefix "  "))
143                           (< (forward-line 1) 1)
144                           (not (looking-at (concat py-eshell-prefix "  File ")))))
145               (setq end (point)))
146           (while (and (> (forward-line -1) -1)
147                       (not (looking-at (concat py-eshell-prefix (if example "File " "  File "))))
148                       (> (point) py-eshell-last-error)))
149           (setq end py-eshell-last-point start (point)))
150
151         ;; Parse information and set overlay
152         (if (save-excursion (goto-char start) (setq errmk (point-marker))
153                             (looking-at (concat py-eshell-prefix (if example "File " "  File "))))
154             (let ((default-directory py-eshell-last-dir))
155               (set (make-local-variable 'py-eshell-last-point) start)
156               (if (and (boundp 'py-eshell-overlay) py-eshell-overlay)
157                   (move-overlay py-eshell-overlay start end)
158                 (set (make-local-variable 'py-eshell-overlay) (make-overlay start end))
159                 (overlay-put py-eshell-overlay 'face 'highlight))
160               (save-excursion
161                 (goto-char start)
162                 (forward-char (+ (length py-eshell-prefix) 7))
163                 (skip-chars-forward "\"")
164                 (setq file (current-word))
165                 (search-forward " line ")
166                 (skip-chars-forward " ")
167                 (setq line (string-to-number
168                             (buffer-substring-no-properties
169                              (point) (progn (skip-chars-forward "0-9") (point)))))
170                 (setq dir default-directory))
171               (if (null file)
172                   (error "File not found")))
173           (py-eshell-error-reset)
174           (error "No further traceback line"))))
175
176     ;; move to error locus
177     (if (and file line errmk)
178         (with-current-buffer (py-find-file errmk file dir)
179           (compilation-goto-locus errmk (save-excursion (goto-line line) (point-marker)) nil)))))
180
181 (defun py-eshell-error-reset ()
182   (interactive)
183   (save-excursion
184     (set-buffer "*eshell*")
185     (if (and (boundp 'py-eshell-overlay) py-eshell-overlay)
186         (delete-overlay py-eshell-overlay))
187     (set (make-local-variable 'py-eshell-last-error) nil)
188     (set (make-local-variable 'py-eshell-last-point) nil)
189     (set (make-local-variable 'py-eshell-last-dir) nil)
190     (set (make-local-variable 'py-eshell-prefix) nil)
191     (set (make-local-variable 'py-eshell-overlay) nil)))
192
193 (global-set-key "\C-xP" 'py-eshell-goto-error)
194 (global-set-key "\C-x\C-p" 'python-shell)
195
196 (add-hook 'python-mode-hook 'python-init-auto-cleanup-imports-on-save)
197
198 (defun py-setup-hook ()
199   ;(set-variable 'py-indent-offset 4)
200   ;(set-variable 'py-smart-indentation nil)
201   (set-variable 'indent-tabs-mode nil)
202   ;(define-key py-mode-map (kbd "RET") 'newline-and-indent)
203   ;(define-key py-mode-map [tab] 'yas/expand)
204   ;(setq yas/after-exit-snippet-hook 'indent-according-to-mode)
205   ;(smart-operator-mode-on)
206   (define-key python-mode-map "\C-ci" 'my-pyflymake-add-import-from-error)
207   (define-key python-mode-map "\C-ce" 'my-flymake-show-error)
208   (define-key python-mode-map "\C-cn" 'my-flymake-goto-next-error)
209   (define-key python-mode-map "\C-cI" 'py-cleanup-imports)
210 )
211
212 (add-hook 'python-mode-hook 'py-setup-hook)
213
214 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
215 ;;; Auto-completion
216 ;;;  Integrates:
217 ;;;   1) Rope
218 ;;;   2) Yasnippet
219 ;;;   all with AutoComplete.el
220 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
221 (defun prefix-list-elements (list prefix)
222   (let (value)
223     (nreverse
224      (dolist (element list value)
225       (setq value (cons (format "%s%s" prefix element) value))))))
226 (defvar ac-source-rope
227   '((candidates
228      . (lambda ()
229          (prefix-list-elements (rope-completions) ac-target))))
230   "Source for Rope")
231 (defun ac-python-find ()
232   "Python `ac-find-function'."
233   (require 'thingatpt)
234   (let ((symbol (car-safe (bounds-of-thing-at-point 'symbol))))
235     (if (null symbol)
236         (if (string= "." (buffer-substring (- (point) 1) (point)))
237             (point)
238           nil)
239       symbol)))
240 (defun ac-python-candidate ()
241   "Python `ac-candidates-function'"
242   (let (candidates)
243     (dolist (source ac-sources)
244       (if (symbolp source)
245           (setq source (symbol-value source)))
246       (let* ((ac-limit (or (cdr-safe (assq 'limit source)) ac-limit))
247              (requires (cdr-safe (assq 'requires source)))
248              cand)
249         (if (or (null requires)
250                 (>= (length ac-target) requires))
251             (setq cand
252                   (delq nil
253                         (mapcar (lambda (candidate)
254                                   (propertize candidate 'source source))
255                                 (funcall (cdr (assq 'candidates source)))))))
256         (if (and (> ac-limit 1)
257                  (> (length cand) ac-limit))
258             (setcdr (nthcdr (1- ac-limit) cand) nil))
259         (setq candidates (append candidates cand))))
260     (delete-dups candidates)))
261 (add-hook 'python-mode-hook
262           (lambda ()
263                  (auto-complete-mode 1)
264                  (set (make-local-variable 'ac-sources)
265                       (append ac-sources '(ac-source-rope) '(ac-source-yasnippet)))
266                  (set (make-local-variable 'ac-find-function) 'ac-python-find)
267                  (set (make-local-variable 'ac-candidate-function) 'ac-python-candidate)
268                  (set (make-local-variable 'ac-auto-start) nil)))
269 ;;Ryan's python specific tab completion
270 (defun ryan-python-tab ()
271   ; Try the following:
272   ; 1) Do a yasnippet expansion
273   ; 2) Do a Rope code completion
274   ; 3) Do an indent
275   (interactive)
276   (if (eql (ac-start) 0)
277       (indent-for-tab-command)))
278 (defadvice ac-start (before advice-turn-on-auto-start activate)
279   (set (make-local-variable 'ac-auto-start) t))
280 (defadvice ac-cleanup (after advice-turn-off-auto-start activate)
281   (set (make-local-variable 'ac-auto-start) nil))
282 ;(define-key py-mode-map "\t" 'ryan-python-tab)
283 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
284 ;;; End Auto Completion
285 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
286 ;; Auto Syntax Error Hightlight
287 (when (load "flymake" t)
288   (defun flymake-pyflakes-init ()
289     (let* ((temp-file (flymake-init-create-temp-buffer-copy
290                        'flymake-create-temp-inplace))
291            (local-file (file-relative-name
292                         temp-file
293                         (file-name-directory buffer-file-name))))
294       (list "pyflakes" (list local-file))))
295   (add-to-list 'flymake-allowed-file-name-masks
296                '("\\.py\\'" flymake-pyflakes-init)))
297 (add-hook 'find-file-hook 'flymake-find-file-hook)
298
299 (defun py-imports-region ()
300   (save-excursion
301     (goto-char (point-min))
302     (while (and (re-search-forward "^\\(import\\s-+\\|from\\s-+\\)" nil t)
303                 (looking-at "__future__")))
304     (beginning-of-line)
305     (setq beg (point))
306     (if (not (looking-at "\\(import\\s-+\\|from\\s-+\\)"))
307         (cons beg beg)
308       (while (looking-at "\\(import\\s-+\\|from\\s-+\\)")
309         (forward-line 1))
310       (cons beg (point)))))
311
312 (defun py-cleanup-imports (&optional nowait)
313   (interactive)
314   (let (beg end)
315     (if (region-active-p)
316         (setq beg (region-beginning)
317               end (region-end))
318       (setq reg (py-imports-region))
319       (if (= (car reg) (cdr reg))
320           (error "No imports found"))
321       (setq beg (car reg) end (cdr reg)))
322     (sort-lines t beg end)
323     (goto-char beg)
324     (let ((end-marker (make-marker))
325           (doing-imports t)
326           (b beg))
327       (set-marker end-marker end)
328       (while (< (point) end-marker)
329         (let ((is-import (looking-at "import\\s-+"))
330               (eol-marker (save-excursion (end-of-line) (point-marker)))
331               prefix)
332           (if (and doing-imports (not is-import))
333               (progn
334                 (if (> (point) b)
335                     (progn
336                       (sort-lines nil beg (point))
337                       (setq b (point))))
338                 (setq doing-imports nil)))
339           (setq prefix (if is-import "import "
340                          (buffer-substring-no-properties
341                           (point) (re-search-forward "\\s-+import\\s-+" eol-marker t))))
342           (while (search-forward "," eol-marker t)
343             (delete-char -1)
344             (delete-horizontal-space)
345             (insert "\n" prefix))
346           (forward-line 1)))
347       (sort-lines nil b (point))
348       (if (and (not nowait) (boundp flymake-is-running))
349           (progn
350             (my-flymake-check-and-wait)
351             (goto-char beg)
352             (while (< (point) end-marker)
353               (if (and (loop for ov in (overlays-at (point))
354                              thereis (flymake-overlay-p ov))
355                        (not (save-excursion (search-forward "unused"
356                                                             (save-excursion (end-of-line) (point))
357                                                             t ))))
358                   (delete-region (point)
359                                  (progn (forward-line 1) (point)))
360                 (forward-line 1))))))))
361
362 (defun flyspell-py-progmode-verify ()
363   "Replacement for standard flyspell-generic-progmode-verify which
364 checks for C/C++ preproc directives. Additionally, anything after ^L
365 is ignored (Those are the file local variables and local words)."
366   (let ((f (get-text-property (point) 'face)))
367     (and (memq f flyspell-prog-text-faces)
368          (not (save-excursion
369                 (beginning-of-line)
370                 (looking-at "#!")))
371          (not (let ((l (max (point-min) (- (point-max) 4096))))
372                 (and (< l (point))
373                      (save-excursion (search-backward "\f" l t)))))
374          (not (let* ((pos (python-in-string/comment))
375                      (c (and pos (char-after pos))))
376                 (and pos (not (save-excursion
377                                 (goto-char pos)
378                                 (beginning-of-line)
379                                 (looking-at (concat "^\\s-*[uUrR]?" (regexp-quote (make-string 3 c))))))))))))
380
381 (defun flyspell-py-mode ()
382   "Turn on `flyspell-mode` for comments and multi-line strings"
383   (interactive)
384   (setq flyspell-generic-check-word-p 'flyspell-py-progmode-verify)
385   (flyspell-mode t))
386
387 (provide 'init_python)