diff --git a/README.md b/README.md new file mode 100644 index 0000000..c3c1949 --- /dev/null +++ b/README.md @@ -0,0 +1,135 @@ +# Python.el + +python.el is now distributed with Emacs; this repository is no longer +maintained. + +## Info + ++ Author: Fabián Ezequiel Gallina ++ Contact: fgallina at gnu dot org ++ Project homepage: http://github.com/fgallina/python.el + +## Install + +If you are using Emacs 24.3 you already have a version of python.el. +If you are using an Emacs 24.x prior 24.3 or you want to update your +copy with some of the enhancements happening on Emacs add the +following snippet in your `.emacs`: + +```emacs-lisp + +(add-to-list 'load-path user-emacs-directory) + +(defun my:ensure-python.el (&optional branch overwrite) + "Install python.el from BRANCH. +After the first install happens the file is not overwritten again +unless the optional argument OVERWRITE is non-nil. When called +interactively python.el will always be overwritten with the +latest version." + (interactive + (list + (completing-read "Install python.el from branch: " + (list "master" "emacs-24") + nil t) + t)) + (let* ((branch (or branch "master")) + (url (format + (concat "http://git.savannah.gnu.org/cgit/emacs.git/plain" + "/lisp/progmodes/python.el?h=%s") branch)) + (destination (expand-file-name "python.el" user-emacs-directory)) + (write (or (not (file-exists-p destination)) overwrite))) + (when write + (with-current-buffer + (url-retrieve-synchronously url) + (delete-region (point-min) (1+ url-http-end-of-headers)) + (write-file destination)) + (byte-compile-file destination t) + destination))) + +(my:ensure-python.el) +``` + +The `master` branch does its best to remain compatible with Emacs 24.x +but it may break, although fixing compatibility breakage is priority +(fill a bug report if you happen to find this). The "emacs-24" tends +to be safer but new features are not added into it. + +Whatever flavor you choose, if you are using a version prior to Emacs +24.3 you need `cl-lib`[0] which is available from GNU ELPA[1] package +archive. + +[0] http://elpa.gnu.org/packages/cl-lib.html +[1] http://elpa.gnu.org/ + +### Alternate method with el-get + +If you are using el-get already you can install `python24` for the +current `emacs-24` branch version or `python` for the `master` branch. + +## Introduction + +This is now the official Python major mode for GNU Emacs. + +It aims to provide the stuff you'll expect from a major mode for +python editing while keeping it simple. + +Currently it implements Syntax highlighting, Indentation, Movement, +Shell interaction, Shell completion, Shell virtualenv support, Pdb +tracking, Symbol completion, Skeletons, FFAP, Code Check, Eldoc, +Imenu. + ++ Syntax highlighting ++ Solid (auto)indentation support ++ auto-detection of indentation levels for current file ++ Robust triple quoted strings support ++ Fancy variable assignment colorization ++ Movement commands you'll expect from a major-mode. ++ Sexp-like movement ++ Python shell integration (not only for Python 2 but also Python 3!) ++ Python shell completion (Same as above!) ++ Python shell virtualenv support (as simple as setting a variable!) ++ PDB Tracking (it even supports ipdb!) ++ Symbol completion that sucks because a running inferior shell + process and valid code in the current buffer are needed (Don't blame + me, it's like that in every python-mode I know). Notice I don't + recommend this thing, use ropemacs instead ++ Skeletons with a tight integration with dabbrev out of the box ++ FFAP (Find Filename At Point), click on an import statement and go + to the module definition. ++ Code check via pychecker by default (this is customizable of + course) ++ Eldoc support (this suffers the same drawbacks as the symbol + completion, but it's the only sane way to do it from Elisp) ++ imenu support to easily navigate your code ++ add-log-current-defun support ++ hideshow support ++ outline support ++ fill paragraph (with customizable docstring formatting) + +The code is well organized in parts with some clean sensitive naming. + +## Requirements + +None, besides Emacs>=24. + +## Emacs 23? + +Latest know version to work with Emacs 23 can be found at + any +functionality/bugfixing back-port that may (or may not) happen in the +future will be placed there. + +## Bug Reports + +The github bug-tracker is being deprecated. + +To report bugs, or to contribute fixes and improvements, use the +built-in Emacs bug reporter (M-x report-emacs-bug) or send email to +bug-gnu-emacs@gnu.org. You can browse Emacs bug database at +debbugs.gnu.org. For more information on contributing, see the +CONTRIBUTE file (distributed with Emacs). + +## License + +python.el is free software under the GPL v3, see LICENSE file for +details. diff --git a/README.org b/README.org deleted file mode 100644 index 2a0b290..0000000 --- a/README.org +++ /dev/null @@ -1,86 +0,0 @@ -This package contains python.el - -* Info - - + Author: Fabián Ezequiel Gallina - + Contact: fabian at anue dot biz - + Project homepage: http://github.com/fgallina/python.el - + My Blog: http://www.from-the-cloud.com - + Downloads page: http://github.com/fgallina/python.el/downloads - + IRC: #python.el at freenode! - -* Introduction - - This is a home-brew python-mode with some excerpts from GNU/Emacs' - python.el. - - It aims to provide the stuff you'll expect from a major mode for - python editing while keeping it simple. - - Currently it implements: - - Syntax highlighting, Indentation, Movement, Shell interaction, Shell - completion, Shell virtualenv support, Pdb tracking, Symbol - completion, Skeletons, FFAP, Code Check, Eldoc, imenu. - - + Syntax highlighting - + Solid (auto)indentation support - + auto-detection of indentation levels for current file - + Triple quoted strings support (stolen without guilt from - GNU/Emacs' original python.el) - + Fancy variable assignment colorization - + Movement commands you'll expect from a major-mode. - + Python shell integration (not only for Python 2 but also Python 3!) - + Python shell completion (Same as above!) - + Python shell virtualenv support (as simple as setting a variable!) - + PDB Tracking (it even supports ipdb!) - + Symbol completion that sucks because a running inferior shell - process and valid code in the current buffer are needed (Don't - blame me, it's like that in every python-mode I know). Notice I - don't recommend this thing, use ropemacs instead - + Skeletons with a tight integration with dabbrev out of the box - + FFAP (Find Filename At Point), click on an import statement and - go to the module definition. - + Code check via pychecker by default (this is customizable of - course) - + Eldoc support (this suffers the same drawbacks as the symbol - completion, but it's the only sane way to do it from Elisp) - + imenu support to easily navigate your code - + add-log-current-defun support - + hideshow support - + outline support - + fill paragraph - - The code is well organized in parts with some clean sensitive - naming. While I did the best effort to keep the code as clean as - possible it might be you find some parts that sucks and that's OK - since I'm not an Elisp guru. Please ping me or fill a ticket when - that happens, could be a nice learning experience. - -* Requirements - - This package was tested with GNU/Emacs 23 only, it should work with - others versions too but I can't ensure anything. Besides from that - nothing else is required. - -* Installation/Usage - - Add this to your .emacs: - - #+BEGIN_EXAMPLE - (add-to-list 'load-path "/folder/containing/file") - (require 'python) - #+END_EXAMPLE - - *M-x describe-mode* when editing a python file should be enough to - get some good overview about what this major-mode is all about :) - -* Bug Reports - - If you find a bug please report it sending an email listed in the - top of the file or just use the github tracker. - -* License - - python.el is free software under the GPL v3, see LICENSE file for - details. diff --git a/python.el b/python.el index 54a657a..f0f67d0 100644 --- a/python.el +++ b/python.el @@ -1,6 +1,6 @@ ;;; python.el --- Python's flying circus support for Emacs -;; Copyright (C) 2003-2012 Free Software Foundation, Inc. +;; Copyright (C) 2003-2013 Free Software Foundation, Inc. ;; Author: Fabián E. Gallina ;; URL: https://github.com/fgallina/python.el @@ -54,8 +54,13 @@ ;; `python-nav-beginning-of-statement', `python-nav-end-of-statement', ;; `python-nav-beginning-of-block' and `python-nav-end-of-block' are ;; included but no bound to any key. At last but not least the -;; specialized `python-nav-forward-sexp' allows easy -;; navigation between code blocks. +;; specialized `python-nav-forward-sexp' allows easy navigation +;; between code blocks. If you prefer `cc-mode'-like `forward-sexp' +;; movement, setting `forward-sexp-function' to nil is enough, You can +;; do that using the `python-mode-hook': + +;; (add-hook 'python-mode-hook +;; (lambda () (setq forward-sexp-function nil))) ;; Shell interaction: is provided and allows you to execute easily any ;; block of code of your current buffer in an inferior Python process. @@ -155,7 +160,10 @@ ;; dabbrev. If you have `dabbrev-mode' activated and ;; `python-skeleton-autoinsert' is set to t, then whenever you type ;; the name of any of those defined and hit SPC, they will be -;; automatically expanded. +;; automatically expanded. As an alternative you can use the defined +;; skeleton commands: `python-skeleton-class', `python-skeleton-def' +;; `python-skeleton-for', `python-skeleton-if', `python-skeleton-try' +;; and `python-skeleton-while'. ;; FFAP: You can find the filename for a given module when using ffap ;; out of the box. This feature needs an inferior python shell @@ -220,7 +228,7 @@ (defgroup python nil "Python Language's flying circus support for Emacs." :group 'languages - :version "23.2" + :version "24.3" :link '(emacs-commentary-link "python")) @@ -700,10 +708,9 @@ START is the buffer position where the sexp starts." ;; After backslash ((setq start (when (not (or (python-syntax-context 'string ppss) (python-syntax-context 'comment ppss))) - (let ((line-beg-pos (line-beginning-position))) - (when (python-info-line-ends-backslash-p - (1- line-beg-pos)) - (- line-beg-pos 2))))) + (let ((line-beg-pos (line-number-at-pos))) + (python-info-line-ends-backslash-p + (1- line-beg-pos))))) 'after-backslash) ;; After beginning of block ((setq start (save-excursion @@ -1187,16 +1194,36 @@ Returns nil if point is not in a def or class." (forward-line -1)))) (point-marker)) -(defun python-nav-end-of-statement () - "Move to end of current statement." +(defun python-nav-end-of-statement (&optional noend) + "Move to end of current statement. +Optional argument NOEND is internal and makes the logic to not +jump to the end of line when moving forward searching for the end +of the statement." (interactive "^") - (while (and (goto-char (line-end-position)) - (not (eobp)) - (when (or - (python-info-line-ends-backslash-p) - (python-syntax-context 'string) - (python-syntax-context 'paren)) - (forward-line 1)))) + (let (string-start bs-pos) + (while (and (or noend (goto-char (line-end-position))) + (not (eobp)) + (cond ((setq string-start (python-syntax-context 'string)) + (goto-char string-start) + (if (python-syntax-context 'paren) + ;; Ended up inside a paren, roll again. + (python-nav-end-of-statement t) + ;; This is not inside a paren, move to the + ;; end of this string. + (goto-char (+ (point) + (python-syntax-count-quotes + (char-after (point)) (point)))) + (or (re-search-forward (rx (syntax string-delimiter)) nil t) + (goto-char (point-max))))) + ((python-syntax-context 'paren) + ;; The statement won't end before we've escaped + ;; at least one level of parenthesis. + (condition-case err + (goto-char (scan-lists (point) 1 -1)) + (scan-error (goto-char (nth 3 err))))) + ((setq bs-pos (python-info-line-ends-backslash-p)) + (goto-char bs-pos) + (forward-line 1)))))) (point-marker)) (defun python-nav-backward-statement (&optional arg) @@ -1301,7 +1328,7 @@ backward to previous block." "Safe version of standard `forward-sexp'. When ARG > 0 move forward, else if ARG is < 0." (or arg (setq arg 1)) - (let ((forward-sexp-function nil) + (let ((forward-sexp-function) (paren-regexp (if (> arg 0) (python-rx close-paren) (python-rx open-paren))) (search-fn @@ -1326,13 +1353,10 @@ backwards." 're-search-backward)) (context-type (python-syntax-context-type))) (cond - ((eq context-type 'string) + ((memq context-type '(string comment)) ;; Inside of a string, get out of it. - (while (and (funcall re-search-fn "[\"']" nil t) - (python-syntax-context 'string)))) - ((eq context-type 'comment) - ;; Inside of a comment, just move forward. - (python-util-forward-comment dir)) + (let ((forward-sexp-function)) + (forward-sexp dir))) ((or (eq context-type 'paren) (and forward-p (looking-at (python-rx open-paren))) (and (not forward-p) @@ -1355,16 +1379,16 @@ backwards." (save-excursion (python-nav-lisp-forward-sexp-safe dir) (point))) - (next-sexp-context - (save-excursion - (goto-char next-sexp-pos) - (cond - ((python-info-beginning-of-block-p) 'block-start) - ((python-info-end-of-block-p) 'block-end) - ((python-info-beginning-of-statement-p) 'statement-start) - ((python-info-end-of-statement-p) 'statement-end) - ((python-info-statement-starts-block-p) 'starts-block) - ((python-info-statement-ends-block-p) 'ends-block))))) + (next-sexp-context + (save-excursion + (goto-char next-sexp-pos) + (cond + ((python-info-beginning-of-block-p) 'block-start) + ((python-info-end-of-block-p) 'block-end) + ((python-info-beginning-of-statement-p) 'statement-start) + ((python-info-end-of-statement-p) 'statement-end) + ((python-info-statement-starts-block-p) 'starts-block) + ((python-info-statement-ends-block-p) 'ends-block))))) (if forward-p (cond ((and (not (eobp)) (python-info-current-line-empty-p)) @@ -1388,8 +1412,8 @@ backwards." (t (goto-char next-sexp-pos))) (cond ((and (not (bobp)) (python-info-current-line-empty-p)) - (python-util-forward-comment dir) - (python-nav--forward-sexp dir)) + (python-util-forward-comment dir) + (python-nav--forward-sexp dir)) ((eq context 'block-end) (python-nav-beginning-of-block)) ((eq context 'statement-end) @@ -1641,7 +1665,11 @@ uniqueness for different types of configurations." (defun python-shell-parse-command () "Calculate the string used to execute the inferior Python process." - (format "%s %s" python-shell-interpreter python-shell-interpreter-args)) + (let ((process-environment (python-shell-calculate-process-environment)) + (exec-path (python-shell-calculate-exec-path))) + (format "%s %s" + (executable-find python-shell-interpreter) + python-shell-interpreter-args))) (defun python-shell-calculate-process-environment () "Calculate process environment given `python-shell-virtualenv-path'." @@ -2300,15 +2328,17 @@ Argument OUTPUT is a string with the output from the comint process." (file-name (with-temp-buffer (insert full-output) - (goto-char (point-min)) - ;; OK, this sucked but now it became a cool hack. The - ;; stacktrace information normally is on the first line - ;; but in some cases (like when doing a step-in) it is - ;; on the second. - (when (or (looking-at python-pdbtrack-stacktrace-info-regexp) - (and - (forward-line) - (looking-at python-pdbtrack-stacktrace-info-regexp))) + ;; When the debugger encounters a pdb.set_trace() + ;; command, it prints a single stack frame. Sometimes + ;; it prints a bit of extra information about the + ;; arguments of the present function. When ipdb + ;; encounters an exception, it prints the _entire_ stack + ;; trace. To handle all of these cases, we want to find + ;; the _last_ stack frame printed in the most recent + ;; batch of output, then jump to the corresponding + ;; file/line number. + (goto-char (point-max)) + (when (re-search-backward python-pdbtrack-stacktrace-info-regexp nil t) (setq line-number (string-to-number (match-string-no-properties 2))) (match-string-no-properties 1))))) @@ -2917,40 +2947,62 @@ Optional argument INCLUDE-TYPE indicates to include the type of the defun. This function is compatible to be used as `add-log-current-defun-function' since it returns nil if point is not inside a defun." - (save-restriction - (widen) - (save-excursion - (end-of-line 1) - (let ((names) - (starting-indentation - (save-excursion - (and - (python-nav-beginning-of-defun 1) - ;; This extra number is just for checking code - ;; against indentation to work well on first run. - (+ (current-indentation) 4)))) - (starting-point (point))) - ;; Check point is inside a defun. - (when (and starting-indentation - (< starting-point + (save-restriction + (widen) + (save-excursion + (end-of-line 1) + (let ((names) + (starting-indentation (current-indentation)) + (starting-pos (point)) + (first-run t) + (last-indent) + (type)) + (catch 'exit + (while (python-nav-beginning-of-defun 1) + (when (save-match-data + (and + (or (not last-indent) + (< (current-indentation) last-indent)) + (or + (and first-run + (save-excursion + ;; If this is the first run, we may add + ;; the current defun at point. + (setq first-run nil) + (goto-char starting-pos) + (python-nav-beginning-of-statement) + (beginning-of-line 1) + (looking-at-p + python-nav-beginning-of-defun-regexp))) + (< starting-pos (save-excursion - (python-nav-end-of-defun) - (point)))) - (catch 'exit - (while (python-nav-beginning-of-defun 1) - (when (< (current-indentation) starting-indentation) - (setq starting-indentation (current-indentation)) - (setq names - (cons - (if (not include-type) - (match-string-no-properties 1) - (mapconcat 'identity - (split-string - (match-string-no-properties 0)) " ")) - names))) - (and (= (current-indentation) 0) (throw 'exit t))))) - (and names - (mapconcat (lambda (string) string) names ".")))))) + (let ((min-indent + (+ (current-indentation) + python-indent-offset))) + (if (< starting-indentation min-indent) + ;; If the starting indentation is not + ;; within the min defun indent make the + ;; check fail. + starting-pos + ;; Else go to the end of defun and add + ;; up the current indentation to the + ;; ending position. + (python-nav-end-of-defun) + (+ (point) + (if (>= (current-indentation) min-indent) + (1+ (current-indentation)) + 0))))))))) + (save-match-data (setq last-indent (current-indentation))) + (if (or (not include-type) type) + (setq names (cons (match-string-no-properties 1) names)) + (let ((match (split-string (match-string-no-properties 0)))) + (setq type (car match)) + (setq names (cons (cadr match) names))))) + ;; Stop searching ASAP. + (and (= (current-indentation) 0) (throw 'exit t)))) + (and names + (concat (and type (format "%s " type)) + (mapconcat 'identity names "."))))))) (defun python-info-current-symbol (&optional replace-self) "Return current symbol using dotty syntax. @@ -3063,7 +3115,7 @@ With optional argument LINE-NUMBER, check that line instead." (save-restriction (widen) (when line-number - (goto-char line-number)) + (python-util-goto-line line-number)) (while (and (not (eobp)) (goto-char (line-end-position)) (python-syntax-context 'paren) @@ -3079,7 +3131,7 @@ Optional argument LINE-NUMBER forces the line number to check against." (save-restriction (widen) (when line-number - (goto-char line-number)) + (python-util-goto-line line-number)) (when (python-info-line-ends-backslash-p) (while (save-excursion (goto-char (line-beginning-position)) @@ -3158,7 +3210,9 @@ operator." (defun python-info-current-line-comment-p () "Check if current line is a comment line." - (char-equal (or (char-after (+ (point) (current-indentation))) ?_) ?#)) + (char-equal + (or (char-after (+ (line-beginning-position) (current-indentation))) ?_) + ?#)) (defun python-info-current-line-empty-p () "Check if current line is empty, ignoring whitespace." @@ -3173,12 +3227,10 @@ operator." ;;; Utility functions -(defun python-util-position (item seq) - "Find the first occurrence of ITEM in SEQ. -Return the index of the matching item, or nil if not found." - (let ((member-result (member item seq))) - (when member-result - (- (length seq) (length member-result))))) +(defun python-util-goto-line (line-number) + "Move point to LINE-NUMBER." + (goto-char (point-min)) + (forward-line (1- line-number))) ;; Stolen from org-mode (defun python-util-clone-local-variables (from-buffer &optional regexp)