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