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