diff options
author | Barry Warsaw <barry@python.org> | 1997-08-09 06:42:08 (GMT) |
---|---|---|
committer | Barry Warsaw <barry@python.org> | 1997-08-09 06:42:08 (GMT) |
commit | c72c11c9e37489938247842fd9092d120db94032 (patch) | |
tree | 1f589e3be5ef5a12b5595b4c0470bdf52a579671 | |
parent | 98d9d09090f335a64ccf76ad4c640dfac471d11a (diff) | |
download | cpython-c72c11c9e37489938247842fd9092d120db94032.zip cpython-c72c11c9e37489938247842fd9092d120db94032.tar.gz cpython-c72c11c9e37489938247842fd9092d120db94032.tar.bz2 |
#Checkpointing X/Emacs 20'fication of this mode.
-rw-r--r-- | Misc/python-mode.el | 441 |
1 files changed, 231 insertions, 210 deletions
diff --git a/Misc/python-mode.el b/Misc/python-mode.el index bdeaa2b..2905006 100644 --- a/Misc/python-mode.el +++ b/Misc/python-mode.el @@ -9,6 +9,9 @@ ;; Version: 3.0 ;; Keywords: python languages oop +(defconst py-version "3.0" + "`python-mode' version number.") + ;; This software is provided as-is, without express or implied ;; warranty. Permission to use, copy, modify, distribute or sell this ;; software, without fee, for any purpose and by any individual or @@ -27,7 +30,7 @@ ;; 18 support. But all in all, the mode works exceedingly well, and ;; I've simply been tweaking it as I go along. Ain't it wonderful ;; that Python has a much more sane syntax than C? (or <shudder> C++?! -;; :-). I can say that; I maintain cc-mode! +;; :-). I can say that; I maintain CC Mode! ;; The following statements, placed in your .emacs file or ;; site-init.el, will cause this file to be autoloaded, and @@ -96,36 +99,51 @@ ;;; Code: +(require 'custom) + ;; user definable variables ;; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv -(defvar py-python-command "python" - "*Shell command used to start Python interpreter.") +(defgroup python nil + "Support for the Python programming language, <http://www.python.org/>" + :group 'languages) -(defvar py-indent-offset 4 - "*Indentation increment. +(defcustom py-python-command "python" + "*Shell command used to start Python interpreter." + :type 'string + :group 'python) + +(defcustom py-indent-offset 4 + "*Amount of offset per level of indentation Note that `\\[py-guess-indent-offset]' can usually guess a good value -when you're editing someone else's Python code.") +when you're editing someone else's Python code." + :type 'integer + :group 'python) -(defvar py-align-multiline-strings-p t - "*Flag describing how multiline triple quoted strings are aligned. +(defcustom py-align-multiline-strings-p t + "*Flag describing how multi-line triple quoted strings are aligned. When this flag is non-nil, continuation lines are lined up under the preceding line's indentation. When this flag is nil, continuation -lines are aligned to column zero.") +lines are aligned to column zero." + :type '(choice (const :tag "Align under preceding line" t) + (const :tag "Align to column zero" nil)) + :group 'python) -(defvar py-block-comment-prefix "## " +(defcustom py-block-comment-prefix "## " "*String used by \\[comment-region] to comment out a block of code. This should follow the convention for non-indenting comment lines so that the indentation commands won't get confused (i.e., the string should be of the form `#x...' where `x' is not a blank or a tab, and -`...' is arbitrary).") +`...' is arbitrary)." + :type 'string + :group 'python) -(defvar py-honor-comment-indentation t +(defcustom py-honor-comment-indentation t "*Controls how comment lines influence subsequent indentation. When nil, all comment lines are skipped for indentation purposes, and -in Emacs 19, a faster algorithm is used. +if possible, a faster algorithm is used (i.e. X/Emacs 19 and beyond). When t, lines that begin with a single `#' are a hint to subsequent line indentation. If the previous line is such a comment line (as @@ -135,9 +153,15 @@ begin with `py-block-comment-prefix' are ignored for indentation purposes. When not nil or t, comment lines that begin with a `#' are used as -indentation hints, unless the comment character is in column zero.") - -(defvar py-scroll-process-buffer t +indentation hints, unless the comment character is in column zero." + :type '(choice + (const :tag "Skip all comment lines (fast)" nil) + (const :tag "Single # `sets' indentation for next line" t) + (const :tag "Single # `sets' indentation except at column zero" other) + ) + :group 'python) + +(defcustom py-scroll-process-buffer t "*Scroll Python process buffer as output arrives. If nil, the Python process buffer acts, with respect to scrolling, like Shell-mode buffers normally act. This is surprisingly complicated and @@ -175,7 +199,9 @@ happier setting this option to nil. Obscure: `End of buffer' above should really say `at or beyond the process mark', but if you know what that means you didn't need to be -told <grin>.") +told <grin>." + :type 'boolean + :group 'python) (defvar py-temp-directory (let ((ok '(lambda (x) @@ -193,7 +219,9 @@ told <grin>.") "*Directory used for temp files created by a *Python* process. By default, the first directory from this list that exists and that you can write into: the value (if any) of the environment variable TMPDIR, -/usr/tmp, /tmp, or the current directory.") +/usr/tmp, /tmp, or the current directory." + :type 'string + :group 'python) (defvar py-beep-if-tab-change t "*Ring the bell if tab-width is changed. @@ -205,7 +233,34 @@ is found before the first code line when the file is entered, and the current value of (the general Emacs variable) `tab-width' does not equal <number>, `tab-width' is set to <number>, a message saying so is displayed in the echo area, and if `py-beep-if-tab-change' is non-nil -the Emacs bell is also rung as a warning.") +the Emacs bell is also rung as a warning." + :type 'boolean + :group 'python) + + +;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +;; NO USER DEFINABLE VARIABLES BEYOND THIS POINT + +;; As of 30-Jan-1997, Emacs 19.34 works but XEmacs 19.15b90 and +;; previous does not. It is suspected that Emacsen before 19.34 are +;; also broken. +(defvar py-parse-partial-sexp-works-p + (let ((buf (get-buffer-create " ---*---pps---*---")) + state status) + (save-excursion + (set-buffer buf) + (erase-buffer) + (insert "(line1\n line2)\nline3") + (lisp-mode) + (goto-char (point-min)) + (setq state (parse-partial-sexp (point) (save-excursion + (forward-line 1) + (point)))) + (parse-partial-sexp (point) (point-max) 0 nil state) + (setq status (not (= (point) (point-max)))) + (kill-buffer buf) + status)) + "Does `parse-partial-sexp' work in this Emacs?") (defvar python-font-lock-keywords (let* ((keywords '("and" "break" "class" @@ -240,29 +295,98 @@ the Emacs bell is also rung as a warning.") "*Controls echoing of arguments of functions & methods in the imenu buffer. When non-nil, arguments are printed.") - - -;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -;; NO USER DEFINABLE VARIABLES BEYOND THIS POINT - (make-variable-buffer-local 'py-indent-offset) -;; Differentiate between Emacs 18, Lucid Emacs, and Emacs 19. This -;; seems to be the standard way of checking this. -;; BAW - This is *not* the right solution. When at all possible, -;; instead of testing for the version of Emacs, use feature tests. - -(setq py-this-is-lucid-emacs-p (string-match "Lucid\\|XEmacs" emacs-version)) -(setq py-this-is-emacs-19-p - (and - (not py-this-is-lucid-emacs-p) - (string-match "^19\\." emacs-version))) - ;; have to bind py-file-queue before installing the kill-emacs hook (defvar py-file-queue nil "Queue of Python temp files awaiting execution. Currently-active file is at the head of the list.") +(defvar py-delete-function 'backward-delete-char-untabify + "*Function called by `py-delete-char' when deleting characters.") + +(defvar py-backspace-function 'backward-delete-char-untabify + "*Function called by `py-backspace-command' when deleting characters.") + + +;; Constants + +;; Regexp matching a Python string literal +(defconst py-stringlit-re + (concat + "'\\([^'\n\\]\\|\\\\.\\)*'" ; single-quoted + "\\|" ; or + "\"\\([^\"\n\\]\\|\\\\.\\)*\"")) ; double-quoted + +;; Regexp matching Python lines that are continued via backslash. +;; This is tricky because a trailing backslash does not mean +;; continuation if it's in a comment +(defconst py-continued-re + (concat + "\\(" "[^#'\"\n\\]" "\\|" py-stringlit-re "\\)*" + "\\\\$")) + +;; Regexp matching blank or comment lines. +(defconst py-blank-or-comment-re "[ \t]*\\($\\|#\\)") + +;; Regexp matching clauses to be outdented one level. +(defconst py-outdent-re + (concat "\\(" (mapconcat 'identity + '("else:" + "except\\(\\s +.*\\)?:" + "finally:" + "elif\\s +.*:") + "\\|") + "\\)")) + + +;; Regexp matching lines to not outdent after. +(defconst py-no-outdent-re + (concat "\\(" (mapconcat 'identity + '("try:" + "except\\(\\s +.*\\)?:" + "while\\s +.*:" + "for\\s +.*:" + "if\\s +.*:" + "elif\\s +.*:" + "\\(return\\|break\\|raise\\|continue\\)[ \t\n]" + ) + "\\|") + "\\)")) + +;; Regexp matching a function, method or variable assignment. If you +;; change this, you probably have to change `py-current-defun' as +;; well. This is only used by `py-current-defun' to find the name for +;; add-log.el. +(defvar py-defun-start-re + "^\\([ \t]*\\)def[ \t]+\\([a-zA-Z_0-9]+\\)\\|\\(^[a-zA-Z_0-9]+\\)[ \t]*=") + +;; Regexp for finding a class name. If you change this, you probably +;; have to change `py-current-defun' as well. This is only used by +;; `py-current-defun' to find the name for add-log.el. +(defvar py-class-start-re "^class[ \t]*\\([a-zA-Z_0-9]+\\)") + + + +;; Utilities + +(defmacro py-safe (&rest body) + ;; safely execute BODY, return nil if an error occurred + (` (condition-case nil + (progn (,@ body)) + (error nil)))) + +(defsubst py-keep-region-active () + ;; Do whatever is necessary to keep the region active in XEmacs. + ;; Ignore byte-compiler warnings you might see. Also note that + ;; FSF's Emacs 19 does it differently; its policy doesn't require us + ;; to take explicit action. + (and (boundp 'zmacs-region-stays) + (setq zmacs-region-stays t))) + + +;; Major mode boilerplate + ;; define a mode-specific abbrev table for those who use such things (defvar python-mode-abbrev-table nil "Abbrev table in use in `python-mode' buffers.") @@ -276,14 +400,10 @@ Currently-active file is at the head of the list.") (and (fboundp 'make-obsolete-variable) (make-obsolete-variable 'py-mode-hook 'python-mode-hook)) -(defvar py-delete-function 'backward-delete-char-untabify - "*Function called by `py-delete-char' when deleting characters.") - (defvar py-mode-map () "Keymap used in `python-mode' buffers.") - (if py-mode-map - () + nil (setq py-mode-map (make-sparse-keymap)) ;; shadow global bindings for newline-and-indent w/ the py- version. @@ -328,148 +448,81 @@ Currently-active file is at the head of the list.") (defvar py-mode-syntax-table nil "Syntax table used in `python-mode' buffers.") - (if py-mode-syntax-table - () + nil (setq py-mode-syntax-table (make-syntax-table)) - ;; BAW - again, blech. - (mapcar (function - (lambda (x) (modify-syntax-entry - (car x) (cdr x) py-mode-syntax-table))) - '(( ?\( . "()" ) ( ?\) . ")(" ) - ( ?\[ . "(]" ) ( ?\] . ")[" ) - ( ?\{ . "(}" ) ( ?\} . "){" ) - ;; fix operator symbols misassigned in the std table - ( ?\$ . "." ) ( ?\% . "." ) ( ?\& . "." ) - ( ?\* . "." ) ( ?\+ . "." ) ( ?\- . "." ) - ( ?\/ . "." ) ( ?\< . "." ) ( ?\= . "." ) - ( ?\> . "." ) ( ?\| . "." ) - ;; for historical reasons, underscore is word class - ;; instead of symbol class. it should be symbol class, - ;; but if you're tempted to change it, try binding M-f and - ;; M-b to py-forward-into-nomenclature and - ;; py-backward-into-nomenclature instead. -baw - ( ?\_ . "w" ) ; underscore is legit in words - ( ?\' . "\"") ; single quote is string quote - ( ?\" . "\"" ) ; double quote is string quote too - ( ?\` . "$") ; backquote is open and close paren - ( ?\# . "<") ; hash starts comment - ( ?\n . ">")))) ; newline ends comment - -(defconst py-stringlit-re - (concat - "'\\([^'\n\\]\\|\\\\.\\)*'" ; single-quoted - "\\|" ; or - "\"\\([^\"\n\\]\\|\\\\.\\)*\"") ; double-quoted - "Regexp matching a Python string literal.") - -;; this is tricky because a trailing backslash does not mean -;; continuation if it's in a comment -(defconst py-continued-re - (concat - "\\(" "[^#'\"\n\\]" "\\|" py-stringlit-re "\\)*" - "\\\\$") - "Regexp matching Python lines that are continued via backslash.") - -(defconst py-blank-or-comment-re "[ \t]*\\($\\|#\\)" - "Regexp matching blank or comment lines.") - -(defconst py-outdent-re - (concat "\\(" (mapconcat 'identity - '("else:" - "except\\(\\s +.*\\)?:" - "finally:" - "elif\\s +.*:") - "\\|") - "\\)") - "Regexp matching clauses to be outdented one level.") - -(defconst py-no-outdent-re - (concat "\\(" (mapconcat 'identity - '("try:" - "except\\(\\s +.*\\)?:" - "while\\s +.*:" - "for\\s +.*:" - "if\\s +.*:" - "elif\\s +.*:" - "\\(return\\|break\\|raise\\|continue\\)[ \t\n]" - ) - "\\|") - "\\)") - "Regexp matching lines to not outdent after.") - -(defvar py-defun-start-re - "^\\([ \t]*\\)def[ \t]+\\([a-zA-Z_0-9]+\\)\\|\\(^[a-zA-Z_0-9]+\\)[ \t]*=" - "Regexp matching a function, method or variable assignment. - -If you change this, you probably have to change `py-current-defun' as well. -This is only used by `py-current-defun' to find the name for add-log.el.") - -(defvar py-class-start-re "^class[ \t]*\\([a-zA-Z_0-9]+\\)" - "Regexp for finding a class name. + (modify-syntax-entry ?\( "()" py-mode-syntax-table) + (modify-syntax-entry ?\) ")(" py-mode-syntax-table) + (modify-syntax-entry ?\[ "(]" py-mode-syntax-table) + (modify-syntax-entry ?\] ")[" py-mode-syntax-table) + (modify-syntax-entry ?\{ "(}" py-mode-syntax-table) + (modify-syntax-entry ?\} "){" py-mode-syntax-table) + ;; Add operator symbols misassigned in the std table + (modify-syntax-entry ?\$ "." py-mode-syntax-table) + (modify-syntax-entry ?\% "." py-mode-syntax-table) + (modify-syntax-entry ?\& "." py-mode-syntax-table) + (modify-syntax-entry ?\* "." py-mode-syntax-table) + (modify-syntax-entry ?\+ "." py-mode-syntax-table) + (modify-syntax-entry ?\- "." py-mode-syntax-table) + (modify-syntax-entry ?\/ "." py-mode-syntax-table) + (modify-syntax-entry ?\< "." py-mode-syntax-table) + (modify-syntax-entry ?\= "." py-mode-syntax-table) + (modify-syntax-entry ?\> "." py-mode-syntax-table) + (modify-syntax-entry ?\| "." py-mode-syntax-table) + ;; For historical reasons, underscore is word class instead of + ;; symbol class. GNU conventions say it should be symbol class, but + ;; there's a natural conflict between what major mode authors want + ;; and what users expect from `forward-word' and `backward-word'. + ;; Guido and I have hashed this out and have decided to keep + ;; underscore in word class. If you're tempted to change it, try + ;; binding M-f and M-b to py-forward-into-nomenclature and + ;; py-backward-into-nomenclature instead. + (modify-syntax-entry ?\_ "w" py-mode-syntax-table) + ;; Both single quote and double quote are string delimiters + (modify-syntax-entry ?\' "\"" py-mode-syntax-table) + (modify-syntax-entry ?\" "\"" py-mode-syntax-table) + ;; backquote is open and close paren + (modify-syntax-entry ?\` "$" py-mode-syntax-table) + ;; comment delimiters + (modify-syntax-entry ?\# "<" py-mode-syntax-table) + (modify-syntax-entry ?\n ">" py-mode-syntax-table) + ) -If you change this, you probably have to change `py-current-defun' as well. -This is only used by `py-current-defun' to find the name for add-log.el.") - -;; As of 30-Jan-1997, Emacs 19.34 works but XEmacs 19.15b90 and -;; previous does not. It is suspected that Emacsen before 19.34 are -;; also broken. -(defvar py-parse-partial-sexp-works-p - (let ((buf (get-buffer-create " ---*---pps---*---")) - state status) - (save-excursion - (set-buffer buf) - (erase-buffer) - (insert "(line1\n line2)\nline3") - (lisp-mode) - (goto-char (point-min)) - (setq state (parse-partial-sexp (point) (save-excursion - (forward-line 1) - (point)))) - (parse-partial-sexp (point) (point-max) 0 nil state) - (setq status (not (= (point) (point-max)))) - (kill-buffer buf) - status)) - "Does `parse-partial-sexp' work in this Emacs?") ;; Menu definitions, only relevent if you have the easymenu.el package ;; (standard in the latest Emacs 19 and XEmacs 19 distributions). (defvar py-menu nil "Menu for Python Mode. - -This menu will get created automatically if you have the easymenu -package. Note that the latest XEmacs 19 and Emacs 19 versions contain -this package.") - -(if (condition-case nil - (require 'easymenu) - (error nil)) - (easy-menu-define - py-menu py-mode-map "Python Mode menu" - '("Python" - ["Comment Out Region" py-comment-region (mark)] - ["Uncomment Region" (py-comment-region (point) (mark) '(4)) (mark)] - "-" - ["Mark current block" py-mark-block t] - ["Mark current def" mark-python-def-or-class t] - ["Mark current class" (mark-python-def-or-class t) t] - "-" - ["Shift region left" py-shift-region-left (mark)] - ["Shift region right" py-shift-region-right (mark)] - "-" - ["Execute buffer" py-execute-buffer t] - ["Execute region" py-execute-region (mark)] - ["Start interpreter..." py-shell t] - "-" - ["Go to start of block" py-goto-block-up t] - ["Go to start of class" (beginning-of-python-def-or-class t) t] - ["Move to end of class" (end-of-python-def-or-class t) t] - ["Move to start of def" beginning-of-python-def-or-class t] - ["Move to end of def" end-of-python-def-or-class t] - "-" - ["Describe mode" py-describe-mode t] - ))) +This menu will get created automatically if you have the `easymenu' +package. Note that the latest X/Emacs releases contain this package.") + +(and (py-safe (require 'easymenu) t) + (easy-menu-define + py-menu py-mode-map "Python Mode menu" + '("Python" + ["Comment Out Region" py-comment-region (mark)] + ["Uncomment Region" (py-comment-region (point) (mark) '(4)) (mark)] + "-" + ["Mark current block" py-mark-block t] + ["Mark current def" mark-python-def-or-class t] + ["Mark current class" (mark-python-def-or-class t) t] + "-" + ["Shift region left" py-shift-region-left (mark)] + ["Shift region right" py-shift-region-right (mark)] + "-" + ["Execute buffer" py-execute-buffer t] + ["Execute region" py-execute-region (mark)] + ["Start interpreter..." py-shell t] + "-" + ["Go to start of block" py-goto-block-up t] + ["Go to start of class" (beginning-of-python-def-or-class t) t] + ["Move to end of class" (end-of-python-def-or-class t) t] + ["Move to start of def" beginning-of-python-def-or-class t] + ["Move to end of def" end-of-python-def-or-class t] + "-" + ["Describe mode" py-describe-mode t] + ))) @@ -734,7 +787,7 @@ py-beep-if-tab-change\t\tring the bell if tab-width is changed" (if py-menu (easy-menu-add py-menu)) ;; Emacs 19 requires this - (if (or py-this-is-lucid-emacs-p py-this-is-emacs-19-p) + (if (boundp 'comment-multi-line) (setq comment-multi-line nil)) ;; hack to allow overriding the tabsize in the file (see tokenizer.c) ;; @@ -774,15 +827,6 @@ py-beep-if-tab-change\t\tring the bell if tab-width is changed" (run-hooks 'py-mode-hook))) -(defun py-keep-region-active () - ;; do whatever is necessary to keep the region active in XEmacs. - ;; Ignore byte-compiler warnings you might see. Also note that - ;; FSF's Emacs 19 does it differently and doesn't its policy doesn't - ;; require us to take explicit action. - (and (boundp 'zmacs-region-stays) - (setq zmacs-region-stays t))) - - ;; electric characters (defun py-outdent-p () ;; returns non-nil if the current line should outdent one level @@ -796,7 +840,6 @@ py-beep-if-tab-change\t\tring the bell if tab-width is changed" (not (looking-at py-no-outdent-re))) ))) - (defun py-electric-colon (arg) "Insert a colon. In certain cases the line is outdented appropriately. If a numeric @@ -2352,13 +2395,9 @@ local bindings to py-newline-and-indent.")) (defun py-kill-emacs-hook () ;; delete our temp files - (while py-file-queue - (py-delete-file-silently (car py-file-queue)) - (setq py-file-queue (cdr py-file-queue))) - (if (not (or py-this-is-lucid-emacs-p py-this-is-emacs-19-p)) - ;; run the hook we inherited, if any - (and py-inherited-kill-emacs-hook - (funcall py-inherited-kill-emacs-hook)))) + (py-safe (while py-file-queue + (py-delete-file-silently (car py-file-queue)) + (setq py-file-queue (cdr py-file-queue))))) ;; make PROCESS's buffer visible, append STRING to it, and force ;; display; also make shell-mode believe the user typed this string, @@ -2371,13 +2410,7 @@ local bindings to py-newline-and-indent.")) (set-buffer pbuf) (goto-char (point-max)) (move-marker (process-mark process) (point)) - (if (not (or py-this-is-emacs-19-p - py-this-is-lucid-emacs-p)) - (move-marker last-input-start (point))) ; muck w/ shell-mode (funcall (process-filter process) process string) - (if (not (or py-this-is-emacs-19-p - py-this-is-lucid-emacs-p)) - (move-marker last-input-end (point))) ; muck w/ shell-mode (set-buffer cbuf)) (sit-for 0)) @@ -2403,8 +2436,6 @@ local bindings to py-newline-and-indent.")) nil))) -(defconst py-version "$Revision$" - "`python-mode' version number.") (defconst py-help-address "python-mode@python.org" "Address accepting submission of bug reports.") @@ -2453,17 +2484,7 @@ to do so may mean a greater delay in fixing your bug.\n\n") ;; arrange to kill temp files when Emacs exists -(if (or py-this-is-emacs-19-p py-this-is-lucid-emacs-p) - (add-hook 'kill-emacs-hook 'py-kill-emacs-hook) - ;; have to trust that other people are as respectful of our hook - ;; fiddling as we are of theirs - (if (boundp 'py-inherited-kill-emacs-hook) - ;; we were loaded before -- trust others not to have screwed us - ;; in the meantime (no choice, really) - nil - ;; else arrange for our hook to run theirs - (setq py-inherited-kill-emacs-hook kill-emacs-hook) - (setq kill-emacs-hook 'py-kill-emacs-hook))) +(add-hook 'kill-emacs-hook 'py-kill-emacs-hook) |