1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-03 02:31:03 -08:00

Fix dedenters and electric colon handling.

* lisp/progmodes/python.el
(python-rx-constituents): Add dedenter and block-ender.
(python-indent-dedenters, python-indent-block-enders): Delete.
(python-indent-context): Return new case for dedenter-statement.
(python-indent-calculate-indentation): Handle new case.
(python-indent-calculate-levels): Fix levels calculation for
dedenter statements.
(python-indent-post-self-insert-function): Fix colon handling.
(python-info-dedenter-opening-block-message): New function.
(python-indent-line): Use it.
(python-info-closing-block)
(python-info-closing-block-message): Remove.
(python-info-dedenter-opening-block-position)
(python-info-dedenter-opening-block-positions)
(python-info-dedenter-statement-p): New functions.

* test/automated/python-tests.el
(python-indent-block-enders-1)
(python-indent-block-enders-2): Fix tests.
(python-indent-block-enders-3)
(python-indent-block-enders-4)
(python-indent-block-enders-5)
(python-indent-dedenters-1)
(python-indent-dedenters-2): Remove tests.
(python-indent-dedenters-1)
(python-indent-dedenters-2)
(python-indent-dedenters-3)
(python-indent-dedenters-4)
(python-indent-dedenters-5)
(python-indent-dedenters-6)
(python-indent-dedenters-7)
(python-info-dedenter-opening-block-position-1)
(python-info-dedenter-opening-block-position-2)
(python-info-dedenter-opening-block-position-3)
(python-info-dedenter-opening-block-positions-1)
(python-info-dedenter-opening-block-positions-2)
(python-info-dedenter-opening-block-positions-3)
(python-info-dedenter-opening-block-positions-4)
(python-info-dedenter-opening-block-positions-5)
(python-info-dedenter-opening-block-message-1)
(python-info-dedenter-opening-block-message-2)
(python-info-dedenter-opening-block-message-3)
(python-info-dedenter-opening-block-message-4)
(python-info-dedenter-opening-block-message-5)
(python-info-dedenter-statement-p-1)
(python-info-dedenter-statement-p-2)
(python-info-dedenter-statement-p-3)
(python-info-dedenter-statement-p-4)
(python-info-dedenter-statement-p-5): New tests.

Fixes: debbugs:15163
This commit is contained in:
Fabián Ezequiel Gallina 2014-07-09 00:55:53 -03:00
parent d8899d09b9
commit fded0b4a15
4 changed files with 814 additions and 193 deletions

View file

@ -1,3 +1,23 @@
2014-07-09 Fabián Ezequiel Gallina <fgallina@gnu.org>
Fix dedenters and electric colon handling. (Bug#15163)
* progmodes/python.el
(python-rx-constituents): Add dedenter and block-ender.
(python-indent-dedenters, python-indent-block-enders): Delete.
(python-indent-context): Return new case for dedenter-statement.
(python-indent-calculate-indentation): Handle new case.
(python-indent-calculate-levels): Fix levels calculation for
dedenter statements.
(python-indent-post-self-insert-function): Fix colon handling.
(python-info-dedenter-opening-block-message): New function.
(python-indent-line): Use it.
(python-info-closing-block)
(python-info-closing-block-message): Remove.
(python-info-dedenter-opening-block-position)
(python-info-dedenter-opening-block-positions)
(python-info-dedenter-statement-p): New functions.
2014-07-08 Stefan Monnier <monnier@iro.umontreal.ca>
* progmodes/sh-script.el (sh-smie-sh-rules): Don't align with a && in

View file

@ -322,6 +322,13 @@
(or "def" "class" "if" "elif" "else" "try"
"except" "finally" "for" "while" "with")
symbol-end))
(dedenter . ,(rx symbol-start
(or "elif" "else" "except" "finally")
symbol-end))
(block-ender . ,(rx symbol-start
(or
"break" "continue" "pass" "raise" "return")
symbol-end))
(decorator . ,(rx line-start (* space) ?@ (any letter ?_)
(* (any word ?_))))
(defun . ,(rx symbol-start (or "def" "class") symbol-end))
@ -631,18 +638,6 @@ It makes underscores and dots word constituent chars.")
(defvar python-indent-levels '(0)
"Levels of indentation available for `python-indent-line-function'.")
(defvar python-indent-dedenters '("else" "elif" "except" "finally")
"List of words that should be dedented.
These make `python-indent-calculate-indentation' subtract the value of
`python-indent-offset'.")
(defvar python-indent-block-enders
'("break" "continue" "pass" "raise" "return")
"List of words that mark the end of a block.
These make `python-indent-calculate-indentation' subtract the
value of `python-indent-offset' when `python-indent-context' is
AFTER-LINE.")
(defun python-indent-guess-indent-offset ()
"Guess and set `python-indent-offset' for the current buffer."
(interactive)
@ -693,6 +688,7 @@ Where status can be any of the following symbols:
* 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."
(save-restriction
@ -747,6 +743,8 @@ START is the buffer position where the sexp starts."
(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
(back-to-indentation)
@ -777,8 +775,7 @@ START is the buffer position where the sexp starts."
(goto-char context-start)
(+ (current-indentation) python-indent-offset))
;; When after a simple line just use previous line
;; indentation, in the case current line starts with a
;; `python-indent-dedenters' de-indent one level.
;; indentation.
(`after-line
(let* ((pair (save-excursion
(goto-char context-start)
@ -786,25 +783,27 @@ START is the buffer position where the sexp starts."
(current-indentation)
(python-info-beginning-of-block-p))))
(context-indentation (car pair))
(after-block-start-p (cdr pair))
;; TODO: Separate block enders into its own case.
(adjustment
(if (or (save-excursion
(back-to-indentation)
(and
;; De-indent only when dedenters are not
;; next to a block start. This allows
;; one-liner constructs such as:
;; if condition: print "yay"
;; else: print "wry"
(not after-block-start-p)
(looking-at (regexp-opt python-indent-dedenters))))
(save-excursion
(python-util-forward-comment -1)
(python-nav-beginning-of-statement)
(looking-at (regexp-opt python-indent-block-enders))))
(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
@ -931,16 +930,25 @@ START is the buffer position where the sexp starts."
(defun python-indent-calculate-levels ()
"Calculate `python-indent-levels' and reset `python-indent-current-level'."
(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 (nreverse python-indent-levels))
(setq python-indent-current-level (1- (length python-indent-levels)))))
(if (not (python-info-dedenter-statement-p))
(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'."
@ -989,7 +997,7 @@ equal to
(indent-to next-indent)
(goto-char starting-pos))
(and follow-indentation-p (back-to-indentation)))
(python-info-closing-block-message))
(python-info-dedenter-opening-block-message))
(defun python-indent-line-function ()
"`indent-line-function' for Python mode.
@ -1125,14 +1133,7 @@ the line will be re-indented automatically if needed."
(eolp)
(not (equal ?: (char-before (1- (point)))))
(not (python-syntax-comment-or-string-p)))
(let ((indentation (current-indentation))
(calculated-indentation (python-indent-calculate-indentation)))
(python-info-closing-block-message)
(when (> indentation calculated-indentation)
(save-excursion
(indent-line-to calculated-indentation)
(when (not (python-info-closing-block-message))
(indent-line-to indentation)))))))))
(python-indent-line)))))
;;; Navigation
@ -3450,49 +3451,88 @@ parent defun name."
(and (python-info-end-of-statement-p)
(python-info-statement-ends-block-p)))
(defun python-info-closing-block ()
"Return the point of the block the current line closes."
(let ((closing-word (save-excursion
(back-to-indentation)
(current-word)))
(indentation (current-indentation)))
(when (member closing-word python-indent-dedenters)
(save-excursion
(forward-line -1)
(while (and (> (current-indentation) indentation)
(not (bobp))
(not (back-to-indentation))
(forward-line -1)))
(back-to-indentation)
(cond
((not (equal indentation (current-indentation))) nil)
((string= closing-word "elif")
(when (member (current-word) '("if" "elif"))
(point-marker)))
((string= closing-word "else")
(when (member (current-word) '("if" "elif" "except" "for" "while"))
(point-marker)))
((string= closing-word "except")
(when (member (current-word) '("try"))
(point-marker)))
((string= closing-word "finally")
(when (member (current-word) '("except" "else"))
(point-marker))))))))
(define-obsolete-function-alias
'python-info-closing-block
'python-info-dedenter-opening-block-position "24.4")
(defun python-info-closing-block-message (&optional closing-block-point)
"Message the contents of the block the current line closes.
With optional argument CLOSING-BLOCK-POINT use that instead of
recalculating it calling `python-info-closing-block'."
(let ((point (or closing-block-point (python-info-closing-block))))
(defun python-info-dedenter-opening-block-position ()
"Return the point of the closest block the current line closes.
Returns nil if point is not on a dedenter statement or no opening
block can be detected. The latter case meaning current file is
likely an invalid python file."
(let ((positions (python-info-dedenter-opening-block-positions))
(indentation (current-indentation))
(position))
(while (and (not position)
positions)
(save-excursion
(goto-char (car positions))
(if (<= (current-indentation) indentation)
(setq position (car positions))
(setq positions (cdr positions)))))
position))
(defun python-info-dedenter-opening-block-positions ()
"Return points of blocks the current line may close sorted by closer.
Returns nil if point is not on a dedenter statement or no opening
block can be detected. The latter case meaning current file is
likely an invalid python file."
(save-excursion
(let ((dedenter-pos (python-info-dedenter-statement-p)))
(when dedenter-pos
(goto-char dedenter-pos)
(let* ((pairs '(("elif" "elif" "if")
("else" "if" "elif" "except" "for" "while")
("except" "except" "try")
("finally" "else" "except" "try")))
(dedenter (match-string-no-properties 0))
(possible-opening-blocks (cdr (assoc-string dedenter pairs)))
(collected-indentations)
(opening-blocks))
(catch 'exit
(while (python-nav--syntactically
(lambda ()
(re-search-backward (python-rx block-start) nil t))
#'<)
(let ((indentation (current-indentation)))
(when (and (not (memq indentation collected-indentations))
(or (not collected-indentations)
(< indentation (apply #'min collected-indentations))))
(setq collected-indentations
(cons indentation collected-indentations))
(when (member (match-string-no-properties 0)
possible-opening-blocks)
(setq opening-blocks (cons (point) opening-blocks))))
(when (zerop indentation)
(throw 'exit nil)))))
;; sort by closer
(nreverse opening-blocks))))))
(define-obsolete-function-alias
'python-info-closing-block-message
'python-info-dedenter-opening-block-message "24.4")
(defun python-info-dedenter-opening-block-message ()
"Message the first line of the block the current statement closes."
(let ((point (python-info-dedenter-opening-block-position)))
(when point
(save-restriction
(widen)
(message "Closes %s" (save-excursion
(goto-char point)
(back-to-indentation)
(buffer-substring
(point) (line-end-position))))))))
(defun python-info-dedenter-statement-p ()
"Return point if current statement is a dedenter.
Sets `match-data' to the keyword that starts the dedenter
statement."
(save-excursion
(python-nav-beginning-of-statement)
(when (and (not (python-syntax-context-type))
(looking-at (python-rx dedenter)))
(point))))
(defun python-info-line-ends-backslash-p (&optional line-number)
"Return non-nil if current line ends with backslash.
With optional argument LINE-NUMBER, check that line instead."

View file

@ -1,3 +1,40 @@
2014-07-09 Fabián Ezequiel Gallina <fgallina@gnu.org>
* automated/python-tests.el
(python-indent-block-enders-1)
(python-indent-block-enders-2): Fix tests.
(python-indent-block-enders-3)
(python-indent-block-enders-4)
(python-indent-block-enders-5)
(python-indent-dedenters-1)
(python-indent-dedenters-2): Remove tests.
(python-indent-dedenters-1)
(python-indent-dedenters-2)
(python-indent-dedenters-3)
(python-indent-dedenters-4)
(python-indent-dedenters-5)
(python-indent-dedenters-6)
(python-indent-dedenters-7)
(python-info-dedenter-opening-block-position-1)
(python-info-dedenter-opening-block-position-2)
(python-info-dedenter-opening-block-position-3)
(python-info-dedenter-opening-block-positions-1)
(python-info-dedenter-opening-block-positions-2)
(python-info-dedenter-opening-block-positions-3)
(python-info-dedenter-opening-block-positions-4)
(python-info-dedenter-opening-block-positions-5)
(python-info-dedenter-opening-block-message-1)
(python-info-dedenter-opening-block-message-2)
(python-info-dedenter-opening-block-message-3)
(python-info-dedenter-opening-block-message-4)
(python-info-dedenter-opening-block-message-5)
(python-info-dedenter-statement-p-1)
(python-info-dedenter-statement-p-2)
(python-info-dedenter-statement-p-3)
(python-info-dedenter-statement-p-4)
(python-info-dedenter-statement-p-5): New tests.
2014-07-01 Fabián Ezequiel Gallina <fgallina@gnu.org>
* automated/python-tests.el

View file

@ -435,79 +435,6 @@ def foo(a, b, c={
(should (eq (car (python-indent-context)) 'after-beginning-of-block))
(should (= (python-indent-calculate-indentation) 4))))
(ert-deftest python-indent-dedenters-1 ()
"Check all dedenters."
(python-tests-with-temp-buffer
"
def foo(a, b, c):
if a:
print (a)
elif b:
print (b)
else:
try:
print (c.pop())
except (IndexError, AttributeError):
print (c)
finally:
print ('nor a, nor b are true')
"
(python-tests-look-at "if a:")
(should (eq (car (python-indent-context)) 'after-beginning-of-block))
(should (= (python-indent-calculate-indentation) 4))
(python-tests-look-at "print (a)")
(should (eq (car (python-indent-context)) 'after-beginning-of-block))
(should (= (python-indent-calculate-indentation) 8))
(python-tests-look-at "elif b:")
(should (eq (car (python-indent-context)) 'after-line))
(should (= (python-indent-calculate-indentation) 4))
(python-tests-look-at "print (b)")
(should (eq (car (python-indent-context)) 'after-beginning-of-block))
(should (= (python-indent-calculate-indentation) 8))
(python-tests-look-at "else:")
(should (eq (car (python-indent-context)) 'after-line))
(should (= (python-indent-calculate-indentation) 4))
(python-tests-look-at "try:")
(should (eq (car (python-indent-context)) 'after-beginning-of-block))
(should (= (python-indent-calculate-indentation) 8))
(python-tests-look-at "print (c.pop())")
(should (eq (car (python-indent-context)) 'after-beginning-of-block))
(should (= (python-indent-calculate-indentation) 12))
(python-tests-look-at "except (IndexError, AttributeError):")
(should (eq (car (python-indent-context)) 'after-line))
(should (= (python-indent-calculate-indentation) 8))
(python-tests-look-at "print (c)")
(should (eq (car (python-indent-context)) 'after-beginning-of-block))
(should (= (python-indent-calculate-indentation) 12))
(python-tests-look-at "finally:")
(should (eq (car (python-indent-context)) 'after-line))
(should (= (python-indent-calculate-indentation) 8))
(python-tests-look-at "print ('nor a, nor b are true')")
(should (eq (car (python-indent-context)) 'after-beginning-of-block))
(should (= (python-indent-calculate-indentation) 12))))
(ert-deftest python-indent-dedenters-2 ()
"Check one-liner block special case.."
(python-tests-with-temp-buffer
"
cond = True
if cond:
if cond: print 'True'
else: print 'False'
else:
return
"
(python-tests-look-at "else: print 'False'")
;; When a block has code after ":" it's just considered a simple
;; line as that's a common thing to happen in one-liners.
(should (eq (car (python-indent-context)) 'after-line))
(should (= (python-indent-calculate-indentation) 4))
(python-tests-look-at "else:")
(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
@ -575,9 +502,9 @@ objects = Thing.objects.all() \\\\
(should (= (python-indent-calculate-indentation) 0))))
(ert-deftest python-indent-block-enders-1 ()
"Test `python-indent-block-enders' value honoring."
"Test de-indentation for pass keyword."
(python-tests-with-temp-buffer
"
"
Class foo(object):
def bar(self):
@ -589,17 +516,17 @@ Class foo(object):
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 (= (python-indent-calculate-indentation) 8))))
(ert-deftest python-indent-block-enders-2 ()
"Test `python-indent-block-enders' value honoring."
"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
@ -612,10 +539,177 @@ Class foo(object):
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 "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))))
(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))))
(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))))
(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))))
(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)
finally:
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)))))
(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)
except IOError:
msg = 'Error saving to disk'
message(msg)
logger.exception(msg)
except Exception:
if hide_details:
logger.exception('Unhandled exception')
else
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)))))
(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)))))
(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)))))
(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)
except IOError:
msg = 'Error saving to disk'
message(msg)
logger.exception(msg)
finally:
if cleanup:
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)))))
(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)))))
(ert-deftest python-indent-dedenters-7 ()
"Test indentation case from Bug#15163."
(python-tests-with-temp-buffer
"
if a:
if b:
pass
else:
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)))))
;;; Navigation
@ -2428,9 +2522,9 @@ if width == 0 and height == 0 and \\\\
(python-util-forward-comment -1)
(should (python-info-end-of-block-p))))
(ert-deftest python-info-closing-block-1 ()
(ert-deftest python-info-dedenter-opening-block-position-1 ()
(python-tests-with-temp-buffer
"
"
if request.user.is_authenticated():
try:
profile = request.user.get_profile()
@ -2445,26 +2539,26 @@ if request.user.is_authenticated():
profile.views += 1
profile.save()
"
(python-tests-look-at "try:")
(should (not (python-info-closing-block)))
(python-tests-look-at "except Profile.DoesNotExist:")
(should (= (python-tests-look-at "try:" -1 t)
(python-info-closing-block)))
(python-tests-look-at "else:")
(should (= (python-tests-look-at "except Profile.DoesNotExist:" -1 t)
(python-info-closing-block)))
(python-tests-look-at "if profile.stats:")
(should (not (python-info-closing-block)))
(python-tests-look-at "else:")
(should (= (python-tests-look-at "if profile.stats:" -1 t)
(python-info-closing-block)))
(python-tests-look-at "finally:")
(should (= (python-tests-look-at "else:" -2 t)
(python-info-closing-block)))))
(python-tests-look-at "try:")
(should (not (python-info-dedenter-opening-block-position)))
(python-tests-look-at "except Profile.DoesNotExist:")
(should (= (python-tests-look-at "try:" -1 t)
(python-info-dedenter-opening-block-position)))
(python-tests-look-at "else:")
(should (= (python-tests-look-at "except Profile.DoesNotExist:" -1 t)
(python-info-dedenter-opening-block-position)))
(python-tests-look-at "if profile.stats:")
(should (not (python-info-dedenter-opening-block-position)))
(python-tests-look-at "else:")
(should (= (python-tests-look-at "if profile.stats:" -1 t)
(python-info-dedenter-opening-block-position)))
(python-tests-look-at "finally:")
(should (= (python-tests-look-at "else:" -2 t)
(python-info-dedenter-opening-block-position)))))
(ert-deftest python-info-closing-block-2 ()
(ert-deftest python-info-dedenter-opening-block-position-2 ()
(python-tests-with-temp-buffer
"
"
if request.user.is_authenticated():
profile = Profile.objects.get_or_create(user=request.user)
if profile.stats:
@ -2475,10 +2569,440 @@ data = {
}
'else'
"
(python-tests-look-at "'else': 'do it'")
(should (not (python-info-closing-block)))
(python-tests-look-at "'else'")
(should (not (python-info-closing-block)))))
(python-tests-look-at "'else': 'do it'")
(should (not (python-info-dedenter-opening-block-position)))
(python-tests-look-at "'else'")
(should (not (python-info-dedenter-opening-block-position)))))
(ert-deftest python-info-dedenter-opening-block-position-3 ()
(python-tests-with-temp-buffer
"
if save:
try:
write_to_disk(data)
except IOError:
msg = 'Error saving to disk'
message(msg)
logger.exception(msg)
except Exception:
if hide_details:
logger.exception('Unhandled exception')
else
finally:
data.free()
"
(python-tests-look-at "try:")
(should (not (python-info-dedenter-opening-block-position)))
(python-tests-look-at "except IOError:")
(should (= (python-tests-look-at "try:" -1 t)
(python-info-dedenter-opening-block-position)))
(python-tests-look-at "except Exception:")
(should (= (python-tests-look-at "except IOError:" -1 t)
(python-info-dedenter-opening-block-position)))
(python-tests-look-at "if hide_details:")
(should (not (python-info-dedenter-opening-block-position)))
;; check indentation modifies the detected opening block
(python-tests-look-at "else")
(should (= (python-tests-look-at "if hide_details:" -1 t)
(python-info-dedenter-opening-block-position)))
(indent-line-to 8)
(should (= (python-tests-look-at "if hide_details:" -1 t)
(python-info-dedenter-opening-block-position)))
(indent-line-to 4)
(should (= (python-tests-look-at "except Exception:" -1 t)
(python-info-dedenter-opening-block-position)))
(indent-line-to 0)
(should (= (python-tests-look-at "if save:" -1 t)
(python-info-dedenter-opening-block-position)))))
(ert-deftest python-info-dedenter-opening-block-positions-1 ()
(python-tests-with-temp-buffer
"
if save:
try:
write_to_disk(data)
except IOError:
msg = 'Error saving to disk'
message(msg)
logger.exception(msg)
except Exception:
if hide_details:
logger.exception('Unhandled exception')
else
finally:
data.free()
"
(python-tests-look-at "try:")
(should (not (python-info-dedenter-opening-block-positions)))
(python-tests-look-at "except IOError:")
(should
(equal (list
(python-tests-look-at "try:" -1 t))
(python-info-dedenter-opening-block-positions)))
(python-tests-look-at "except Exception:")
(should
(equal (list
(python-tests-look-at "except IOError:" -1 t))
(python-info-dedenter-opening-block-positions)))
(python-tests-look-at "if hide_details:")
(should (not (python-info-dedenter-opening-block-positions)))
;; check indentation does not modify the detected opening blocks
(python-tests-look-at "else")
(should
(equal (list
(python-tests-look-at "if hide_details:" -1 t)
(python-tests-look-at "except Exception:" -1 t)
(python-tests-look-at "if save:" -1 t))
(python-info-dedenter-opening-block-positions)))
(indent-line-to 8)
(should
(equal (list
(python-tests-look-at "if hide_details:" -1 t)
(python-tests-look-at "except Exception:" -1 t)
(python-tests-look-at "if save:" -1 t))
(python-info-dedenter-opening-block-positions)))
(indent-line-to 4)
(should
(equal (list
(python-tests-look-at "if hide_details:" -1 t)
(python-tests-look-at "except Exception:" -1 t)
(python-tests-look-at "if save:" -1 t))
(python-info-dedenter-opening-block-positions)))
(indent-line-to 0)
(should
(equal (list
(python-tests-look-at "if hide_details:" -1 t)
(python-tests-look-at "except Exception:" -1 t)
(python-tests-look-at "if save:" -1 t))
(python-info-dedenter-opening-block-positions)))))
(ert-deftest python-info-dedenter-opening-block-positions-2 ()
"Test detection of opening blocks for elif."
(python-tests-with-temp-buffer
"
if var:
if var2:
something()
elif var3:
something_else()
elif
"
(python-tests-look-at "elif var3:")
(should
(equal (list
(python-tests-look-at "if var2:" -1 t)
(python-tests-look-at "if var:" -1 t))
(python-info-dedenter-opening-block-positions)))
(python-tests-look-at "elif\n")
(should
(equal (list
(python-tests-look-at "elif var3:" -1 t)
(python-tests-look-at "if var:" -1 t))
(python-info-dedenter-opening-block-positions)))))
(ert-deftest python-info-dedenter-opening-block-positions-3 ()
"Test detection of opening blocks for else."
(python-tests-with-temp-buffer
"
try:
something()
except:
if var:
if var2:
something()
elif var3:
something_else()
else
if var4:
while var5:
var4.pop()
else
for value in var6:
if value > 0:
print value
else
"
(python-tests-look-at "else\n")
(should
(equal (list
(python-tests-look-at "elif var3:" -1 t)
(python-tests-look-at "if var:" -1 t)
(python-tests-look-at "except:" -1 t))
(python-info-dedenter-opening-block-positions)))
(python-tests-look-at "else\n")
(should
(equal (list
(python-tests-look-at "while var5:" -1 t)
(python-tests-look-at "if var4:" -1 t))
(python-info-dedenter-opening-block-positions)))
(python-tests-look-at "else\n")
(should
(equal (list
(python-tests-look-at "if value > 0:" -1 t)
(python-tests-look-at "for value in var6:" -1 t)
(python-tests-look-at "if var4:" -1 t))
(python-info-dedenter-opening-block-positions)))))
(ert-deftest python-info-dedenter-opening-block-positions-4 ()
"Test detection of opening blocks for except."
(python-tests-with-temp-buffer
"
try:
something()
except ValueError:
something_else()
except
"
(python-tests-look-at "except ValueError:")
(should
(equal (list (python-tests-look-at "try:" -1 t))
(python-info-dedenter-opening-block-positions)))
(python-tests-look-at "except\n")
(should
(equal (list (python-tests-look-at "except ValueError:" -1 t))
(python-info-dedenter-opening-block-positions)))))
(ert-deftest python-info-dedenter-opening-block-positions-5 ()
"Test detection of opening blocks for finally."
(python-tests-with-temp-buffer
"
try:
something()
finally
try:
something_else()
except:
logger.exception('something went wrong')
finally
try:
something_else_else()
except Exception:
logger.exception('something else went wrong')
else:
print ('all good')
finally
"
(python-tests-look-at "finally\n")
(should
(equal (list (python-tests-look-at "try:" -1 t))
(python-info-dedenter-opening-block-positions)))
(python-tests-look-at "finally\n")
(should
(equal (list (python-tests-look-at "except:" -1 t))
(python-info-dedenter-opening-block-positions)))
(python-tests-look-at "finally\n")
(should
(equal (list (python-tests-look-at "else:" -1 t))
(python-info-dedenter-opening-block-positions)))))
(ert-deftest python-info-dedenter-opening-block-message-1 ()
"Test dedenters inside strings are ignored."
(python-tests-with-temp-buffer
"'''
try:
something()
except:
logger.exception('something went wrong')
'''
"
(python-tests-look-at "except\n")
(should (not (python-info-dedenter-opening-block-message)))))
(ert-deftest python-info-dedenter-opening-block-message-2 ()
"Test except keyword."
(python-tests-with-temp-buffer
"
try:
something()
except:
logger.exception('something went wrong')
"
(python-tests-look-at "except:")
(should (string=
"Closes try:"
(substring-no-properties
(python-info-dedenter-opening-block-message))))
(end-of-line)
(should (string=
"Closes try:"
(substring-no-properties
(python-info-dedenter-opening-block-message))))))
(ert-deftest python-info-dedenter-opening-block-message-3 ()
"Test else keyword."
(python-tests-with-temp-buffer
"
try:
something()
except:
logger.exception('something went wrong')
else:
logger.debug('all good')
"
(python-tests-look-at "else:")
(should (string=
"Closes except:"
(substring-no-properties
(python-info-dedenter-opening-block-message))))
(end-of-line)
(should (string=
"Closes except:"
(substring-no-properties
(python-info-dedenter-opening-block-message))))))
(ert-deftest python-info-dedenter-opening-block-message-4 ()
"Test finally keyword."
(python-tests-with-temp-buffer
"
try:
something()
except:
logger.exception('something went wrong')
else:
logger.debug('all good')
finally:
clean()
"
(python-tests-look-at "finally:")
(should (string=
"Closes else:"
(substring-no-properties
(python-info-dedenter-opening-block-message))))
(end-of-line)
(should (string=
"Closes else:"
(substring-no-properties
(python-info-dedenter-opening-block-message))))))
(ert-deftest python-info-dedenter-opening-block-message-5 ()
"Test elif keyword."
(python-tests-with-temp-buffer
"
if a:
something()
elif b:
"
(python-tests-look-at "elif b:")
(should (string=
"Closes if a:"
(substring-no-properties
(python-info-dedenter-opening-block-message))))
(end-of-line)
(should (string=
"Closes if a:"
(substring-no-properties
(python-info-dedenter-opening-block-message))))))
(ert-deftest python-info-dedenter-statement-p-1 ()
"Test dedenters inside strings are ignored."
(python-tests-with-temp-buffer
"'''
try:
something()
except:
logger.exception('something went wrong')
'''
"
(python-tests-look-at "except\n")
(should (not (python-info-dedenter-statement-p)))))
(ert-deftest python-info-dedenter-statement-p-2 ()
"Test except keyword."
(python-tests-with-temp-buffer
"
try:
something()
except:
logger.exception('something went wrong')
"
(python-tests-look-at "except:")
(should (= (point) (python-info-dedenter-statement-p)))
(end-of-line)
(should (= (save-excursion
(back-to-indentation)
(point))
(python-info-dedenter-statement-p)))))
(ert-deftest python-info-dedenter-statement-p-3 ()
"Test else keyword."
(python-tests-with-temp-buffer
"
try:
something()
except:
logger.exception('something went wrong')
else:
logger.debug('all good')
"
(python-tests-look-at "else:")
(should (= (point) (python-info-dedenter-statement-p)))
(end-of-line)
(should (= (save-excursion
(back-to-indentation)
(point))
(python-info-dedenter-statement-p)))))
(ert-deftest python-info-dedenter-statement-p-4 ()
"Test finally keyword."
(python-tests-with-temp-buffer
"
try:
something()
except:
logger.exception('something went wrong')
else:
logger.debug('all good')
finally:
clean()
"
(python-tests-look-at "finally:")
(should (= (point) (python-info-dedenter-statement-p)))
(end-of-line)
(should (= (save-excursion
(back-to-indentation)
(point))
(python-info-dedenter-statement-p)))))
(ert-deftest python-info-dedenter-statement-p-5 ()
"Test elif keyword."
(python-tests-with-temp-buffer
"
if a:
something()
elif b:
"
(python-tests-look-at "elif b:")
(should (= (point) (python-info-dedenter-statement-p)))
(end-of-line)
(should (= (save-excursion
(back-to-indentation)
(point))
(python-info-dedenter-statement-p)))))
(ert-deftest python-info-line-ends-backslash-p-1 ()
(python-tests-with-temp-buffer