update
[emacs-init.git] / python / init_python.el
index b238fcf..4c71a2d 100644 (file)
@@ -1,17 +1,14 @@
-(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)
-        ))
+;(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)
+;(provide 'python)
+;(provide 'python21)
+
+(global-unset-key "\C-xpn")
+
+(require 'python)
+
 ;; pymacs
 (autoload 'pymacs-apply "pymacs")
 (autoload 'pymacs-call "pymacs")
 ;;  '(add-to-list 'pymacs-load-path YOUR-PYMACS-DIRECTORY"))
 (pymacs-load "ropemacs" "rope-")
 (setq ropemacs-enable-autoimport t)
+
+;; M-/               rope-code-assist
+;; C-c g             rope-goto-definition
+;; C-c d             rope-show-doc
+;; C-c f             rope-find-occurrences
+;; M-?               rope-lucky-assist
+
+(define-key ropemacs-local-keymap "\M-/" 'hippie-expand)
+
+(defun write-file-py-cleanup-imports ()
+  (save-excursion
+    (condition-case nil
+        (py-cleanup-imports t)
+      (error . nil)))
+  nil)
+
+(defun python-init-auto-cleanup-imports-on-save ()
+  (add-hook 'write-contents-functions 'write-file-py-cleanup-imports nil t))
+
+(defun my-flymake-error-at-point ()
+  (condition-case  nil
+      (flymake-ler-text (car (nth 0 (flymake-find-err-info flymake-err-info
+                                                           (locate-current-line-number)))))
+    (error (error "no flymake error at point"))))
+
+(defun my-flymake-show-error ()
+  (interactive)
+  (message (my-flymake-error-at-point)))
+
+(defun my-pyflymake-add-import-from-error ()
+  (interactive)
+  (let ((err (my-flymake-error-at-point)))
+    (if (string-match "undefined name '\\(.*\\)'" err)
+        (py-add-import (match-string 1 err))
+      (error "no missing symbol at point"))))
+
+(defun py-add-import (import)
+  (interactive "sModule to import: ")
+  (save-window-excursion
+    (save-excursion
+      (goto-char (car (py-imports-region)))
+      (insert "import " import "\n")
+      (py-cleanup-imports t)
+      (sit-for 2))))
+
+(defun my-flymake-check-and-wait ()
+  (if (boundp flymake-is-running)
+      (progn
+        (while flymake-is-running (sit-for .1))
+        (flymake-start-syntax-check)
+        (while flymake-is-running (sit-for .1)))))
+
+(defun my-flymake-goto-next-error ()
+  (interactive)
+  (my-flymake-check-and-wait)
+  (flymake-goto-next-error)
+  (my-flymake-show-error))
+
+(defun my-flymake-goto-prev-error ()
+  (interactive)
+  (my-flymake-check-and-wait)
+  (flymake-goto-prev-error)
+  (my-flymake-show-error))
+
+(defun py-find-file (errormark filename defaultdir)
+  (let ((fullname (expand-file-name filename defaultdir)))
+    (or (and (not (file-exists-p fullname))
+             (let* ((name (loop for name = fullname then (directory-file-name
+                                                          (file-name-directory name))
+                                if (file-exists-p name) return name))
+                    (fmt (and name (with-temp-buffer
+                                     (insert-file-contents name nil 0 1024 t) (archive-find-type))))
+                    (tail (and fmt (substring fullname (1+ (length name))))))
+               (if fmt
+                   (with-current-buffer (find-file-noselect name)
+                     (goto-char (point-min))
+                     (re-search-forward (concat " " (regexp-quote tail) "$"))
+                     (save-window-excursion
+                       (archive-extract)
+                       (current-buffer))))))
+        (compilation-find-file errormark filename defaultdir))))
+
+(require 'arc-mode)
+
+(defun py-eshell-goto-error (&optional back)
+  (interactive "P")
+  (display-buffer "*eshell*" t)
+  (let (here end start dir file line errmk example)
+    (with-current-buffer "*eshell*"
+      (save-excursion
+        ;; Find and validate last traceback
+        (goto-char (point-max))
+        (re-search-backward "^\\(.*\\)Traceback \\|^Failed example:")
+        (beginning-of-line)
+        (if (looking-at "Failed example:")
+            (progn
+              (forward-line -2)
+              (setq example t)))
+        (if (or (not (and (boundp 'py-eshell-last-error)
+                          (boundp 'py-eshell-last-point)))
+                (null py-eshell-last-error)
+                (null py-eshell-last-point)
+                (null py-eshell-last-dir)
+                (not (= py-eshell-last-error (point))))
+            (progn
+              (set (make-local-variable 'py-eshell-last-error) (point))
+              (set (make-local-variable 'py-eshell-prefix) (or (match-string 1) ""))
+              (if (string-match "Original $" py-eshell-prefix)
+                  (setq py-eshell-prefix (substring py-eshell-prefix 0 (match-beginning 0))))
+              (if example
+                  (forward-line 2)
+                (while (and (< (forward-line 1) 1) (looking-at (concat py-eshell-prefix "  ")))))
+              (set (make-local-variable 'py-eshell-last-point) (point))
+              (set (make-local-variable 'py-eshell-last-dir) default-directory)
+              (while
+                  (and (< (forward-line 1) 1)
+                       (not (if (looking-at ".*Leaving directory ")
+                                (progn
+                                  (goto-char (match-end 0))
+                                  (skip-chars-forward "'\"`")
+                                  (let ((dir (current-word)))
+                                    (and dir (setq py-eshell-last-dir
+                                                   (file-name-as-directory dir)))))))))))
+        (goto-char py-eshell-last-point)
+
+        ;; Nove to next / prev frame in traceback
+        (if back
+            (progn
+              (while (and (looking-at (concat py-eshell-prefix "  "))
+                          (< (forward-line 1) 1)
+                          (not (looking-at (concat py-eshell-prefix "  File ")))))
+              (setq start (point))
+              (while (and (looking-at (concat py-eshell-prefix "  "))
+                          (< (forward-line 1) 1)
+                          (not (looking-at (concat py-eshell-prefix "  File ")))))
+              (setq end (point)))
+          (while (and (> (forward-line -1) -1)
+                      (not (looking-at (concat py-eshell-prefix (if example "File " "  File "))))
+                      (> (point) py-eshell-last-error)))
+          (setq end py-eshell-last-point start (point)))
+
+        ;; Parse information and set overlay
+        (if (save-excursion (goto-char start) (setq errmk (point-marker))
+                            (looking-at (concat py-eshell-prefix (if example "File " "  File "))))
+            (let ((default-directory py-eshell-last-dir))
+              (set (make-local-variable 'py-eshell-last-point) start)
+              (if (and (boundp 'py-eshell-overlay) py-eshell-overlay)
+                  (move-overlay py-eshell-overlay start end)
+                (set (make-local-variable 'py-eshell-overlay) (make-overlay start end))
+                (overlay-put py-eshell-overlay 'face 'highlight))
+              (save-excursion
+                (goto-char start)
+                (forward-char (+ (length py-eshell-prefix) 7))
+                (skip-chars-forward "\"")
+                (setq file (current-word))
+                (search-forward " line ")
+                (skip-chars-forward " ")
+                (setq line (string-to-number
+                            (buffer-substring-no-properties
+                             (point) (progn (skip-chars-forward "0-9") (point)))))
+                (setq dir default-directory))
+              (if (null file)
+                  (error "File not found")))
+          (py-eshell-error-reset)
+          (error "No further traceback line"))))
+
+    ;; move to error locus
+    (if (and file line errmk)
+        (with-current-buffer (py-find-file errmk file dir)
+          (compilation-goto-locus errmk (save-excursion (goto-line line) (point-marker)) nil)))))
+
+(defun py-eshell-error-reset ()
+  (interactive)
+  (save-excursion
+    (set-buffer "*eshell*")
+    (if (and (boundp 'py-eshell-overlay) py-eshell-overlay)
+        (delete-overlay py-eshell-overlay))
+    (set (make-local-variable 'py-eshell-last-error) nil)
+    (set (make-local-variable 'py-eshell-last-point) nil)
+    (set (make-local-variable 'py-eshell-last-dir) nil)
+    (set (make-local-variable 'py-eshell-prefix) nil)
+    (set (make-local-variable 'py-eshell-overlay) nil)))
+
+(global-set-key "\C-xP" 'py-eshell-goto-error)
+(global-set-key "\C-x\C-p" 'python-shell)
+
+(add-hook 'python-mode-hook 'python-init-auto-cleanup-imports-on-save)
+
+(defun py-setup-hook ()
+  ;(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)
+  (define-key python-mode-map "\C-ci" 'my-pyflymake-add-import-from-error)
+  (define-key python-mode-map "\C-ce" 'my-flymake-show-error)
+  (define-key python-mode-map "\C-cn" 'my-flymake-goto-next-error)
+  (define-key python-mode-map "\C-cp" 'my-flymake-goto-prev-error)
+  (define-key python-mode-map "\C-cI" 'py-cleanup-imports)
+)
+
+(add-hook 'python-mode-hook 'py-setup-hook)
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;; Auto-completion
 ;;;  Integrates:
 ;;;   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 nil
+  (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)
+          (point)
           nil)
-      symbol)))
-(defun ac-python-candidate ()
-  "Python `ac-candidates-function'"
-  (let (candidates)
-    (dolist (source ac-sources)
-      (if (symbolp source)
+        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))
+        (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))
+          (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)
+          (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))
+  (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
            (local-file (file-relative-name
                         temp-file
                         (file-name-directory buffer-file-name))))
+      (message "flymake init pyflakes %s" local-file)
       (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)
 
-(defun py-cleanup-imports ()
+(defun safer-flymake-find-file-hook ()
+  "Don't barf if we can't open this flymake file"
+  (let ((flymake-filename
+         (flymake-create-temp-inplace (buffer-file-name) "flymake")))
+    (if (file-writable-p flymake-filename)
+        (flymake-find-file-hook)
+      (message
+       (format
+        "Couldn't enable flymake; permission denied on %s" flymake-filename)))))
+(add-hook 'find-file-hook 'safer-flymake-find-file-hook)
+
+(defun py-skip-few-lines (&optional count)
+  (if (null count) (setq count 3))
+  (let ((blanks 0))
+    (while
+        (and (or (when (eolp) (setq count 0) (incf blanks) t)
+                 (when (> count 0) (decf count) t))
+             (< (forward-line 1) 1)))
+    (> blanks 0)))
+
+(defun py-imports-region ()
+  (save-excursion
+    (goto-char (point-min))
+    (while (and (re-search-forward "^\\(import\\s-+\\|from\\s-+\\)" nil t)
+                (looking-at "__future__")))
+    (beginning-of-line)
+    (setq beg (point))
+    (while (and (py-skip-few-lines)
+                (looking-at "import\\s-+\\|from\\s-+"))
+      (setq beg (point)))
+    (if (not (looking-at "\\(import\\s-+\\|from\\s-+\\)"))
+        (cons beg beg)
+      (while (looking-at "\\(import\\s-+\\|from\\s-+\\)")
+        (forward-line 1))
+      (cons beg (point)))))
+
+(defun py-cleanup-imports (&optional nowait)
   (interactive)
   (let (beg end)
     (if (region-active-p)
         (setq beg (region-beginning)
               end (region-end))
-      (save-excursion
-        (goto-char (point-min))
-        (while (and (re-search-forward "^\\(import\\s-+\\|from\\s-+\\)" nil t)
-                    (looking-at "__future__")))
-        (beginning-of-line)
-        (if (not (looking-at "\\(import\\s-+\\|from\\s-+\\)"))
-            (error "No imports found"))
-        (setq beg (point))
-        (while (looking-at "\\(import\\s-+\\|from\\s-+\\)")
-          (forward-line 1))
-        (setq end (point))))
+      (setq reg (py-imports-region))
+      (if (= (car reg) (cdr reg))
+          (error "No imports found"))
+      (setq beg (car reg) end (cdr reg)))
     (sort-lines t beg end)
     (goto-char beg)
     (let ((end-marker (make-marker))
             (insert "\n" prefix))
           (forward-line 1)))
       (sort-lines nil b (point))
-      (if (boundp flymake-is-running)
+      (if (and (not nowait) (boundp flymake-is-running))
           (progn
-            (while flymake-is-running (sit-for .1))
-            (flymake-start-syntax-check)
-            (while flymake-is-running (sit-for .1))
+            (my-flymake-check-and-wait)
             (goto-char beg)
             (while (< (point) end-marker)
               (if (and (loop for ov in (overlays-at (point))