(interactive "P") (let* ((ci (current-indentation)) (move-to-indentation-p (<= (current-column) ci)) (need (py-compute-indentation (not arg)))) ;; see if we need to dedent (if (py-outdent-p) (setq need (- need py-indent-offset))) (if (/= ci need) (save-excursion (beginning-of-line) (delete-horizontal-space) (indent-to need))) (if move-to-indentation-p (back-to-indentation)))) (defun py-newline-and-indent () "Strives to act like the Emacs `newline-and-indent'.This is just `strives to' because correct indentation can't be computedfrom scratch for Python code. In general, deletes the whitespace beforepoint, inserts a newline, and takes an educated guess as to how you wantthe new line indented." (interactive) (let ((ci (current-indentation))) (if (< ci (current-column)) ; if point beyond indentation (newline-and-indent) ;; else try to act like newline-and-indent "normally" acts (beginning-of-line) (insert-char ?\n 1) (move-to-column ci)))) (defun py-compute-indentation (honor-block-close-p) "Compute Python indentation.When HONOR-BLOCK-CLOSE-P is non-nil, statements such as `return',`raise', `break', `continue', and `pass' force one level ofdedenting." (save-excursion (beginning-of-line) (let* ((bod (py-point 'bod)) (pps (parse-partial-sexp bod (point))) (boipps (parse-partial-sexp bod (py-point 'boi))) placeholder) (cond ;; are we inside a multi-line string or comment? ((or (and (nth 3 pps) (nth 3 boipps)) (and (nth 4 pps) (nth 4 boipps))) (save-excursion (if (not py-align-multiline-strings-p) 0 ;; skip back over blank & non-indenting comment lines ;; note: will skip a blank or non-indenting comment line ;; that happens to be a continuation line too (re-search-backward "^[ \t]*\\([^ \t\n#]\\|#[ \t\n]\\)" nil 'move) (back-to-indentation) (current-column)))) ;; are we on a continuation line? ((py-continuation-line-p) (let ((startpos (point)) (open-bracket-pos (py-nesting-level)) endpos searching found state) (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 (and (< (point) startpos) (/= startpos (save-excursion (goto-char (1+ open-bracket-pos)) (forward-comment (point-max)) (point)))) ;; again mimic the first list item (current-indentation) ;; else they're about to enter the first item (goto-char open-bracket-pos) (setq placeholder (point)) (py-goto-initial-line) (py-goto-beginning-of-tqs (save-excursion (nth 3 (parse-partial-sexp placeholder (point))))) (+ (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"))) ;; if this is a continuation for a block opening ;; statement, add some extra offset. (+ (current-column) (if (py-statement-opens-block-p) py-continuation-offset 0) 1) )))) ;; not on a continuation line ((bobp) (current-indentation)) ;; Dfn: "Indenting comment line". A line containing only a ;; comment, but which is treated like a statement for ;; indentation calculation purposes. Such lines are only ;; treated specially by the mode; they are not treated ;; specially by the Python interpreter. ;; The rules for indenting comment lines are a line where: ;; - the first non-whitespace character is `#', and ;; - the character following the `#' is whitespace, and ;; - the line is dedented with respect to (i.e. to the left ;; of) the indentation of the preceding non-blank line. ;; The first non-blank line following an indenting comment ;; line is given the same amount of indentation as the ;; indenting comment line. ;; All other comment-only lines are ignored for indentation ;; purposes. ;; Are we looking at a comment-only line which is *not* an ;; indenting comment line? If so, we assume that it's been ;; placed at the desired indentation, so leave it alone. ;; Indenting comment lines are aligned as statements down ;; below. ((and (looking-at "[ \t]*#[^ \t\n]") ;; NOTE: this test will not be performed in older Emacsen (fboundp 'forward-comment) (<= (current-indentation) (save-excursion (forward-comment (- (point-max))) (current-indentation)))) (current-indentation)) ;; else indentation based on that of the statement that ;; precedes us; use the first line of that statement to ;; establish the base, in case the user forced a non-std ;; indentation for the continuation lines (if any) (t ;; skip back over blank & non-indenting comment lines note: ;; will skip a blank or non-indenting comment line that ;; happens to be a continuation line too. use fast Emacs 19 ;; function if it's there. (if (and (eq py-honor-comment-indentation nil) (fboundp 'forward-comment)) (forward-comment (- (point-max))) (let ((prefix-re (concat py-block-comment-prefix "[ \t]*")) done) (while (not done) (re-search-backward "^[ \t]*\\([^ \t\n#]\\|#\\)" nil 'move) (setq done (or (bobp) (and (eq py-honor-comment-indentation t) (save-excursion (back-to-indentation) (not (looking-at prefix-re)) )) (and (not (eq py-honor-comment-indentation t)) (save-excursion (back-to-indentation) (and (not (looking-at prefix-re)) (or (looking-at "[^#]") (not (zerop (current-column))) )) )) )) ))) ;; if we landed inside a string, go to the beginning of that ;; string. this handles triple quoted, multi-line spanning ;; strings. (py-goto-beginning-of-tqs (nth 3 (parse-partial-sexp bod (point)))) ;; now skip backward over continued lines (setq placeholder (point)) (py-goto-initial-line) ;; we may *now* have landed in a TQS, so find the beginning of ;; this string. (py-goto-beginning-of-tqs (save-excursion (nth 3 (parse-partial-sexp placeholder (point))))) (+ (current-indentation) (if (py-statement-opens-block-p) py-indent-offset (if (and honor-block-close-p (py-statement-closes-block-p)) (- py-indent-offset) 0))) ))))) (defun py-guess-indent-offset (&optional global) "Guess a good value for, and change, `py-indent-offset'. By default, make a buffer-local copy of `py-indent-offset' with thenew value, so that other Python buffers are not affected. With\\[universal-argument] (programmatically, optional argument GLOBAL),change the global value of `py-indent-offset'. This affects allPython buffers (that don't have their own buffer-local copy), boththose currently existing and those created later in the Emacs session. Some people use a different value for `py-indent-offset' than you use.There's no excuse for such foolishness, but sometimes you have to dealwith their ugly code anyway. This function examines the file and sets`py-indent-offset' to what it thinks it was when they created themess. Specifically, it searches forward from the statement containing point,looking for a line that opens a block of code. `py-indent-offset' isset to the difference in indentation between that line and the Pythonstatement following it. If the search doesn't succeed going forward,it's tried again going backward." (interactive "P") ; raw prefix arg (let (new-value (start (point)) (restart (point)) (found nil) colon-indent) (py-goto-initial-line) (while (not (or found (eobp))) (when (and (re-search-forward ":[ \t]*\\($\\|[#\\]\\)" nil 'move) (not (py-in-literal restart))) (setq restart (point)) (py-goto-initial-line) (if (py-statement-opens-block-p) (setq found t) (goto-char restart)))) (unless found (goto-char start) (py-goto-initial-line) (while (not (or found (bobp))) (setq found (and (re-search-backward ":[ \t]*\\($\\|[#\\]\\)" nil 'move) (or (py-goto-initial-line) t) ; always true -- side effect (py-statement-opens-block-p))))) (setq colon-indent (current-indentation) found (and found (zerop (py-next-statement 1))) new-value (- (current-indentation) colon-indent)) (goto-char start) (if (not found) (error "Sorry, couldn't guess a value for py-indent-offset") (funcall (if global 'kill-local-variable 'make-local-variable) 'py-indent-offset) (setq py-indent-offset new-value) (or noninteractive (message "%s value of py-indent-offset set to %d" (if global "Global" "Local") py-indent-offset))) )) (defun py-comment-indent-function () "Python version of `comment-indent-function'." ;; This is required when filladapt is turned off. Without it, when ;; filladapt is not used, comments which start in column zero ;; cascade one character to the right (save-excursion (beginning-of-line) (let ((eol (py-point 'eol))) (and comment-start-skip (re-search-forward comment-start-skip eol t) (setq eol (match-beginning 0))) (goto-char eol) (skip-chars-backward " \t") (max comment-column (+ (current-column) (if (bolp) 0 1))) ))) (defun py-narrow-to-defun (&optional class) "Make text outside current defun invisible.The defun visible is the one that contains point or follows point.Optional CLASS is passed directly to `py-beginning-of-def-or-class'." (interactive "P") (save-excursion (widen) (py-end-of-def-or-class class) (let ((end (point))) (py-beginning-of-def-or-class class) (narrow-to-region (point) end)))) (defun py-shift-region (start end count) "Indent lines from START to END by COUNT spaces." (save-excursion (goto-char end) (beginning-of-line) (setq end (point)) (goto-char start) (beginning-of-line) (setq start (point)) (indent-rigidly start end count))) (defun py-shift-region-left (start end &optional count) "Shift region of Python code to the left.The lines from the line containing the start of the current region upto (but not including) the line containing the end of the region areshifted to the left, by `py-indent-offset' columns. If a prefix argument is given, the region is instead shifted by thatmany columns. With no active region, dedent only the current line.You cannot dedent the region if any line is already at column zero." (interactive (let ((p (point)) (m (mark)) (arg current-prefix-arg)) (if m (list (min p m) (max p m) arg) (list p (save-excursion (forward-line 1) (point)) arg)))) ;; if any line is at column zero, don't shift the region (save-excursion (goto-char start) (while (< (point) end) (back-to-indentation) (if (and (zerop (current-column)) (not (looking-at "\\s *$"))) (error "Region is at left edge")) (forward-line 1))) (py-shift-region start end (- (prefix-numeric-value (or count py-indent-offset)))) (py-keep-region-active)) (defun py-shift-region-right (start end &optional count) "Shift region of Python code to the right.The lines from the line containing the start of the current region upto (but not including) the line containing the end of the region areshifted to the right, by `py-indent-offset' columns. If a prefix argument is given, the region is instead shifted by thatmany columns. With no active region, indent only the current line." (interactive (let ((p (point)) (m (mark)) (arg current-prefix-arg)) (if m (list (min p m) (max p m) arg) (list p (save-excursion (forward-line 1) (point)) arg)))) (py-shift-region start end (prefix-numeric-value (or count py-indent-offset))) (py-keep-region-active)) (defun py-indent-region (start end &optional indent-offset) "Reindent a region of Python code. The lines from the line containing the start of the current region upto (but not including) the line containing the end of the region arereindented. If the first line of the region has a non-whitespacecharacter in the first column, the first line is left alone and therest of the region is reindented with respect to it. Else the entireregion is reindented with respect to the (closest code or indentingcomment) statement immediately preceding the region. This is useful when code blocks are moved or yanked, when enclosingcontrol structures are introduced or removed, or to reformat codeusing a new value for the indentation offset. If a numeric prefix argument is given, it will be used as the value ofthe indentation offset. Else the value of `py-indent-offset' will beused. Warning: The region must be consistently indented before this functionis called! This function does not compute proper indentation fromscratch (that's impossible in Python), it merely adjusts the existingindentation to be correct in context. Warning: This function really has no idea what to do withnon-indenting comment lines, and shifts them as if they were indentingcomment lines. Fixing this appears to require telepathy. Special cases: whitespace is deleted from blank lines; continuationlines are shifted by the same amount their initial line was shifted,in order to preserve their relative indentation with respect to theirinitial line; and comment lines beginning in column 1 are ignored." (interactive "*r\nP") ; region; raw prefix arg (save-excursion (goto-char end) (beginning-of-line) (setq end (point-marker)) (goto-char start) (beginning-of-line) (let ((py-indent-offset (prefix-numeric-value (or indent-offset py-indent-offset))) (indents '(-1)) ; stack of active indent levels (target-column 0) ; column to which to indent (base-shifted-by 0) ; amount last base line was shifted (indent-base (if (looking-at "[ \t\n]") (py-compute-indentation t) 0)) ci) (while (< (point) end) (setq ci (current-indentation)) ;; figure out appropriate target column (cond ((or (eq (following-char) ?#) ; comment in column 1 (looking-at "[ \t]*$")) ; entirely blank (setq target-column 0)) ((py-continuation-line-p) ; shift relative to base line (setq target-column (+ ci base-shifted-by))) (t ; new base line (if (> ci (car indents)) ; going deeper; push it (setq indents (cons ci indents)) ;; else we should have seen this indent before (setq indents (memq ci indents)) ; pop deeper indents (if (null indents) (error "Bad indentation in region, at line %d" (save-restriction (widen) (1+ (count-lines 1 (point))))))) (setq target-column (+ indent-base (* py-indent-offset (- (length indents) 2)))) (setq base-shifted-by (- target-column ci)))) ;; shift as needed (if (/= ci target-column) (progn (delete-horizontal-space) (indent-to target-column))) (forward-line 1)))) (set-marker end nil)) (defun py-comment-region (beg end &optional arg) "Like `comment-region' but uses double hash (`#') comment starter." (interactive "r\nP") (let ((comment-start py-block-comment-prefix)) (comment-region beg end arg))) ;; Functions for moving point(defun py-previous-statement (count) "Go to the start of the COUNTth preceding Python statement.By default, goes to the previous statement. If there is no suchstatement, goes to the first statement. Return count of statementsleft to move. `Statements' do not include blank, comment, orcontinuation lines." (interactive "p") ; numeric prefix arg (if (< count 0) (py-next-statement (- count)) (py-goto-initial-line) (let (start) (while (and (setq start (point)) ; always true -- side effect (> count 0) (zerop (forward-line -1)) (py-goto-statement-at-or-above)) (setq count (1- count))) (if (> count 0) (goto-char start))) count)) (defun py-next-statement (count) "Go to the start of next Python statement.If the statement at point is the i'th Python statement, goes to thestart of statement i+COUNT. If there is no such statement, goes to thelast statement. Returns count of statements left to move. `Statements'do not include blank, comment, or continuation lines." (interactive "p") ; numeric prefix arg (if (< count 0) (py-previous-statement (- count)) (beginning-of-line) (let (start) (while (and (setq start (point)) ; always true -- side effect (> count 0) (py-goto-statement-below)) (setq count (1- count))) (if (> count 0) (goto-char start))) count)) (defun py-goto-block-up (&optional nomark) "Move up to start of current block.Go to the statement that starts the smallest enclosing block; roughlyspeaking, this will be the closest preceding statement that ends with acolon and is indented less than the statement you started on. Ifsuccessful, also sets the mark to the starting point. `\\[py-mark-block]' can be used afterward to mark the whole codeblock, if desired. If called from a program, the mark will not be set if optional argumentNOMARK is not nil."Lines 2001–2500 of 3768. Previous lines Load more
(interactive "P") (let* ((ci (current-indentation)) (move-to-indentation-p (<= (current-column) ci)) (need (py-compute-indentation (not arg)))) ;; see if we need to dedent (if (py-outdent-p) (setq need (- need py-indent-offset))) (if (/= ci need) (save-excursion (beginning-of-line) (delete-horizontal-space) (indent-to need))) (if move-to-indentation-p (back-to-indentation)))) (defun py-newline-and-indent () "Strives to act like the Emacs `newline-and-indent'. This is just `strives to' because correct indentation can't be computed from scratch for Python code. In general, deletes the whitespace before point, inserts a newline, and takes an educated guess as to how you want the new line indented." (interactive) (let ((ci (current-indentation))) (if (< ci (current-column)) ; if point beyond indentation (newline-and-indent) ;; else try to act like newline-and-indent "normally" acts (beginning-of-line) (insert-char ?\n 1) (move-to-column ci)))) (defun py-compute-indentation (honor-block-close-p) "Compute Python indentation. When HONOR-BLOCK-CLOSE-P is non-nil, statements such as `return', `raise', `break', `continue', and `pass' force one level of dedenting." (save-excursion (beginning-of-line) (let* ((bod (py-point 'bod)) (pps (parse-partial-sexp bod (point))) (boipps (parse-partial-sexp bod (py-point 'boi))) placeholder) (cond ;; are we inside a multi-line string or comment? ((or (and (nth 3 pps) (nth 3 boipps)) (and (nth 4 pps) (nth 4 boipps))) (save-excursion (if (not py-align-multiline-strings-p) 0 ;; skip back over blank & non-indenting comment lines ;; note: will skip a blank or non-indenting comment line ;; that happens to be a continuation line too (re-search-backward "^[ \t]*\\([^ \t\n#]\\|#[ \t\n]\\)" nil 'move) (back-to-indentation) (current-column)))) ;; are we on a continuation line? ((py-continuation-line-p) (let ((startpos (point)) (open-bracket-pos (py-nesting-level)) endpos searching found state) (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 (and (< (point) startpos) (/= startpos (save-excursion (goto-char (1+ open-bracket-pos)) (forward-comment (point-max)) (point)))) ;; again mimic the first list item (current-indentation) ;; else they're about to enter the first item (goto-char open-bracket-pos) (setq placeholder (point)) (py-goto-initial-line) (py-goto-beginning-of-tqs (save-excursion (nth 3 (parse-partial-sexp placeholder (point))))) (+ (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"))) ;; if this is a continuation for a block opening ;; statement, add some extra offset. (+ (current-column) (if (py-statement-opens-block-p) py-continuation-offset 0) 1) )))) ;; not on a continuation line ((bobp) (current-indentation)) ;; Dfn: "Indenting comment line". A line containing only a ;; comment, but which is treated like a statement for ;; indentation calculation purposes. Such lines are only ;; treated specially by the mode; they are not treated ;; specially by the Python interpreter. ;; The rules for indenting comment lines are a line where: ;; - the first non-whitespace character is `#', and ;; - the character following the `#' is whitespace, and ;; - the line is dedented with respect to (i.e. to the left ;; of) the indentation of the preceding non-blank line. ;; The first non-blank line following an indenting comment ;; line is given the same amount of indentation as the ;; indenting comment line. ;; All other comment-only lines are ignored for indentation ;; purposes. ;; Are we looking at a comment-only line which is *not* an ;; indenting comment line? If so, we assume that it's been ;; placed at the desired indentation, so leave it alone. ;; Indenting comment lines are aligned as statements down ;; below. ((and (looking-at "[ \t]*#[^ \t\n]") ;; NOTE: this test will not be performed in older Emacsen (fboundp 'forward-comment) (<= (current-indentation) (save-excursion (forward-comment (- (point-max))) (current-indentation)))) (current-indentation)) ;; else indentation based on that of the statement that ;; precedes us; use the first line of that statement to ;; establish the base, in case the user forced a non-std ;; indentation for the continuation lines (if any) (t ;; skip back over blank & non-indenting comment lines note: ;; will skip a blank or non-indenting comment line that ;; happens to be a continuation line too. use fast Emacs 19 ;; function if it's there. (if (and (eq py-honor-comment-indentation nil) (fboundp 'forward-comment)) (forward-comment (- (point-max))) (let ((prefix-re (concat py-block-comment-prefix "[ \t]*")) done) (while (not done) (re-search-backward "^[ \t]*\\([^ \t\n#]\\|#\\)" nil 'move) (setq done (or (bobp) (and (eq py-honor-comment-indentation t) (save-excursion (back-to-indentation) (not (looking-at prefix-re)) )) (and (not (eq py-honor-comment-indentation t)) (save-excursion (back-to-indentation) (and (not (looking-at prefix-re)) (or (looking-at "[^#]") (not (zerop (current-column))) )) )) )) ))) ;; if we landed inside a string, go to the beginning of that ;; string. this handles triple quoted, multi-line spanning ;; strings. (py-goto-beginning-of-tqs (nth 3 (parse-partial-sexp bod (point)))) ;; now skip backward over continued lines (setq placeholder (point)) (py-goto-initial-line) ;; we may *now* have landed in a TQS, so find the beginning of ;; this string. (py-goto-beginning-of-tqs (save-excursion (nth 3 (parse-partial-sexp placeholder (point))))) (+ (current-indentation) (if (py-statement-opens-block-p) py-indent-offset (if (and honor-block-close-p (py-statement-closes-block-p)) (- py-indent-offset) 0))) ))))) (defun py-guess-indent-offset (&optional global) "Guess a good value for, and change, `py-indent-offset'. By default, make a buffer-local copy of `py-indent-offset' with the new value, so that other Python buffers are not affected. With \\[universal-argument] (programmatically, optional argument GLOBAL), change the global value of `py-indent-offset'. This affects all Python buffers (that don't have their own buffer-local copy), both those currently existing and those created later in the Emacs session. Some people use a different value for `py-indent-offset' than you use. There's no excuse for such foolishness, but sometimes you have to deal with their ugly code anyway. This function examines the file and sets `py-indent-offset' to what it thinks it was when they created the mess. Specifically, it searches forward from the statement containing point, looking for a line that opens a block of code. `py-indent-offset' is set to the difference in indentation between that line and the Python statement following it. If the search doesn't succeed going forward, it's tried again going backward." (interactive "P") ; raw prefix arg (let (new-value (start (point)) (restart (point)) (found nil) colon-indent) (py-goto-initial-line) (while (not (or found (eobp))) (when (and (re-search-forward ":[ \t]*\\($\\|[#\\]\\)" nil 'move) (not (py-in-literal restart))) (setq restart (point)) (py-goto-initial-line) (if (py-statement-opens-block-p) (setq found t) (goto-char restart)))) (unless found (goto-char start) (py-goto-initial-line) (while (not (or found (bobp))) (setq found (and (re-search-backward ":[ \t]*\\($\\|[#\\]\\)" nil 'move) (or (py-goto-initial-line) t) ; always true -- side effect (py-statement-opens-block-p))))) (setq colon-indent (current-indentation) found (and found (zerop (py-next-statement 1))) new-value (- (current-indentation) colon-indent)) (goto-char start) (if (not found) (error "Sorry, couldn't guess a value for py-indent-offset") (funcall (if global 'kill-local-variable 'make-local-variable) 'py-indent-offset) (setq py-indent-offset new-value) (or noninteractive (message "%s value of py-indent-offset set to %d" (if global "Global" "Local") py-indent-offset))) )) (defun py-comment-indent-function () "Python version of `comment-indent-function'." ;; This is required when filladapt is turned off. Without it, when ;; filladapt is not used, comments which start in column zero ;; cascade one character to the right (save-excursion (beginning-of-line) (let ((eol (py-point 'eol))) (and comment-start-skip (re-search-forward comment-start-skip eol t) (setq eol (match-beginning 0))) (goto-char eol) (skip-chars-backward " \t") (max comment-column (+ (current-column) (if (bolp) 0 1))) ))) (defun py-narrow-to-defun (&optional class) "Make text outside current defun invisible. The defun visible is the one that contains point or follows point. Optional CLASS is passed directly to `py-beginning-of-def-or-class'." (interactive "P") (save-excursion (widen) (py-end-of-def-or-class class) (let ((end (point))) (py-beginning-of-def-or-class class) (narrow-to-region (point) end)))) (defun py-shift-region (start end count) "Indent lines from START to END by COUNT spaces." (save-excursion (goto-char end) (beginning-of-line) (setq end (point)) (goto-char start) (beginning-of-line) (setq start (point)) (indent-rigidly start end count))) (defun py-shift-region-left (start end &optional count) "Shift region of Python code to the left. The lines from the line containing the start of the current region up to (but not including) the line containing the end of the region are shifted to the left, by `py-indent-offset' columns. If a prefix argument is given, the region is instead shifted by that many columns. With no active region, dedent only the current line. You cannot dedent the region if any line is already at column zero." (interactive (let ((p (point)) (m (mark)) (arg current-prefix-arg)) (if m (list (min p m) (max p m) arg) (list p (save-excursion (forward-line 1) (point)) arg)))) ;; if any line is at column zero, don't shift the region (save-excursion (goto-char start) (while (< (point) end) (back-to-indentation) (if (and (zerop (current-column)) (not (looking-at "\\s *$"))) (error "Region is at left edge")) (forward-line 1))) (py-shift-region start end (- (prefix-numeric-value (or count py-indent-offset)))) (py-keep-region-active)) (defun py-shift-region-right (start end &optional count) "Shift region of Python code to the right. The lines from the line containing the start of the current region up to (but not including) the line containing the end of the region are shifted to the right, by `py-indent-offset' columns. If a prefix argument is given, the region is instead shifted by that many columns. With no active region, indent only the current line." (interactive (let ((p (point)) (m (mark)) (arg current-prefix-arg)) (if m (list (min p m) (max p m) arg) (list p (save-excursion (forward-line 1) (point)) arg)))) (py-shift-region start end (prefix-numeric-value (or count py-indent-offset))) (py-keep-region-active)) (defun py-indent-region (start end &optional indent-offset) "Reindent a region of Python code. The lines from the line containing the start of the current region up to (but not including) the line containing the end of the region are reindented. If the first line of the region has a non-whitespace character in the first column, the first line is left alone and the rest of the region is reindented with respect to it. Else the entire region is reindented with respect to the (closest code or indenting comment) statement immediately preceding the region. This is useful when code blocks are moved or yanked, when enclosing control structures are introduced or removed, or to reformat code using a new value for the indentation offset. If a numeric prefix argument is given, it will be used as the value of the indentation offset. Else the value of `py-indent-offset' will be used. Warning: The region must be consistently indented before this function is called! This function does not compute proper indentation from scratch (that's impossible in Python), it merely adjusts the existing indentation to be correct in context. Warning: This function really has no idea what to do with non-indenting comment lines, and shifts them as if they were indenting comment lines. Fixing this appears to require telepathy. Special cases: whitespace is deleted from blank lines; continuation lines are shifted by the same amount their initial line was shifted, in order to preserve their relative indentation with respect to their initial line; and comment lines beginning in column 1 are ignored." (interactive "*r\nP") ; region; raw prefix arg (save-excursion (goto-char end) (beginning-of-line) (setq end (point-marker)) (goto-char start) (beginning-of-line) (let ((py-indent-offset (prefix-numeric-value (or indent-offset py-indent-offset))) (indents '(-1)) ; stack of active indent levels (target-column 0) ; column to which to indent (base-shifted-by 0) ; amount last base line was shifted (indent-base (if (looking-at "[ \t\n]") (py-compute-indentation t) 0)) ci) (while (< (point) end) (setq ci (current-indentation)) ;; figure out appropriate target column (cond ((or (eq (following-char) ?#) ; comment in column 1 (looking-at "[ \t]*$")) ; entirely blank (setq target-column 0)) ((py-continuation-line-p) ; shift relative to base line (setq target-column (+ ci base-shifted-by))) (t ; new base line (if (> ci (car indents)) ; going deeper; push it (setq indents (cons ci indents)) ;; else we should have seen this indent before (setq indents (memq ci indents)) ; pop deeper indents (if (null indents) (error "Bad indentation in region, at line %d" (save-restriction (widen) (1+ (count-lines 1 (point))))))) (setq target-column (+ indent-base (* py-indent-offset (- (length indents) 2)))) (setq base-shifted-by (- target-column ci)))) ;; shift as needed (if (/= ci target-column) (progn (delete-horizontal-space) (indent-to target-column))) (forward-line 1)))) (set-marker end nil)) (defun py-comment-region (beg end &optional arg) "Like `comment-region' but uses double hash (`#') comment starter." (interactive "r\nP") (let ((comment-start py-block-comment-prefix)) (comment-region beg end arg))) ;; Functions for moving point (defun py-previous-statement (count) "Go to the start of the COUNTth preceding Python statement. By default, goes to the previous statement. If there is no such statement, goes to the first statement. Return count of statements left to move. `Statements' do not include blank, comment, or continuation lines." (interactive "p") ; numeric prefix arg (if (< count 0) (py-next-statement (- count)) (py-goto-initial-line) (let (start) (while (and (setq start (point)) ; always true -- side effect (> count 0) (zerop (forward-line -1)) (py-goto-statement-at-or-above)) (setq count (1- count))) (if (> count 0) (goto-char start))) count)) (defun py-next-statement (count) "Go to the start of next Python statement. If the statement at point is the i'th Python statement, goes to the start of statement i+COUNT. If there is no such statement, goes to the last statement. Returns count of statements left to move. `Statements' do not include blank, comment, or continuation lines." (interactive "p") ; numeric prefix arg (if (< count 0) (py-previous-statement (- count)) (beginning-of-line) (let (start) (while (and (setq start (point)) ; always true -- side effect (> count 0) (py-goto-statement-below)) (setq count (1- count))) (if (> count 0) (goto-char start))) count)) (defun py-goto-block-up (&optional nomark) "Move up to start of current block. Go to the statement that starts the smallest enclosing block; roughly speaking, this will be the closest preceding statement that ends with a colon and is indented less than the statement you started on. If successful, also sets the mark to the starting point. `\\[py-mark-block]' can be used afterward to mark the whole code block, if desired. If called from a program, the mark will not be set if optional argument NOMARK is not nil."