Subject: bug#22663: python-mode: Fix electric indent bug after dedenting colon

From: Joel Rosdahl <[email-masked]>
Date: Thu, 27 Dec 2018 18:38:09 +0100

It looks like the bug still exists in Emacs 26.1 and on latest master.

Attached is an updated patch with change log entries in the commit
message on suggested by
https://www.gnu.org/software/emacs/manual/html_node/emacs/Sending-Patches.html#Sending-Patches.
Please also note that the patch includes a test case update that
verifies that the bug has been fixed.

-- Joel
From 078168c86055d5b117dee4674038190821df674a Mon Sep 17 00:00:00 2001
From: Joel Rosdahl <[email-masked]>
Date: Thu, 27 Dec 2018 16:52:07 +0100
Subject: [PATCH] Fix electric indent bug in python-mode after dedenting colon

* list/progmodes/python.el (python-indent-post-self-insert-function):
  Use markers instead of positions when reindenting statement(s) after
  inserting electric colon to avoid reindenting too many
  statements (bug#22663).

* test/lisp/progmodes/python-tests.el (python-indent-electric-colon-2):
  Improve test case to also verify the fix of bug#22663.
---
 lisp/progmodes/python.el            | 19 ++++++++++---------
 test/lisp/progmodes/python-tests.el |  3 +++
 2 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 654a0d3aea..0bde1fb8db 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -1334,16 +1334,17 @@ the line will be re-indented automatically if needed."
            (not (equal ?: (char-before (1- (point)))))
            (not (python-syntax-comment-or-string-p)))
       ;; Just re-indent dedenters
-      (let ((dedenter-pos (python-info-dedenter-statement-p))
-            (current-pos (point)))
+      (let ((dedenter-pos (python-info-dedenter-statement-p)))
         (when dedenter-pos
-          (save-excursion
-            (goto-char dedenter-pos)
-            (python-indent-line)
-            (unless (= (line-number-at-pos dedenter-pos)
-                       (line-number-at-pos current-pos))
-              ;; Reindent region if this is a multiline statement
-              (python-indent-region dedenter-pos current-pos)))))))))
+          (let ((start (copy-marker dedenter-pos))
+                (end (point-marker)))
+            (save-excursion
+              (goto-char start)
+              (python-indent-line)
+              (unless (= (line-number-at-pos start)
+                         (line-number-at-pos end))
+                ;; Reindent region if this is a multiline statement
+                (python-indent-region start end))))))))))
 
 
 ;;; Mark
diff --git a/test/lisp/progmodes/python-tests.el b/test/lisp/progmodes/python-tests.el
index 0b9f8484c1..3be3ef6cc7 100644
--- a/test/lisp/progmodes/python-tests.el
+++ b/test/lisp/progmodes/python-tests.el
@@ -1161,10 +1161,13 @@ def b()
 if do:
     something()
     else
+outside
 "
    (python-tests-look-at "else")
    (goto-char (line-end-position))
    (python-tests-self-insert ":")
+   (should (= (current-indentation) 0))
+   (python-tests-look-at "outside")
    (should (= (current-indentation) 0))))
 
 (ert-deftest python-indent-electric-colon-3 ()
-- 
2.17.1