((nth 8 ppss) (if (nth 4 ppss) 'comment 'string))
((nth 1 ppss) 'paren))))
-(defsubst python-syntax-comment-or-string-p ()
- "Return non-nil if point is inside 'comment or 'string."
- (nth 8 (syntax-ppss)))
+(defsubst python-syntax-comment-or-string-p (&optional ppss)
+ "Return non-nil if PPSS is inside 'comment or 'string."
+ (nth 8 (or ppss (syntax-ppss))))
+
+(defsubst python-syntax-closing-paren-p ()
+ "Return non-nil if char after point is a closing paren."
+ (= (syntax-class (syntax-after (point)))
+ (syntax-class (string-to-syntax ")"))))
(define-obsolete-function-alias
'python-info-ppss-context #'python-syntax-context "24.3")
'python-guess-indent 'python-indent-guess-indent-offset "24.3")
(defvar python-indent-current-level 0
- "Current indentation level `python-indent-line-function' is using.")
+ "Deprecated var available for compatibility.")
(defvar python-indent-levels '(0)
- "Levels of indentation available for `python-indent-line-function'.")
+ "Deprecated var available for compatibility.")
+
+(make-obsolete-variable
+ 'python-indent-current-level
+ "The indentation API changed to avoid global state.
+The function `python-indent-calculate-levels' does not use it
+anymore. If you were defadvising it and or depended on this
+variable for indentation customizations, refactor your code to
+work on `python-indent-calculate-indentation' instead."
+ "24.5")
+
+(make-obsolete-variable
+ 'python-indent-levels
+ "The indentation API changed to avoid global state.
+The function `python-indent-calculate-levels' does not use it
+anymore. If you were defadvising it and or depended on this
+variable for indentation customizations, refactor your code to
+work on `python-indent-calculate-indentation' instead."
+ "24.5")
(defun python-indent-guess-indent-offset ()
"Guess and set `python-indent-offset' for the current buffer."
python-indent-offset)))))))
(defun python-indent-context ()
- "Get information on indentation context.
-Context information is returned with a cons with the form:
- (STATUS . START)
-
-Where status can be any of the following symbols:
-
- * after-comment: When current line might continue a comment block
- * inside-paren: If point in between (), {} or []
- * inside-string: If point is inside a string
- * after-backslash: Previous line ends in a backslash
- * after-beginning-of-block: Point is after beginning of block
- * after-line: Point is after normal line
- * dedenter-statement: Point is on a dedenter statement.
- * no-indent: Point is at beginning of buffer or other special case
-START is the buffer position where the sexp starts."
+ "Get information about the current indentation context.
+Context is returned in a cons with the form (STATUS . START).
+
+STATUS can be one of the following:
+
+keyword
+-------
+
+:after-comment
+ - Point is after a comment line.
+ - START is the position of the \"#\" character.
+:inside-string
+ - Point is inside string.
+ - START is the position of the first quote that starts it.
+:no-indent
+ - No possible indentation case matches.
+ - START is always zero.
+
+:inside-paren
+ - Fallback case when point is inside paren.
+ - START is the first non space char position *after* the open paren.
+:inside-paren-at-closing-nested-paren
+ - Point is on a line that contains a nested paren closer.
+ - START is the position of the open paren it closes.
+:inside-paren-at-closing-paren
+ - Point is on a line that contains a paren closer.
+ - START is the position of the open paren.
+:inside-paren-newline-start
+ - Point is inside a paren with items starting in their own line.
+ - START is the position of the open paren.
+:inside-paren-newline-start-from-block
+ - Point is inside a paren with items starting in their own line
+ from a block start.
+ - START is the position of the open paren.
+
+:after-backslash
+ - Fallback case when point is after backslash.
+ - START is the char after the position of the backslash.
+:after-backslash-assignment-continuation
+ - Point is after a backslashed assignment.
+ - START is the char after the position of the backslash.
+:after-backslash-block-continuation
+ - Point is after a backslashed block continuation.
+ - START is the char after the position of the backslash.
+:after-backslash-dotted-continuation
+ - Point is after a backslashed dotted continuation. Previous
+ line must contain a dot to align with.
+ - START is the char after the position of the backslash.
+:after-backslash-first-line
+ - First line following a backslashed continuation.
+ - START is the char after the position of the backslash.
+
+:after-block-end
+ - Point is after a line containing a block ender.
+ - START is the position where the ender starts.
+:after-block-start
+ - Point is after a line starting a block.
+ - START is the position where the block starts.
+:after-line
+ - Point is after a simple line.
+ - START is the position where the previous line starts.
+:at-dedenter-block-start
+ - Point is on a line starting a dedenter block.
+ - START is the position where the dedenter block starts."
(save-restriction
(widen)
- (let ((ppss (save-excursion (beginning-of-line) (syntax-ppss)))
- (start))
- (cons
- (cond
- ;; Beginning of buffer
- ((save-excursion
- (goto-char (line-beginning-position))
- (bobp))
- 'no-indent)
- ;; Comment continuation
- ((save-excursion
- (when (and
- (or
- (python-info-current-line-comment-p)
- (python-info-current-line-empty-p))
- (progn
- (forward-comment -1)
- (python-info-current-line-comment-p)))
- (setq start (point))
- 'after-comment)))
- ;; Inside string
- ((setq start (python-syntax-context 'string ppss))
- 'inside-string)
- ;; Inside a paren
- ((setq start (python-syntax-context 'paren ppss))
- 'inside-paren)
- ;; After backslash
- ((setq start (when (not (or (python-syntax-context 'string ppss)
- (python-syntax-context 'comment ppss)))
- (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
- (when (progn
- (back-to-indentation)
- (python-util-forward-comment -1)
- (equal (char-before) ?:))
- ;; Move to the first block start that's not in within
- ;; a string, comment or paren and that's not a
- ;; continuation line.
- (while (and (re-search-backward
- (python-rx block-start) nil t)
- (or
- (python-syntax-context-type)
- (python-info-continuation-line-p))))
- (when (looking-at (python-rx block-start))
- (point-marker)))))
- 'after-beginning-of-block)
- ((when (setq start (python-info-dedenter-statement-p))
- 'dedenter-statement))
- ;; After normal line
- ((setq start (save-excursion
+ (let ((ppss (save-excursion
+ (beginning-of-line)
+ (syntax-ppss))))
+ (cond
+ ;; Beginning of buffer.
+ ((= (line-number-at-pos) 1)
+ (cons :no-indent 0))
+ ;; Comment continuation (maybe).
+ ((save-excursion
+ (when (and
+ (or
+ (python-info-current-line-comment-p)
+ (python-info-current-line-empty-p))
+ (forward-comment -1)
+ (python-info-current-line-comment-p))
+ (cons :after-comment (point)))))
+ ;; Inside a string.
+ ((let ((start (python-syntax-context 'string ppss)))
+ (when start
+ (cons :inside-string start))))
+ ;; Inside a paren.
+ ((let* ((start (python-syntax-context 'paren ppss))
+ (starts-in-newline
+ (when start
+ (save-excursion
+ (goto-char start)
+ (forward-char)
+ (not
+ (= (line-number-at-pos)
+ (progn
+ (python-util-forward-comment)
+ (line-number-at-pos))))))))
+ (when start
+ (cond
+ ;; Current line only holds the closing paren.
+ ((save-excursion
+ (skip-syntax-forward " ")
+ (when (and (python-syntax-closing-paren-p)
+ (progn
+ (forward-char 1)
+ (not (python-syntax-context 'paren))))
+ (cons :inside-paren-at-closing-paren start))))
+ ;; Current line only holds a closing paren for nested.
+ ((save-excursion
+ (back-to-indentation)
+ (python-syntax-closing-paren-p))
+ (cons :inside-paren-at-closing-nested-paren start))
+ ;; This line starts from a opening block in its own line.
+ ((save-excursion
+ (goto-char start)
+ (when (and
+ starts-in-newline
+ (save-excursion
+ (back-to-indentation)
+ (looking-at (python-rx block-start))))
+ (cons
+ :inside-paren-newline-start-from-block start))))
+ (starts-in-newline
+ (cons :inside-paren-newline-start start))
+ ;; General case.
+ (t (cons :inside-paren
+ (save-excursion
+ (goto-char (1+ start))
+ (skip-syntax-forward "(" 1)
+ (skip-syntax-forward " ")
+ (point))))))))
+ ;; After backslash.
+ ((let ((start (when (not (python-syntax-comment-or-string-p ppss))
+ (python-info-line-ends-backslash-p
+ (1- (line-number-at-pos))))))
+ (when start
+ (cond
+ ;; Continuation of dotted expression.
+ ((save-excursion
+ (back-to-indentation)
+ (when (eq (char-after) ?\.)
+ ;; Move point back until it's not inside a paren.
+ (while (prog2
+ (forward-line -1)
+ (and (not (bobp))
+ (python-syntax-context 'paren))))
+ (goto-char (line-end-position))
+ (while (and (search-backward
+ "." (line-beginning-position) t)
+ (python-syntax-context-type)))
+ ;; Ensure previous statement has dot to align with.
+ (when (and (eq (char-after) ?\.)
+ (not (python-syntax-context-type)))
+ (cons :after-backslash-dotted-continuation (point))))))
+ ;; Continuation of block definition.
+ ((let ((block-continuation-start
+ (python-info-block-continuation-line-p)))
+ (when block-continuation-start
+ (save-excursion
+ (goto-char block-continuation-start)
+ (re-search-forward
+ (python-rx block-start (* space))
+ (line-end-position) t)
+ (cons :after-backslash-block-continuation (point))))))
+ ;; Continuation of assignment.
+ ((let ((assignment-continuation-start
+ (python-info-assignment-continuation-line-p)))
+ (when assignment-continuation-start
+ (save-excursion
+ (goto-char assignment-continuation-start)
+ (cons :after-backslash-assignment-continuation (point))))))
+ ;; First line after backslash continuation start.
+ ((save-excursion
+ (goto-char start)
+ (when (or (= (line-number-at-pos) 1)
+ (not (python-info-beginning-of-backslash
+ (1- (line-number-at-pos)))))
+ (cons :after-backslash-first-line start))))
+ ;; General case.
+ (t (cons :after-backslash start))))))
+ ;; After beginning of block.
+ ((let ((start (save-excursion
+ (back-to-indentation)
+ (python-util-forward-comment -1)
+ (when (equal (char-before) ?:)
+ (python-nav-beginning-of-block)))))
+ (when start
+ (cons :after-block-start start))))
+ ;; At dedenter statement.
+ ((let ((start (python-info-dedenter-statement-p)))
+ (when start
+ (cons :at-dedenter-block-start start))))
+ ;; After normal line.
+ ((let ((start (save-excursion
(back-to-indentation)
- (skip-chars-backward (rx (or whitespace ?\n)))
+ (skip-chars-backward " \t\n")
(python-nav-beginning-of-statement)
- (point-marker)))
- 'after-line)
- ;; Do not indent
- (t 'no-indent))
- start))))
-
-(defun python-indent-calculate-indentation ()
- "Calculate correct indentation offset for the current line."
- (let* ((indentation-context (python-indent-context))
- (context-status (car indentation-context))
- (context-start (cdr indentation-context)))
- (save-restriction
- (widen)
- (save-excursion
- (pcase context-status
- (`no-indent 0)
- (`after-comment
- (goto-char context-start)
- (current-indentation))
- ;; When point is after beginning of block just add one level
- ;; of indentation relative to the context-start
- (`after-beginning-of-block
- (goto-char context-start)
- (+ (current-indentation) python-indent-offset))
- ;; When after a simple line just use previous line
- ;; indentation.
- (`after-line
- (let* ((pair (save-excursion
- (goto-char context-start)
- (cons
- (current-indentation)
- (python-info-beginning-of-block-p))))
- (context-indentation (car pair))
- ;; TODO: Separate block enders into its own case.
- (adjustment
- (if (save-excursion
- (python-util-forward-comment -1)
- (python-nav-beginning-of-statement)
- (looking-at (python-rx block-ender)))
- python-indent-offset
- 0)))
- (- context-indentation adjustment)))
- ;; When point is on a dedenter statement, search for the
- ;; opening block that corresponds to it and use its
- ;; indentation. If no opening block is found just remove
- ;; indentation as this is an invalid python file.
- (`dedenter-statement
- (let ((block-start-point
- (python-info-dedenter-opening-block-position)))
- (save-excursion
- (if (not block-start-point)
- 0
- (goto-char block-start-point)
- (current-indentation)))))
- ;; When inside of a string, do nothing. just use the current
- ;; indentation. XXX: perhaps it would be a good idea to
- ;; invoke standard text indentation here
- (`inside-string
- (goto-char context-start)
- (current-indentation))
- ;; After backslash we have several possibilities.
- (`after-backslash
- (cond
- ;; Check if current line is a dot continuation. For this
- ;; the current line must start with a dot and previous
- ;; line must contain a dot too.
- ((save-excursion
- (back-to-indentation)
- (when (looking-at "\\.")
- ;; If after moving one line back point is inside a paren it
- ;; needs to move back until it's not anymore
- (while (prog2
- (forward-line -1)
- (and (not (bobp))
- (python-syntax-context 'paren))))
- (goto-char (line-end-position))
- (while (and (re-search-backward
- "\\." (line-beginning-position) t)
- (python-syntax-context-type)))
- (if (and (looking-at "\\.")
- (not (python-syntax-context-type)))
- ;; The indentation is the same column of the
- ;; first matching dot that's not inside a
- ;; comment, a string or a paren
- (current-column)
- ;; No dot found on previous line, just add another
- ;; indentation level.
- (+ (current-indentation) python-indent-offset)))))
- ;; Check if prev line is a block continuation
- ((let ((block-continuation-start
- (python-info-block-continuation-line-p)))
- (when block-continuation-start
- ;; If block-continuation-start is set jump to that
- ;; marker and use first column after the block start
- ;; as indentation value.
- (goto-char block-continuation-start)
- (re-search-forward
- (python-rx block-start (* space))
- (line-end-position) t)
- (current-column))))
- ;; Check if current line is an assignment continuation
- ((let ((assignment-continuation-start
- (python-info-assignment-continuation-line-p)))
- (when assignment-continuation-start
- ;; If assignment-continuation is set jump to that
- ;; marker and use first column after the assignment
- ;; operator as indentation value.
- (goto-char assignment-continuation-start)
- (current-column))))
- (t
- (forward-line -1)
- (goto-char (python-info-beginning-of-backslash))
- (if (save-excursion
- (and
- (forward-line -1)
- (goto-char
- (or (python-info-beginning-of-backslash) (point)))
- (python-info-line-ends-backslash-p)))
- ;; The two previous lines ended in a backslash so we must
- ;; respect previous line indentation.
- (current-indentation)
- ;; What happens here is that we are dealing with the second
- ;; line of a backslash continuation, in that case we just going
- ;; to add one indentation level.
- (+ (current-indentation) python-indent-offset)))))
- ;; When inside a paren there's a need to handle nesting
- ;; correctly
- (`inside-paren
- (cond
- ;; If current line closes the outermost open paren use the
- ;; current indentation of the context-start line.
- ((save-excursion
- (skip-syntax-forward "\s" (line-end-position))
- (when (and (looking-at (regexp-opt '(")" "]" "}")))
- (progn
- (forward-char 1)
- (not (python-syntax-context 'paren))))
- (goto-char context-start)
- (current-indentation))))
- ;; If open paren is contained on a line by itself add another
- ;; indentation level, else look for the first word after the
- ;; opening paren and use it's column position as indentation
- ;; level.
- ((let* ((content-starts-in-newline)
- (indent
- (save-excursion
- (if (setq content-starts-in-newline
- (progn
- (goto-char context-start)
- (forward-char)
- (save-restriction
- (narrow-to-region
- (line-beginning-position)
- (line-end-position))
- (python-util-forward-comment))
- (looking-at "$")))
- (+ (current-indentation) python-indent-offset)
- (current-column)))))
- ;; Adjustments
- (cond
- ;; If current line closes a nested open paren de-indent one
- ;; level.
- ((progn
- (back-to-indentation)
- (looking-at (regexp-opt '(")" "]" "}"))))
- (- indent python-indent-offset))
- ;; If the line of the opening paren that wraps the current
- ;; line starts a block add another level of indentation to
- ;; follow new pep8 recommendation. See: http://ur1.ca/5rojx
- ((save-excursion
- (when (and content-starts-in-newline
- (progn
- (goto-char context-start)
- (back-to-indentation)
- (looking-at (python-rx block-start))))
- (+ indent python-indent-offset))))
- (t indent)))))))))))
-
-(defun python-indent-calculate-levels ()
- "Calculate `python-indent-levels' and reset `python-indent-current-level'."
- (if (or (python-info-continuation-line-p)
- (not (python-info-dedenter-statement-p)))
- ;; XXX: This asks for a refactor. Even if point is on a
- ;; dedenter statement, it could be multiline and in that case
- ;; the continuation lines should be indented with normal rules.
- (let* ((indentation (python-indent-calculate-indentation))
- (remainder (% indentation python-indent-offset))
- (steps (/ (- indentation remainder) python-indent-offset)))
- (setq python-indent-levels (list 0))
- (dotimes (step steps)
- (push (* python-indent-offset (1+ step)) python-indent-levels))
- (when (not (eq 0 remainder))
- (push (+ (* python-indent-offset steps) remainder) python-indent-levels)))
- (setq python-indent-levels
- (or
- (mapcar (lambda (pos)
- (save-excursion
- (goto-char pos)
- (current-indentation)))
- (python-info-dedenter-opening-block-positions))
- (list 0))))
- (setq python-indent-current-level (1- (length python-indent-levels))
- python-indent-levels (nreverse python-indent-levels)))
-
-(defun python-indent-toggle-levels ()
- "Toggle `python-indent-current-level' over `python-indent-levels'."
- (setq python-indent-current-level (1- python-indent-current-level))
- (when (< python-indent-current-level 0)
- (setq python-indent-current-level (1- (length python-indent-levels)))))
-
-(defun python-indent-line (&optional force-toggle)
+ (point))))
+ (when start
+ (if (save-excursion
+ (python-util-forward-comment -1)
+ (python-nav-beginning-of-statement)
+ (looking-at (python-rx block-ender)))
+ (cons :after-block-end start)
+ (cons :after-line start)))))
+ ;; Default case: do not indent.
+ (t (cons :no-indent 0))))))
+
+(defun python-indent--calculate-indentation ()
+ "Internal implementation of `python-indent-calculate-indentation'.
+May return an integer for the maximum possible indentation at
+current context or a list of integers. The latter case is only
+happening for :at-dedenter-block-start context since the
+possibilities can be narrowed to especific indentation points."
+ (save-restriction
+ (widen)
+ (save-excursion
+ (pcase (python-indent-context)
+ (`(:no-indent . ,_) 0)
+ (`(,(or :after-line
+ :after-comment
+ :inside-string
+ :after-backslash
+ :inside-paren-at-closing-paren
+ :inside-paren-at-closing-nested-paren) . ,start)
+ ;; Copy previous indentation.
+ (goto-char start)
+ (current-indentation))
+ (`(,(or :after-block-start
+ :after-backslash-first-line
+ :inside-paren-newline-start) . ,start)
+ ;; Add one indentation level.
+ (goto-char start)
+ (+ (current-indentation) python-indent-offset))
+ (`(,(or :inside-paren
+ :after-backslash-block-continuation
+ :after-backslash-assignment-continuation
+ :after-backslash-dotted-continuation) . ,start)
+ ;; Use the column given by the context.
+ (goto-char start)
+ (current-column))
+ (`(:after-block-end . ,start)
+ ;; Subtract one indentation level.
+ (goto-char start)
+ (- (current-indentation) python-indent-offset))
+ (`(:at-dedenter-block-start . ,_)
+ ;; List all possible indentation levels from opening blocks.
+ (let ((opening-block-start-points
+ (python-info-dedenter-opening-block-positions)))
+ (if (not opening-block-start-points)
+ 0 ; if not found default to first column
+ (mapcar (lambda (pos)
+ (save-excursion
+ (goto-char pos)
+ (current-indentation)))
+ opening-block-start-points))))
+ (`(,(or :inside-paren-newline-start-from-block) . ,start)
+ ;; Add two indentation levels to make the suite stand out.
+ (goto-char start)
+ (+ (current-indentation) (* python-indent-offset 2)))))))
+
+(defun python-indent--calculate-levels (indentation)
+ "Calculate levels list given INDENTATION.
+Argument INDENTATION can either be an integer or a list of
+integers. Levels are returned in ascending order, and in the
+case INDENTATION is a list, this order is enforced."
+ (if (listp indentation)
+ (sort (copy-sequence indentation) #'<)
+ (let* ((remainder (% indentation python-indent-offset))
+ (steps (/ (- indentation remainder) python-indent-offset))
+ (levels (mapcar (lambda (step)
+ (* python-indent-offset step))
+ (number-sequence steps 0 -1))))
+ (reverse
+ (if (not (zerop remainder))
+ (cons indentation levels)
+ levels)))))
+
+(defun python-indent--previous-level (levels indentation)
+ "Return previous level from LEVELS relative to INDENTATION."
+ (let* ((levels (sort (copy-sequence levels) #'>))
+ (default (car levels)))
+ (catch 'return
+ (dolist (level levels)
+ (when (funcall #'< level indentation)
+ (throw 'return level)))
+ default)))
+
+(defun python-indent-calculate-indentation (&optional previous)
+ "Calculate indentation.
+Get indentation of PREVIOUS level when argument is non-nil.
+Return the max level of the cycle when indentation reaches the
+minimum."
+ (let* ((indentation (python-indent--calculate-indentation))
+ (levels (python-indent--calculate-levels indentation)))
+ (if previous
+ (python-indent--previous-level levels (current-indentation))
+ (apply #'max levels))))
+
+(defun python-indent-line (&optional previous)
"Internal implementation of `python-indent-line-function'.
-Uses the offset calculated in
-`python-indent-calculate-indentation' and available levels
-indicated by the variable `python-indent-levels' to set the
-current indentation.
+Use the PREVIOUS level when argument is non-nil, otherwise indent
+to the maxium available level. When indentation is the minimum
+possible and PREVIOUS is non-nil, cycle back to the maximum
+level."
+ (let ((follow-indentation-p
+ ;; Check if point is within indentation.
+ (and (<= (line-beginning-position) (point))
+ (>= (+ (line-beginning-position)
+ (current-indentation))
+ (point)))))
+ (save-excursion
+ (indent-line-to
+ (python-indent-calculate-indentation previous))
+ (python-info-dedenter-opening-block-message))
+ (when follow-indentation-p
+ (back-to-indentation))))
-When the variable `last-command' is equal to one of the symbols
-inside `python-indent-trigger-commands' or FORCE-TOGGLE is
-non-nil it cycles levels indicated in the variable
-`python-indent-levels' by setting the current level in the
-variable `python-indent-current-level'.
-
-When the variable `last-command' is not equal to one of the
-symbols inside `python-indent-trigger-commands' and FORCE-TOGGLE
-is nil it calculates possible indentation levels and saves them
-in the variable `python-indent-levels'. Afterwards it sets the
-variable `python-indent-current-level' correctly so offset is
-equal to
- (nth python-indent-current-level python-indent-levels)"
- (or
- (and (or (and (memq this-command python-indent-trigger-commands)
- (eq last-command this-command))
- force-toggle)
- (not (equal python-indent-levels '(0)))
- (or (python-indent-toggle-levels) t))
- (python-indent-calculate-levels))
- (let* ((starting-pos (point-marker))
- (indent-ending-position
- (+ (line-beginning-position) (current-indentation)))
- (follow-indentation-p
- (or (bolp)
- (and (<= (line-beginning-position) starting-pos)
- (>= indent-ending-position starting-pos))))
- (next-indent (nth python-indent-current-level python-indent-levels)))
- (unless (= next-indent (current-indentation))
- (beginning-of-line)
- (delete-horizontal-space)
- (indent-to next-indent)
- (goto-char starting-pos))
- (and follow-indentation-p (back-to-indentation)))
- (python-info-dedenter-opening-block-message))
+(defun python-indent-calculate-levels ()
+ "Return possible indentation levels."
+ (python-indent--calculate-levels
+ (python-indent--calculate-indentation)))
(defun python-indent-line-function ()
"`indent-line-function' for Python mode.
-See `python-indent-line' for details."
- (python-indent-line))
+When the variable `last-command' is equal to one of the symbols
+inside `python-indent-trigger-commands' it cycles possible
+indentation levels from right to left."
+ (python-indent-line
+ (and (memq this-command python-indent-trigger-commands)
+ (eq last-command this-command))))
(defun python-indent-dedent-line ()
"De-indent current line."
(interactive "*")
- (when (and (not (python-syntax-comment-or-string-p))
- (<= (point-marker) (save-excursion
- (back-to-indentation)
- (point-marker)))
- (> (current-column) 0))
- (python-indent-line t)
- t))
+ (when (and (not (bolp))
+ (not (python-syntax-comment-or-string-p))
+ (= (+ (line-beginning-position)
+ (current-indentation))
+ (point)))
+ (python-indent-line t)
+ t))
(defun python-indent-dedent-line-backspace (arg)
"De-indent current line.
Argument ARG is passed to `backward-delete-char-untabify' when
point is not in between the indentation."
(interactive "*p")
- (when (not (python-indent-dedent-line))
+ (unless (python-indent-dedent-line)
(backward-delete-char-untabify arg)))
+
(put 'python-indent-dedent-line-backspace 'delete-selection 'supersede)
(defun python-indent-region (start end)
foo = long_function_name(var_one, var_two,
var_three, var_four)
"
- (should (eq (car (python-indent-context)) 'no-indent))
+ (should (eq (car (python-indent-context)) :no-indent))
(should (= (python-indent-calculate-indentation) 0))
(python-tests-look-at "foo = long_function_name(var_one, var_two,")
- (should (eq (car (python-indent-context)) 'after-line))
+ (should (eq (car (python-indent-context)) :after-line))
(should (= (python-indent-calculate-indentation) 0))
(python-tests-look-at "var_three, var_four)")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren))
(should (= (python-indent-calculate-indentation) 25))))
(ert-deftest python-indent-pep8-2 ()
var_four):
print (var_one)
"
- (should (eq (car (python-indent-context)) 'no-indent))
+ (should (eq (car (python-indent-context)) :no-indent))
(should (= (python-indent-calculate-indentation) 0))
(python-tests-look-at "def long_function_name(")
- (should (eq (car (python-indent-context)) 'after-line))
+ (should (eq (car (python-indent-context)) :after-line))
(should (= (python-indent-calculate-indentation) 0))
(python-tests-look-at "var_one, var_two, var_three,")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context))
+ :inside-paren-newline-start-from-block))
(should (= (python-indent-calculate-indentation) 8))
(python-tests-look-at "var_four):")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context))
+ :inside-paren-newline-start-from-block))
(should (= (python-indent-calculate-indentation) 8))
(python-tests-look-at "print (var_one)")
- (should (eq (car (python-indent-context)) 'after-beginning-of-block))
+ (should (eq (car (python-indent-context))
+ :after-block-start))
(should (= (python-indent-calculate-indentation) 4))))
(ert-deftest python-indent-pep8-3 ()
var_one, var_two,
var_three, var_four)
"
- (should (eq (car (python-indent-context)) 'no-indent))
+ (should (eq (car (python-indent-context)) :no-indent))
(should (= (python-indent-calculate-indentation) 0))
(python-tests-look-at "foo = long_function_name(")
- (should (eq (car (python-indent-context)) 'after-line))
+ (should (eq (car (python-indent-context)) :after-line))
(should (= (python-indent-calculate-indentation) 0))
(python-tests-look-at "var_one, var_two,")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
(should (= (python-indent-calculate-indentation) 4))
(python-tests-look-at "var_three, var_four)")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
(should (= (python-indent-calculate-indentation) 4))))
+(ert-deftest python-indent-base-case ()
+ "Check base case does not trigger errors."
+ (python-tests-with-temp-buffer
+ "
+
+"
+ (goto-char (point-min))
+ (should (eq (car (python-indent-context)) :no-indent))
+ (should (= (python-indent-calculate-indentation) 0))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :after-line))
+ (should (= (python-indent-calculate-indentation) 0))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :after-line))
+ (should (= (python-indent-calculate-indentation) 0))))
+
(ert-deftest python-indent-after-comment-1 ()
"The most simple after-comment case that shouldn't fail."
(python-tests-with-temp-buffer
# with the exception with which the first child failed.
"
(python-tests-look-at "# We only complete")
- (should (eq (car (python-indent-context)) 'after-line))
+ (should (eq (car (python-indent-context)) :after-block-end))
(should (= (python-indent-calculate-indentation) 8))
(python-tests-look-at "# terminal state")
- (should (eq (car (python-indent-context)) 'after-comment))
+ (should (eq (car (python-indent-context)) :after-comment))
(should (= (python-indent-calculate-indentation) 8))
(python-tests-look-at "# with the exception")
- (should (eq (car (python-indent-context)) 'after-comment))
+ (should (eq (car (python-indent-context)) :after-comment))
;; This one indents relative to previous block, even given the fact
;; that it was under-indented.
(should (= (python-indent-calculate-indentation) 4))
(python-tests-look-at "# terminal state" -1)
;; It doesn't hurt to check again.
- (should (eq (car (python-indent-context)) 'after-comment))
+ (should (eq (car (python-indent-context)) :after-comment))
(python-indent-line)
(should (= (current-indentation) 8))
(python-tests-look-at "# with the exception")
- (should (eq (car (python-indent-context)) 'after-comment))
+ (should (eq (car (python-indent-context)) :after-comment))
;; Now everything should be lined up.
(should (= (python-indent-calculate-indentation) 8))))
# yeah, that.
"
(python-tests-look-at "# I don't do much")
- (should (eq (car (python-indent-context)) 'after-beginning-of-block))
+ (should (eq (car (python-indent-context)) :after-block-start))
(should (= (python-indent-calculate-indentation) 4))
(python-tests-look-at "return arg")
;; Comment here just gets ignored, this line is not a comment so
;; the rules won't apply here.
- (should (eq (car (python-indent-context)) 'after-beginning-of-block))
+ (should (eq (car (python-indent-context)) :after-block-start))
(should (= (python-indent-calculate-indentation) 4))
(python-tests-look-at "# This comment is badly")
- (should (eq (car (python-indent-context)) 'after-line))
+ (should (eq (car (python-indent-context)) :after-block-end))
;; The return keyword moves indentation backwards 4 spaces, but
;; let's assume this comment was placed there because the user
;; wanted to (manually adding spaces or whatever).
(should (= (python-indent-calculate-indentation) 0))
(python-tests-look-at "# but we won't mess")
- (should (eq (car (python-indent-context)) 'after-comment))
+ (should (eq (car (python-indent-context)) :after-comment))
(should (= (python-indent-calculate-indentation) 4))
;; Behave the same for blank lines: potentially a comment.
(forward-line 1)
- (should (eq (car (python-indent-context)) 'after-comment))
+ (should (eq (car (python-indent-context)) :after-comment))
(should (= (python-indent-calculate-indentation) 4))
(python-tests-look-at "now_we_do_mess")
;; Here is where comment indentation starts to get ignored and
;; where the user can't freely indent anymore.
- (should (eq (car (python-indent-context)) 'after-line))
+ (should (eq (car (python-indent-context)) :after-block-end))
(should (= (python-indent-calculate-indentation) 0))
(python-tests-look-at "# yeah, that.")
- (should (eq (car (python-indent-context)) 'after-line))
+ (should (eq (car (python-indent-context)) :after-line))
(should (= (python-indent-calculate-indentation) 0))))
(ert-deftest python-indent-inside-paren-1 ()
}
"
(python-tests-look-at "data = {")
- (should (eq (car (python-indent-context)) 'after-line))
+ (should (eq (car (python-indent-context)) :after-line))
(should (= (python-indent-calculate-indentation) 0))
(python-tests-look-at "'key':")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
(should (= (python-indent-calculate-indentation) 4))
(python-tests-look-at "{")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
(should (= (python-indent-calculate-indentation) 4))
(python-tests-look-at "'objlist': [")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
(should (= (python-indent-calculate-indentation) 8))
(python-tests-look-at "{")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
(should (= (python-indent-calculate-indentation) 12))
(python-tests-look-at "'pk': 1,")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
(should (= (python-indent-calculate-indentation) 16))
(python-tests-look-at "'name': 'first',")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
(should (= (python-indent-calculate-indentation) 16))
(python-tests-look-at "},")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context))
+ :inside-paren-at-closing-nested-paren))
(should (= (python-indent-calculate-indentation) 12))
(python-tests-look-at "{")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
(should (= (python-indent-calculate-indentation) 12))
(python-tests-look-at "'pk': 2,")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
(should (= (python-indent-calculate-indentation) 16))
(python-tests-look-at "'name': 'second',")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
(should (= (python-indent-calculate-indentation) 16))
(python-tests-look-at "}")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context))
+ :inside-paren-at-closing-nested-paren))
(should (= (python-indent-calculate-indentation) 12))
(python-tests-look-at "]")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context))
+ :inside-paren-at-closing-nested-paren))
(should (= (python-indent-calculate-indentation) 8))
(python-tests-look-at "}")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context))
+ :inside-paren-at-closing-nested-paren))
(should (= (python-indent-calculate-indentation) 4))
(python-tests-look-at "}")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren-at-closing-paren))
(should (= (python-indent-calculate-indentation) 0))))
(ert-deftest python-indent-inside-paren-2 ()
}}
"
(python-tests-look-at "data = {")
- (should (eq (car (python-indent-context)) 'after-line))
+ (should (eq (car (python-indent-context)) :after-line))
(should (= (python-indent-calculate-indentation) 0))
(python-tests-look-at "'objlist': [")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
(should (= (python-indent-calculate-indentation) 4))
(python-tests-look-at "{'pk': 1,")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
(should (= (python-indent-calculate-indentation) 8))
(python-tests-look-at "'name': 'first'},")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren))
(should (= (python-indent-calculate-indentation) 9))
(python-tests-look-at "{'pk': 2,")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
(should (= (python-indent-calculate-indentation) 8))
(python-tests-look-at "'name': 'second'}")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren))
(should (= (python-indent-calculate-indentation) 9))
(python-tests-look-at "]")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context))
+ :inside-paren-at-closing-nested-paren))
(should (= (python-indent-calculate-indentation) 4))
(python-tests-look-at "}}")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context))
+ :inside-paren-at-closing-nested-paren))
(should (= (python-indent-calculate-indentation) 0))
(python-tests-look-at "}")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren-at-closing-paren))
(should (= (python-indent-calculate-indentation) 0))))
+(ert-deftest python-indent-inside-paren-3 ()
+ "The simplest case possible."
+ (python-tests-with-temp-buffer
+ "
+data = ('these',
+ 'are',
+ 'the',
+ 'tokens')
+"
+ (python-tests-look-at "data = ('these',")
+ (should (eq (car (python-indent-context)) :after-line))
+ (should (= (python-indent-calculate-indentation) 0))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :inside-paren))
+ (should (= (python-indent-calculate-indentation) 8))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :inside-paren))
+ (should (= (python-indent-calculate-indentation) 8))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :inside-paren))
+ (should (= (python-indent-calculate-indentation) 8))))
+
+(ert-deftest python-indent-inside-paren-4 ()
+ "Respect indentation of first column."
+ (python-tests-with-temp-buffer
+ "
+data = [ [ 'these', 'are'],
+ ['the', 'tokens' ] ]
+"
+ (python-tests-look-at "data = [ [ 'these', 'are'],")
+ (should (eq (car (python-indent-context)) :after-line))
+ (should (= (python-indent-calculate-indentation) 0))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :inside-paren))
+ (should (= (python-indent-calculate-indentation) 9))))
+
+(ert-deftest python-indent-inside-paren-5 ()
+ "Test when :inside-paren initial parens are skipped in context start."
+ (python-tests-with-temp-buffer
+ "
+while ((not some_condition) and
+ another_condition):
+ do_something_interesting(
+ with_some_arg)
+"
+ (python-tests-look-at "while ((not some_condition) and")
+ (should (eq (car (python-indent-context)) :after-line))
+ (should (= (python-indent-calculate-indentation) 0))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :inside-paren))
+ (should (= (python-indent-calculate-indentation) 7))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :after-block-start))
+ (should (= (python-indent-calculate-indentation) 4))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
+ (should (= (python-indent-calculate-indentation) 8))))
+
+(ert-deftest python-indent-inside-paren-6 ()
+ "This should be aligned.."
+ (python-tests-with-temp-buffer
+ "
+CHOICES = (('some', 'choice'),
+ ('another', 'choice'),
+ ('more', 'choices'))
+"
+ (python-tests-look-at "CHOICES = (('some', 'choice'),")
+ (should (eq (car (python-indent-context)) :after-line))
+ (should (= (python-indent-calculate-indentation) 0))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :inside-paren))
+ (should (= (python-indent-calculate-indentation) 11))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :inside-paren))
+ (should (= (python-indent-calculate-indentation) 11))))
+
(ert-deftest python-indent-after-block-1 ()
"The most simple after-block case that shouldn't fail."
(python-tests-with-temp-buffer
"
def foo(a, b, c=True):
"
- (should (eq (car (python-indent-context)) 'no-indent))
+ (should (eq (car (python-indent-context)) :no-indent))
(should (= (python-indent-calculate-indentation) 0))
(goto-char (point-max))
- (should (eq (car (python-indent-context)) 'after-beginning-of-block))
+ (should (eq (car (python-indent-context)) :after-block-start))
(should (= (python-indent-calculate-indentation) 4))))
(ert-deftest python-indent-after-block-2 ()
}):
"
(goto-char (point-max))
- (should (eq (car (python-indent-context)) 'after-beginning-of-block))
+ (should (eq (car (python-indent-context)) :after-block-start))
(should (= (python-indent-calculate-indentation) 4))))
+(ert-deftest python-indent-after-block-3 ()
+ "A weird (malformed) sample, usually found in python shells."
+ (python-tests-with-temp-buffer
+ "
+In [1]:
+def func():
+pass
+
+In [2]:
+something
+"
+ (python-tests-look-at "pass")
+ (should (eq (car (python-indent-context)) :after-block-start))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "something")
+ (end-of-line)
+ (should (eq (car (python-indent-context)) :after-line))
+ (should (= (python-indent-calculate-indentation) 0))))
+
(ert-deftest python-indent-after-backslash-1 ()
"The most common case."
(python-tests-with-temp-buffer
something_4, something_5
"
(python-tests-look-at "from foo.bar.baz import something, something_1")
- (should (eq (car (python-indent-context)) 'after-line))
+ (should (eq (car (python-indent-context)) :after-line))
(should (= (python-indent-calculate-indentation) 0))
(python-tests-look-at "something_2 something_3,")
- (should (eq (car (python-indent-context)) 'after-backslash))
+ (should (eq (car (python-indent-context)) :after-backslash-first-line))
(should (= (python-indent-calculate-indentation) 4))
(python-tests-look-at "something_4, something_5")
- (should (eq (car (python-indent-context)) 'after-backslash))
+ (should (eq (car (python-indent-context)) :after-backslash))
(should (= (python-indent-calculate-indentation) 4))
(goto-char (point-max))
- (should (eq (car (python-indent-context)) 'after-line))
+ (should (eq (car (python-indent-context)) :after-line))
(should (= (python-indent-calculate-indentation) 0))))
(ert-deftest python-indent-after-backslash-2 ()
.values_list()
"
(python-tests-look-at "objects = Thing.objects.all()")
- (should (eq (car (python-indent-context)) 'after-line))
+ (should (eq (car (python-indent-context)) :after-line))
(should (= (python-indent-calculate-indentation) 0))
(python-tests-look-at ".filter(")
- (should (eq (car (python-indent-context)) 'after-backslash))
+ (should (eq (car (python-indent-context))
+ :after-backslash-dotted-continuation))
(should (= (python-indent-calculate-indentation) 23))
(python-tests-look-at "type='toy',")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
(should (= (python-indent-calculate-indentation) 27))
(python-tests-look-at "status='bought'")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
(should (= (python-indent-calculate-indentation) 27))
(python-tests-look-at ") \\\\")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren-at-closing-paren))
(should (= (python-indent-calculate-indentation) 23))
(python-tests-look-at ".aggregate(")
- (should (eq (car (python-indent-context)) 'after-backslash))
+ (should (eq (car (python-indent-context))
+ :after-backslash-dotted-continuation))
(should (= (python-indent-calculate-indentation) 23))
(python-tests-look-at "Sum('amount')")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren-newline-start))
(should (= (python-indent-calculate-indentation) 27))
(python-tests-look-at ") \\\\")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren-at-closing-paren))
(should (= (python-indent-calculate-indentation) 23))
(python-tests-look-at ".values_list()")
- (should (eq (car (python-indent-context)) 'after-backslash))
+ (should (eq (car (python-indent-context))
+ :after-backslash-dotted-continuation))
(should (= (python-indent-calculate-indentation) 23))
(forward-line 1)
- (should (eq (car (python-indent-context)) 'after-line))
+ (should (eq (car (python-indent-context)) :after-line))
(should (= (python-indent-calculate-indentation) 0))))
+(ert-deftest python-indent-after-backslash-3 ()
+ "Backslash continuation from block start."
+ (python-tests-with-temp-buffer
+ "
+with open('/path/to/some/file/you/want/to/read') as file_1, \\\\
+ open('/path/to/some/file/being/written', 'w') as file_2:
+ file_2.write(file_1.read())
+"
+ (python-tests-look-at
+ "with open('/path/to/some/file/you/want/to/read') as file_1, \\\\")
+ (should (eq (car (python-indent-context)) :after-line))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at
+ "open('/path/to/some/file/being/written', 'w') as file_2")
+ (should (eq (car (python-indent-context))
+ :after-backslash-block-continuation))
+ (should (= (python-indent-calculate-indentation) 5))
+ (python-tests-look-at "file_2.write(file_1.read())")
+ (should (eq (car (python-indent-context)) :after-block-start))
+ (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-after-backslash-4 ()
+ "Backslash continuation from assignment."
+ (python-tests-with-temp-buffer
+ "
+super_awful_assignment = some_calculation() and \\\\
+ another_calculation() and \\\\
+ some_final_calculation()
+"
+ (python-tests-look-at
+ "super_awful_assignment = some_calculation() and \\\\")
+ (should (eq (car (python-indent-context)) :after-line))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at "another_calculation() and \\\\")
+ (should (eq (car (python-indent-context))
+ :after-backslash-assignment-continuation))
+ (should (= (python-indent-calculate-indentation) 25))
+ (python-tests-look-at "some_final_calculation()")
+ (should (eq (car (python-indent-context)) :after-backslash))
+ (should (= (python-indent-calculate-indentation) 25))))
+
+(ert-deftest python-indent-after-backslash-5 ()
+ "Dotted continuation bizarre example."
+ (python-tests-with-temp-buffer
+ "
+def delete_all_things():
+ Thing \\\\
+ .objects.all() \\\\
+ .delete()
+"
+ (python-tests-look-at "Thing \\\\")
+ (should (eq (car (python-indent-context)) :after-block-start))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at ".objects.all() \\\\")
+ (should (eq (car (python-indent-context)) :after-backslash-first-line))
+ (should (= (python-indent-calculate-indentation) 8))
+ (python-tests-look-at ".delete()")
+ (should (eq (car (python-indent-context))
+ :after-backslash-dotted-continuation))
+ (should (= (python-indent-calculate-indentation) 16))))
+
(ert-deftest python-indent-block-enders-1 ()
"Test de-indentation for pass keyword."
(python-tests-with-temp-buffer
- "
+ "
Class foo(object):
def bar(self):
else:
pass
"
- (python-tests-look-at "3)")
- (forward-line 1)
- (should (= (python-indent-calculate-indentation) 8))
- (python-tests-look-at "pass")
- (forward-line 1)
- (should (= (python-indent-calculate-indentation) 8))))
+ (python-tests-look-at "3)")
+ (forward-line 1)
+ (should (= (python-indent-calculate-indentation) 8))
+ (python-tests-look-at "pass")
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :after-block-end))
+ (should (= (python-indent-calculate-indentation) 8))))
(ert-deftest python-indent-block-enders-2 ()
"Test de-indentation for return keyword."
(python-tests-with-temp-buffer
- "
+ "
Class foo(object):
'''raise lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
2,
3)
"
- (python-tests-look-at "def")
- (should (= (python-indent-calculate-indentation) 4))
- (python-tests-look-at "if")
- (should (= (python-indent-calculate-indentation) 8))
- (python-tests-look-at "return")
- (should (= (python-indent-calculate-indentation) 12))
- (goto-char (point-max))
- (should (= (python-indent-calculate-indentation) 8))))
+ (python-tests-look-at "def")
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "if")
+ (should (= (python-indent-calculate-indentation) 8))
+ (python-tests-look-at "return")
+ (should (= (python-indent-calculate-indentation) 12))
+ (goto-char (point-max))
+ (should (eq (car (python-indent-context)) :after-block-end))
+ (should (= (python-indent-calculate-indentation) 8))))
(ert-deftest python-indent-block-enders-3 ()
"Test de-indentation for continue keyword."
(python-tests-with-temp-buffer
- "
+ "
for element in lst:
if element is None:
continue
"
- (python-tests-look-at "if")
- (should (= (python-indent-calculate-indentation) 4))
- (python-tests-look-at "continue")
- (should (= (python-indent-calculate-indentation) 8))
- (forward-line 1)
- (should (= (python-indent-calculate-indentation) 4))))
+ (python-tests-look-at "if")
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "continue")
+ (should (= (python-indent-calculate-indentation) 8))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :after-block-end))
+ (should (= (python-indent-calculate-indentation) 4))))
(ert-deftest python-indent-block-enders-4 ()
"Test de-indentation for break keyword."
(python-tests-with-temp-buffer
- "
+ "
for element in lst:
if element is None:
break
"
- (python-tests-look-at "if")
- (should (= (python-indent-calculate-indentation) 4))
- (python-tests-look-at "break")
- (should (= (python-indent-calculate-indentation) 8))
- (forward-line 1)
- (should (= (python-indent-calculate-indentation) 4))))
+ (python-tests-look-at "if")
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "break")
+ (should (= (python-indent-calculate-indentation) 8))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :after-block-end))
+ (should (= (python-indent-calculate-indentation) 4))))
(ert-deftest python-indent-block-enders-5 ()
"Test de-indentation for raise keyword."
(python-tests-with-temp-buffer
- "
+ "
for element in lst:
if element is None:
raise ValueError('Element cannot be None')
"
- (python-tests-look-at "if")
- (should (= (python-indent-calculate-indentation) 4))
- (python-tests-look-at "raise")
- (should (= (python-indent-calculate-indentation) 8))
- (forward-line 1)
- (should (= (python-indent-calculate-indentation) 4))))
+ (python-tests-look-at "if")
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "raise")
+ (should (= (python-indent-calculate-indentation) 8))
+ (forward-line 1)
+ (should (eq (car (python-indent-context)) :after-block-end))
+ (should (= (python-indent-calculate-indentation) 4))))
(ert-deftest python-indent-dedenters-1 ()
"Test de-indentation for the elif keyword."
(python-tests-with-temp-buffer
- "
+ "
if save:
try:
write_to_disk(data)
cleanup()
elif
"
- (python-tests-look-at "elif\n")
- (should (eq (car (python-indent-context)) 'dedenter-statement))
- (should (= (python-indent-calculate-indentation) 0))
- (should (equal (python-indent-calculate-levels) '(0)))))
+ (python-tests-look-at "elif\n")
+ (should (eq (car (python-indent-context)) :at-dedenter-block-start))
+ (should (= (python-indent-calculate-indentation) 0))
+ (should (= (python-indent-calculate-indentation t) 0))))
(ert-deftest python-indent-dedenters-2 ()
"Test de-indentation for the else keyword."
(python-tests-with-temp-buffer
- "
+ "
if save:
try:
write_to_disk(data)
finally:
data.free()
"
- (python-tests-look-at "else\n")
- (should (eq (car (python-indent-context)) 'dedenter-statement))
- (should (= (python-indent-calculate-indentation) 8))
- (should (equal (python-indent-calculate-levels) '(0 4 8)))))
+ (python-tests-look-at "else\n")
+ (should (eq (car (python-indent-context)) :at-dedenter-block-start))
+ (should (= (python-indent-calculate-indentation) 8))
+ (python-indent-line t)
+ (should (= (python-indent-calculate-indentation t) 4))
+ (python-indent-line t)
+ (should (= (python-indent-calculate-indentation t) 0))
+ (python-indent-line t)
+ (should (= (python-indent-calculate-indentation t) 8))))
(ert-deftest python-indent-dedenters-3 ()
"Test de-indentation for the except keyword."
(python-tests-with-temp-buffer
- "
+ "
if save:
try:
write_to_disk(data)
except
"
- (python-tests-look-at "except\n")
- (should (eq (car (python-indent-context)) 'dedenter-statement))
- (should (= (python-indent-calculate-indentation) 4))
- (should (equal (python-indent-calculate-levels) '(4)))))
+ (python-tests-look-at "except\n")
+ (should (eq (car (python-indent-context)) :at-dedenter-block-start))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-indent-line t)
+ (should (= (python-indent-calculate-indentation t) 4))))
(ert-deftest python-indent-dedenters-4 ()
"Test de-indentation for the finally keyword."
(python-tests-with-temp-buffer
- "
+ "
if save:
try:
write_to_disk(data)
finally
"
- (python-tests-look-at "finally\n")
- (should (eq (car (python-indent-context)) 'dedenter-statement))
- (should (= (python-indent-calculate-indentation) 4))
- (should (equal (python-indent-calculate-levels) '(4)))))
+ (python-tests-look-at "finally\n")
+ (should (eq (car (python-indent-context)) :at-dedenter-block-start))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-indent-line t)
+ (should (= (python-indent-calculate-indentation) 4))))
(ert-deftest python-indent-dedenters-5 ()
"Test invalid levels are skipped in a complex example."
(python-tests-with-temp-buffer
- "
+ "
if save:
try:
write_to_disk(data)
do_cleanup()
else
"
- (python-tests-look-at "else\n")
- (should (eq (car (python-indent-context)) 'dedenter-statement))
- (should (= (python-indent-calculate-indentation) 8))
- (should (equal (python-indent-calculate-levels) '(0 8)))))
+ (python-tests-look-at "else\n")
+ (should (eq (car (python-indent-context)) :at-dedenter-block-start))
+ (should (= (python-indent-calculate-indentation) 8))
+ (should (= (python-indent-calculate-indentation t) 0))
+ (python-indent-line t)
+ (should (= (python-indent-calculate-indentation t) 8))))
(ert-deftest python-indent-dedenters-6 ()
"Test indentation is zero when no opening block for dedenter."
(python-tests-with-temp-buffer
- "
+ "
try:
# if save:
write_to_disk(data)
else
"
- (python-tests-look-at "else\n")
- (should (eq (car (python-indent-context)) 'dedenter-statement))
- (should (= (python-indent-calculate-indentation) 0))
- (should (equal (python-indent-calculate-levels) '(0)))))
+ (python-tests-look-at "else\n")
+ (should (eq (car (python-indent-context)) :at-dedenter-block-start))
+ (should (= (python-indent-calculate-indentation) 0))
+ (should (= (python-indent-calculate-indentation t) 0))))
(ert-deftest python-indent-dedenters-7 ()
"Test indentation case from Bug#15163."
(python-tests-with-temp-buffer
- "
+ "
if a:
if b:
pass
pass
else:
"
- (python-tests-look-at "else:" 2)
- (should (eq (car (python-indent-context)) 'dedenter-statement))
- (should (= (python-indent-calculate-indentation) 0))
- (should (equal (python-indent-calculate-levels) '(0)))))
+ (python-tests-look-at "else:" 2)
+ (should (eq (car (python-indent-context)) :at-dedenter-block-start))
+ (should (= (python-indent-calculate-indentation) 0))
+ (should (= (python-indent-calculate-indentation t) 0))))
(ert-deftest python-indent-dedenters-8 ()
"Test indentation for Bug#18432."
elif (a == 3 or
a == 4):
"
+ (python-tests-look-at "elif (a == 3 or")
+ (should (eq (car (python-indent-context)) :at-dedenter-block-start))
+ (should (= (python-indent-calculate-indentation) 0))
+ (should (= (python-indent-calculate-indentation t) 0))
(python-tests-look-at "a == 4):\n")
- (should (eq (car (python-indent-context)) 'inside-paren))
+ (should (eq (car (python-indent-context)) :inside-paren))
(should (= (python-indent-calculate-indentation) 6))
- (should (equal (python-indent-calculate-levels) '(0 4 6)))))
+ (python-indent-line)
+ (should (= (python-indent-calculate-indentation t) 4))
+ (python-indent-line t)
+ (should (= (python-indent-calculate-indentation t) 0))
+ (python-indent-line t)
+ (should (= (python-indent-calculate-indentation t) 6))))
+
+(ert-deftest python-indent-inside-string-1 ()
+ "Test indentation for strings."
+ (python-tests-with-temp-buffer
+ "
+multiline = '''
+bunch
+of
+lines
+'''
+"
+ (python-tests-look-at "multiline = '''")
+ (should (eq (car (python-indent-context)) :after-line))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at "bunch")
+ (should (eq (car (python-indent-context)) :inside-string))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at "of")
+ (should (eq (car (python-indent-context)) :inside-string))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at "lines")
+ (should (eq (car (python-indent-context)) :inside-string))
+ (should (= (python-indent-calculate-indentation) 0))
+ (python-tests-look-at "'''")
+ (should (eq (car (python-indent-context)) :inside-string))
+ (should (= (python-indent-calculate-indentation) 0))))
+
+(ert-deftest python-indent-inside-string-2 ()
+ "Test indentation for docstrings."
+ (python-tests-with-temp-buffer
+ "
+def fn(a, b, c=True):
+ '''docstring
+ bunch
+ of
+ lines
+ '''
+"
+ (python-tests-look-at "'''docstring")
+ (should (eq (car (python-indent-context)) :after-block-start))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "bunch")
+ (should (eq (car (python-indent-context)) :inside-string))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "of")
+ (should (eq (car (python-indent-context)) :inside-string))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "lines")
+ (should (eq (car (python-indent-context)) :inside-string))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "'''")
+ (should (eq (car (python-indent-context)) :inside-string))
+ (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-inside-string-3 ()
+ "Test indentation for nested strings."
+ (python-tests-with-temp-buffer
+ "
+def fn(a, b, c=True):
+ some_var = '''
+ bunch
+ of
+ lines
+ '''
+"
+ (python-tests-look-at "some_var = '''")
+ (should (eq (car (python-indent-context)) :after-block-start))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "bunch")
+ (should (eq (car (python-indent-context)) :inside-string))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "of")
+ (should (eq (car (python-indent-context)) :inside-string))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "lines")
+ (should (eq (car (python-indent-context)) :inside-string))
+ (should (= (python-indent-calculate-indentation) 4))
+ (python-tests-look-at "'''")
+ (should (eq (car (python-indent-context)) :inside-string))
+ (should (= (python-indent-calculate-indentation) 4))))
(ert-deftest python-indent-electric-colon-1 ()
"Test indentation case from Bug#18228."