[EMACS] python-mode patch, 1.08a -> 1.08az

Tim Peters (tim@ksr.com)
Tue, 26 Apr 94 03:22:24 -0400

Attached is a patch to be applied against the Python/Misc/python-mode.el
(1.08a) distributed with Python 1.0.0:

cd $YOUR_ROOT/Python/Misc
patch < this_message

Only change from 1.08ay is a new rule for indenting the first item in an
unclosed paren/brace/bracket structure, when the first item isn't on the
same line as the opening paren(etc). Now it indents py-indent-offset
columns beyond the indentation of the line containing the opening paren.
As before, items after the first strive to line up under the first item.

As a result, only two lines ("*" in column 1) in the following now need
hand-fiddling, and assuming py-indent-offset is 8 (what I believe it was
for the people who sent these):

weekday_translations = {
'Monday': 'Maandag',
'Tuesday': 'Dinsdag',
...
* }

self.tbox = Box({
* 'alignment' : vp.VCENTER,
'stretchability': (vp.FIXED, vp.ELASTIC),
'child_list' :[

ToggleButton({
'name' : 'edit',
'radio_group' : rg,
'callback' : self.SetDrawToolCB,
'use_indicator' : vp.FALSE,
'stretchability': (vp.ELASTIC, vp.FIXED)
}),

ToggleButton({
'name' : 'rect',
'radio_group' : rg,
'callback' : self.SetDrawToolCB,
'use_indicator' : vp.FALSE,
'stretchability': (vp.ELASTIC, vp.FIXED)
})
]
})

In the 2nd example, indentations of both 10 and 8 were used, and I
couldn't think of a comprehensible rule to deliver the "10" one by magic
("indent first item to line up under '=' sign" appears to be the one at
work, but that often doesn't work well ... better to get a consistent 8
(= whatever py-indent-offset is for you), and fiddle it to taste).

Note that the "py-indent-offset + baseline indentation" rule only applies
if there's nothing interesting on the base line after the open bracket.
If you put the first item _on_ the base line, other items still line up
with it; e.g.,

except ( OverflowError, NameError,
(ZeroDivisionError, f( x,
y,
z ))):

breaking-the-last-connection-with-c-mode's-scheme-ly y'rs - tim

Tim Peters tim@ksr.com
not speaking for Kendall Square Research Corp

Change log

version 1.08az
more fiddling for continuation lines in a nest
indent first item to py-indent-offset + baseline indent
update docs

version 1.08ay
fancier indentation for continuation lines
backslash: special-case assignments
nests: strive to mimic indentation of first nest item
update docs

version 1.08ax
teach py-stringlit-re and py-continued-re about double-quoted strings
e.g. a = "#" + \
wasn't recognized as a continued line
make indent-region-function a local vrbl & set to py-indent-region
fiddle indentation for continuation lines
adapt unclosed brace/bracket/paren scheme from donald beaudry
implement guido's suggestion for backslash lines
purge py-continuation-offset
update docs

*** python-mode.el Tue Apr 26 03:14:52 1994
--- ../python-mode.el Tue Apr 26 02:37:41 1994
***************
*** 1,4 ****
! ;;; Major mode for editing Python programs, version 1.08a
;; by: Tim Peters <tim@ksr.com>
;; after an original idea by: Michael A. Guravage
;;
--- 1,4 ----
! ;;; Major mode for editing Python programs, version 1.08az
;; by: Tim Peters <tim@ksr.com>
;; after an original idea by: Michael A. Guravage
;;
***************
*** 40,55 ****
Note that `\\[py-guess-indent-offset]' can usually guess a good value when you're
editing someone else's Python code.")

- (defvar py-continuation-offset 2
- "*Indentation (in addition to py-indent-offset) for continued lines.
- The additional indentation given to the first continuation line in a
- multi-line statement. Each subsequent continuation line in the
- statement inherits its indentation from the line that precedes it, so if
- you don't like the default indentation given to the first continuation
- line, change it to something you do like and Python-mode will
- automatically use that for the remaining continuation lines (or, until
- you change the indentation again).")
-
(defvar py-block-comment-prefix "##"
"*String used by py-comment-region to comment out a block of code.
This should follow the convention for non-indenting comment lines so
--- 40,45 ----
***************
*** 204,210 ****
( ?\# . "<") ; hash starts comment
( ?\n . ">")))) ; newline ends comment

! (defconst py-stringlit-re "'\\([^'\n\\]\\|\\\\.\\)*'"
"regexp matching a Python string literal")

;; this is tricky because a trailing backslash does not mean
--- 194,204 ----
( ?\# . "<") ; 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
***************
*** 211,219 ****
;; continuation if it's in a comment
(defconst py-continued-re
(concat
! "\\(" "[^#'\n\\]" "\\|" py-stringlit-re "\\)*"
"\\\\$")
! "regexp matching Python lines that are continued")

(defconst py-blank-or-comment-re "[ \t]*\\($\\|#\\)"
"regexp matching blank or comment lines")
--- 205,213 ----
;; 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")
***************
*** 231,237 ****
VARIABLES

py-indent-offset\tindentation increment
- py-continuation-offset\textra indentation given to continuation lines
py-block-comment-prefix\tcomment string used by py-comment-region
py-python-command\tshell command to invoke Python interpreter
py-scroll-process-buffer\talways scroll Python process buffer
--- 225,230 ----
***************
*** 254,259 ****
--- 247,253 ----
(comment-start . "# ")
(comment-start-skip . "# *")
(comment-column . 40)
+ (indent-region-function . py-indent-region)
(indent-line-function . py-indent-line)))

;; hack to allow overriding the tabsize in the file (see tokenizer.c)
***************
*** 515,526 ****
(cond
;; are we on a continuation line?
( (py-continuation-line-p)
! (forward-line -1)
! (if (py-continuation-line-p) ; on at least 3rd line in block
! (current-indentation) ; so just continue the pattern
! ;; else on 2nd line in block, so indent more
! (+ (current-indentation) py-indent-offset
! py-continuation-offset)))
;; not on a continuation line

;; if at start of restriction, or on a non-indenting comment line,
--- 509,576 ----
(cond
;; are we on a continuation line?
( (py-continuation-line-p)
! (let ( (startpos (point))
! (open-bracket-pos (py-nesting-level))
! endpos searching found)
! (if open-bracket-pos
! (progn
! ;; align with first item in list; else a normal
! ;; indent beyond the line with the open bracket
! (goto-char (1+ open-bracket-pos)) ; just beyond bracket
! ;; is the first list item on the same line?
! (skip-chars-forward " \t")
! (if (null (memq (following-char) '(?\n ?# ?\\)))
! ; yes, so line up with it
! (current-column)
! ;; first list item on another line, or doesn't exist yet
! (forward-line 1)
! (while (and (< (point) startpos)
! (looking-at "[ \t]*[#\n\\\\]")) ; skip noise
! (forward-line 1))
! (if (< (point) startpos)
! ;; again mimic the first list item
! (current-indentation)
! ;; else they're about to enter the first item
! (goto-char open-bracket-pos)
! (+ (current-indentation) py-indent-offset))))
!
! ;; else on backslash continuation line
! (forward-line -1)
! (if (py-continuation-line-p) ; on at least 3rd line in block
! (current-indentation) ; so just continue the pattern
! ;; else started on 2nd line in block, so indent more.
! ;; if base line is an assignment with a start on a RHS,
! ;; indent to 2 beyond the leftmost "="; else skip first
! ;; chunk of non-whitespace characters on base line, + 1 more
! ;; column
! (end-of-line)
! (setq endpos (point) searching t)
! (back-to-indentation)
! (setq startpos (point))
! ;; look at all "=" from left to right, stopping at first
! ;; one not nested in a list or string
! (while searching
! (skip-chars-forward "^=" endpos)
! (if (= (point) endpos)
! (setq searching nil)
! (forward-char 1)
! (setq state (parse-partial-sexp startpos (point)))
! (if (and (zerop (car state)) ; not in a bracket
! (null (nth 3 state))) ; & not in a string
! (progn
! (setq searching nil) ; done searching in any case
! (setq found
! (not (or
! (eq (following-char) ?=)
! (memq (char-after (- (point) 2))
! '(?< ?> ?!)))))))))
! (if (or (not found) ; not an assignment
! (looking-at "[ \t]*\\\\")) ; <=><spaces><backslash>
! (progn
! (goto-char startpos)
! (skip-chars-forward "^ \t\n")))
! (1+ (current-column))))))
!
;; not on a continuation line

;; if at start of restriction, or on a non-indenting comment line,
***************
*** 1144,1150 ****
@VARIABLES

py-indent-offset\tindentation increment
- py-continuation-offset\textra indentation given to continuation lines
py-block-comment-prefix\tcomment string used by py-comment-region

py-python-command\tshell command to invoke Python interpreter
--- 1194,1199 ----
***************
*** 1153,1159 ****

py-beep-if-tab-change\tring the bell if tab-width is changed
%v:py-indent-offset
- %v:py-continuation-offset
%v:py-block-comment-prefix
%v:py-python-command
%v:py-scroll-process-buffer
--- 1202,1207 ----
***************
*** 1209,1225 ****
statement containing point, even if point happens to be in the middle of
some continuation line.

- A Bad Idea

- Always put something on the initial line of a multi-line statement
- besides the backslash! E.g., don't do this:
-
- \t\\
- \ta = b # what's the indentation of this stmt?
-
- While that's legal Python, it's silly & would be very expensive for
- Python mode to handle correctly.
-
@INDENTATION

Primarily for entering new code:
--- 1257,1263 ----
***************
*** 1265,1270 ****
--- 1303,1330 ----
statement has `:' as its last significant (non-whitespace and non-
comment) character. If the suggested indentation is too much, use
\\[py-delete-char] to reduce it.
+
+ Continuation lines are given extra indentation. If you don't like the
+ suggested indentation, change it to something you do like, and Python-
+ mode will strive to indent later lines of the statement in the same way.
+
+ If a line is a continuation line by virtue of being in an unclosed
+ paren/bracket/brace structure (`list', for short), the suggested
+ indentation depends on whether the current line contains the first item
+ in the list. If it does, it's indented py-indent-offset columns beyond
+ the indentation of the line containing the open bracket. If you don't
+ like that, change it by hand. The remaining items in the list will mimic
+ whatever indentation you give to the first item.
+
+ If a line is a continuation line because the line preceding it ends with
+ a backslash, the third and following lines of the statement inherit their
+ indentation from the line preceding them. The indentation of the second
+ line in the statement depends on the form of the first (base) line: if
+ the base line is an assignment statement with anything more interesting
+ than the backslash following the leftmost assigning `=', the second line
+ is indented two columns beyond that `='. Else it's indented to two
+ columns beyond the leftmost solid chunk of non-whitespace characters on
+ the base line.

Warning: indent-region should not normally be used! It calls \\[indent-for-tab-command]
repeatedly, and as explained above, \\[indent-for-tab-command] can't guess the block

>>> END OF PATCH