From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: kobarity Newsgroups: gmane.emacs.bugs Subject: bug#63959: python-mode does not keep indentation in square brackets [] Date: Sun, 18 Jun 2023 23:56:50 +0900 Message-ID: References: <0a9099c91a3cd323c78e08fb6263d11fe66429bb.camel@yandex.ru> <83mt18285n.fsf@gnu.org> Mime-Version: 1.0 (generated by SEMI-EPG 1.14.7 - "Harue") Content-Type: multipart/mixed; boundary="Multipart_Sun_Jun_18_23:56:49_2023-1" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="30104"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Wanderlust/2.15.9 (Almost Unreal) SEMI-EPG/1.14.7 (Harue) FLIM-LB/1.14.9 (=?UTF-8?Q?Goj=C5=8D?=) APEL-LB/10.8 EasyPG/1.0.0 Emacs/30.0.50 (x86_64-pc-linux-gnu) MULE/6.0 (HANACHIRUSATO) Cc: 63959@debbugs.gnu.org, Stefan Monnier , Konstantin Kharlamov To: Eli Zaretskii Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Sun Jun 18 16:58:24 2023 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1qAtrD-0007de-Ne for geb-bug-gnu-emacs@m.gmane-mx.org; Sun, 18 Jun 2023 16:58:23 +0200 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1qAtqu-000567-LR; Sun, 18 Jun 2023 10:58:04 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1qAtqt-00055h-7j for bug-gnu-emacs@gnu.org; Sun, 18 Jun 2023 10:58:03 -0400 Original-Received: from debbugs.gnu.org ([209.51.188.43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1qAtqs-0002B0-Ur for bug-gnu-emacs@gnu.org; Sun, 18 Jun 2023 10:58:02 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1qAtqs-0003gB-Fd for bug-gnu-emacs@gnu.org; Sun, 18 Jun 2023 10:58:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: kobarity Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sun, 18 Jun 2023 14:58:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 63959 X-GNU-PR-Package: emacs Original-Received: via spool by 63959-submit@debbugs.gnu.org id=B63959.168710024014068 (code B ref 63959); Sun, 18 Jun 2023 14:58:02 +0000 Original-Received: (at 63959) by debbugs.gnu.org; 18 Jun 2023 14:57:20 +0000 Original-Received: from localhost ([127.0.0.1]:54819 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qAtqC-0003eq-43 for submit@debbugs.gnu.org; Sun, 18 Jun 2023 10:57:20 -0400 Original-Received: from mail-pf1-f169.google.com ([209.85.210.169]:42131) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qAtqA-0003eX-16 for 63959@debbugs.gnu.org; Sun, 18 Jun 2023 10:57:18 -0400 Original-Received: by mail-pf1-f169.google.com with SMTP id d2e1a72fcca58-666edfc50deso891612b3a.0 for <63959@debbugs.gnu.org>; Sun, 18 Jun 2023 07:57:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20221208; t=1687100232; x=1689692232; h=mime-version:user-agent:references:in-reply-to:subject:cc:to:from :message-id:date:from:to:cc:subject:date:message-id:reply-to; bh=CJyP7ZEzRUArAK1dU54KnxAVnoBHjdnT5sBvqzGX5po=; b=O+cchf0mApuJwCl5mlZol5C2jsSmPX411X+ri+Bttkemvl2X05q0fD3U3qyVy9uTSS AbBUDDaq3GJxNxsSQdAUD3rkqPWn8oTWRDm9F6mI23d99wOUK1xGa5IfFSPLDKhq/YjH PPrBA3Favt3+s3diDxKJJwVinbXqJMMoYasGqAzoLYIwmz/0YWCUyKU4YFKXElS3Vlrq GPi67gxkG9Qw4rv0giSaMPzk0JZVzIYhNimsPUOxKdbDyYZrlRu3cWUx69lybh4WcigT lvaz8gcgnjNgPdP0wirE1KHiOEJ5DLzNGEWFNZiJqUkzaIjSD3iaQ0Lx6wT01+E3vYq+ BWEg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20221208; t=1687100232; x=1689692232; h=mime-version:user-agent:references:in-reply-to:subject:cc:to:from :message-id:date:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=CJyP7ZEzRUArAK1dU54KnxAVnoBHjdnT5sBvqzGX5po=; b=EuIHbkdpTZ3vqlKxslhWiAmuSlhGA0Jz1DCjFHueBnR15bm9G8NYVgeuOfxSY99P6H ZLAKyCT6WWgJ9MpsXmjVMw3Tflbpvf8B4uj1kT9J+Tcj9iyMV0edYJYF4SG5pubsMj0+ qhE0FhoJNhs4dzFnvNvXfGW0Ma+toTviXYFluXQ0+gWZf7ov4FiWkuv52YSBjj5auNfA CSGKVcw0Y0fBy0zUHxzM5zK+WAjSfCiMvIWF39fVB3RR6Ln3x2yCbptrUYXskEic5npj P/7gSiZOWvHFrRwQcJNOrO67gAFh65zlxHw6qg0p8SNw000epPDmGrb0+QILJ7eIehGh rCvQ== X-Gm-Message-State: AC+VfDyUPTRa/QkYmxaYKmtTjxM25joqRAJjzKsSqpIKJFZnvguQVeMJ NZvMAOD3UZ0yxanvIwsitBk= X-Google-Smtp-Source: ACHHUZ4exj9O9rwC3AQQVGDckOCvIqK0VUKntngflvW9VeT6y8MD7BqGpxRLoI+JjQeOnwmQo+eDBA== X-Received: by 2002:a05:6a00:17a3:b0:668:7fb2:d9a5 with SMTP id s35-20020a056a0017a300b006687fb2d9a5mr198880pfg.17.1687100231739; Sun, 18 Jun 2023 07:57:11 -0700 (PDT) Original-Received: from localhost (58x12x133x161.ap58.ftth.ucom.ne.jp. [58.12.133.161]) by smtp.gmail.com with ESMTPSA id m26-20020a056a00165a00b0064f71d8adf8sm16155120pfc.208.2023.06.18.07.57.10 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 18 Jun 2023 07:57:11 -0700 (PDT) In-Reply-To: X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.bugs:263638 Archived-At: --Multipart_Sun_Jun_18_23:56:49_2023-1 Content-Type: text/plain; charset=US-ASCII I wrote: > Eli Zaretskii wrote: > > > From: Konstantin Kharlamov > > > Date: Thu, 08 Jun 2023 12:39:23 +0300 > > > > > > Usually in programming modes, when previous indentation is kind of "special", > > > the new lines should keep the indentation from the previous line. However, it > > > doesn't work in this case. > > > > > > > > > # Steps to reproduce > > > > > > 1. Create file `test.py` with following content: > > > > > > for infix in [ # some description > > > "_cdata", "_cmeta", "_corig", "_cpool", "_cvol", "_wcorig", > > > "indentation is broken here", "bar"]: > > > print(infix) > > > > > > 2. Open it as `emacs -Q test.py` > > > 3. Put a caret on the 3rd line (which says "indentation is broken" > > > 4. Press TAB > > > > > > > > > ## Expected > > > > > > Indentation won't change > > > > > > ## Actual > > > > > > The line goes back by 4 spaces or so > > > > > > # Additional information > > > > > > emacs version: compiled from latest git a week ago, commit 5cace109d2b > > > > kobarity, any comments? > > I think the current Python mode tries to indent based on parens, > regardless of the indentation of the previous line. However, it would > also be reasonable to maintain the indentation of the previous line. > I will see if I can implement it. Attached is a patch to implement it. I introduced a new indent context `:inside-paren-continuation-line' for the continuation lines within paren. The indent context respects the indentation of the previous line. However, it may happen that the previous line is indented for the inner paren as in the ERT `python-indent-inside-paren-2': data = {'key': { 'objlist': [ {'pk': 1, 'name': 'first'}, {'pk': 2, 'name': 'second'} ] }} The line "{'pk': 2," is considered as the continuation line, but it should not respect the indentation of the PREVIOUS line "'name': 'first'},". So skipping such lines with inner parens were needed. It searches backward for the line which starts with the item of the same opening paren as the target line. In the case of the above example, if the target line is "{'pk': 2,", its opening paren is "[" in the line "'objlist': [". It first checks the previous line "'name': 'first'},", but its opening paren is "{" in "{'pk': 1,". So this line is skipped. Next, it checks the line "{'pk': 1," and its opening paren is "[" in the line "'objlist': [", which is same as the target line. So the target line's indentation will be as same as the line "{'pk': 1,". It would be helpful if you could try this patch. Does anyone think we should have a customize variable that switches between the traditional behavior of ignoring the indentation of the previous line and this new behavior? --Multipart_Sun_Jun_18_23:56:49_2023-1 Content-Type: application/octet-stream; type=patch; name="0001-Fix-Python-indentation-of-continuation-lines-within-.patch" Content-Disposition: attachment; filename="0001-Fix-Python-indentation-of-continuation-lines-within-.patch" Content-Transfer-Encoding: 7bit >From c3aac2626eb346b19f5128208dc6ca4b7ca7b6c3 Mon Sep 17 00:00:00 2001 From: kobarity Date: Sun, 18 Jun 2023 23:47:25 +0900 Subject: [PATCH] Fix Python indentation of continuation lines within parens * lisp/progmodes/python.el (python-indent-context): Add a new indent context `:inside-paren-continuation-line'. (python-indent--calculate-indentation): Use the new indent context. * test/lisp/progmodes/python-tests.el (python-indent-pep8-2) (python-indent-pep8-3) (python-indent-inside-paren-1) (python-indent-inside-paren-2) (python-indent-inside-paren-3) (python-indent-inside-paren-6) (python-indent-after-backslash-2): Change to use the new indent context. (python-indent-inside-paren-8) (python-indent-inside-paren-9): New tests. (Bug#63959) --- lisp/progmodes/python.el | 26 ++++++++- test/lisp/progmodes/python-tests.el | 89 +++++++++++++++++++++++++---- 2 files changed, 101 insertions(+), 14 deletions(-) diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 26dafde7591..50d712ebb0c 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1406,6 +1406,10 @@ python-indent-context - Point is inside a paren from a block start followed by some items on the same line. - START is the first non space char position *after* the open paren. +:inside-paren-continuation-line + - Point is on a continuation line inside a paren. + - START is the position where the previous line (excluding lines + for inner parens) starts. :after-backslash - Fallback case when point is after backslash. @@ -1460,7 +1464,21 @@ python-indent-context (= (line-number-at-pos) (progn (python-util-forward-comment) - (line-number-at-pos)))))))) + (line-number-at-pos))))))) + (continuation-start + (when start + (save-excursion + (forward-line -1) + (back-to-indentation) + ;; Skip inner parens. + (cl-loop with prev-start = (python-syntax-context 'paren) + while (and prev-start (>= prev-start start)) + if (= prev-start start) + return (point) + else do (goto-char prev-start) + (back-to-indentation) + (setq prev-start + (python-syntax-context 'paren))))))) (when start (cond ;; Current line only holds the closing paren. @@ -1476,6 +1494,9 @@ python-indent-context (back-to-indentation) (python-syntax-closing-paren-p)) (cons :inside-paren-at-closing-nested-paren start)) + ;; This line is a continuation of the previous line. + (continuation-start + (cons :inside-paren-continuation-line continuation-start)) ;; This line starts from an opening block in its own line. ((save-excursion (goto-char start) @@ -1591,7 +1612,8 @@ python-indent--calculate-indentation (`(,(or :after-line :after-comment :inside-string - :after-backslash) . ,start) + :after-backslash + :inside-paren-continuation-line) . ,start) ;; Copy previous indentation. (goto-char start) (current-indentation)) diff --git a/test/lisp/progmodes/python-tests.el b/test/lisp/progmodes/python-tests.el index 9323f72f384..54e32cbf07b 100644 --- a/test/lisp/progmodes/python-tests.el +++ b/test/lisp/progmodes/python-tests.el @@ -683,7 +683,7 @@ python-indent-pep8-2 (should (= (python-indent-calculate-indentation) 8)) (python-tests-look-at "var_four):") (should (eq (car (python-indent-context)) - :inside-paren-newline-start-from-block)) + :inside-paren-continuation-line)) (should (= (python-indent-calculate-indentation) 8)) (python-tests-look-at "print (var_one)") (should (eq (car (python-indent-context)) @@ -707,8 +707,8 @@ python-indent-pep8-3 (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-newline-start)) - (should (= (python-indent-calculate-indentation) 4)))) + (should (eq (car (python-indent-context)) :inside-paren-continuation-line)) + (should (= (python-indent-calculate-indentation) 2)))) (ert-deftest python-indent-hanging-close-paren () "Like first pep8 case, but with hanging close paren." ;; See Bug#20742. @@ -864,7 +864,7 @@ python-indent-inside-paren-1 (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-newline-start)) + (should (eq (car (python-indent-context)) :inside-paren-continuation-line)) (should (= (python-indent-calculate-indentation) 4)) (python-tests-look-at "'objlist': [") (should (eq (car (python-indent-context)) :inside-paren-newline-start)) @@ -876,20 +876,20 @@ python-indent-inside-paren-1 (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-newline-start)) + (should (eq (car (python-indent-context)) :inside-paren-continuation-line)) (should (= (python-indent-calculate-indentation) 16)) (python-tests-look-at "},") (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-newline-start)) + (should (eq (car (python-indent-context)) :inside-paren-continuation-line)) (should (= (python-indent-calculate-indentation) 12)) (python-tests-look-at "'pk': 2,") (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-newline-start)) + (should (eq (car (python-indent-context)) :inside-paren-continuation-line)) (should (= (python-indent-calculate-indentation) 16)) (python-tests-look-at "}") (should (eq (car (python-indent-context)) @@ -933,7 +933,7 @@ python-indent-inside-paren-2 (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-newline-start)) + (should (eq (car (python-indent-context)) :inside-paren-continuation-line)) (should (= (python-indent-calculate-indentation) 8)) (python-tests-look-at "'name': 'second'}") (should (eq (car (python-indent-context)) :inside-paren)) @@ -966,10 +966,10 @@ python-indent-inside-paren-3 (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 (eq (car (python-indent-context)) :inside-paren-continuation-line)) (should (= (python-indent-calculate-indentation) 8)) (forward-line 1) - (should (eq (car (python-indent-context)) :inside-paren)) + (should (eq (car (python-indent-context)) :inside-paren-continuation-line)) (should (= (python-indent-calculate-indentation) 8)))) (ert-deftest python-indent-inside-paren-4 () @@ -1023,7 +1023,7 @@ python-indent-inside-paren-6 (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 (eq (car (python-indent-context)) :inside-paren-continuation-line)) (should (= (python-indent-calculate-indentation) 11)))) (ert-deftest python-indent-inside-paren-7 () @@ -1034,6 +1034,71 @@ python-indent-inside-paren-7 ;; This signals an error if the test fails (should (eq (car (python-indent-context)) :inside-paren-newline-start)))) +(ert-deftest python-indent-inside-paren-8 () + "Test for Bug#63959." + (python-tests-with-temp-buffer + " +for a in [ # comment + 'some', # Manually indented. + 'thing']: # Respect indentation of the previous line. +" + (python-tests-look-at "for a in [ # comment") + (should (eq (car (python-indent-context)) :no-indent)) + (should (= (python-indent-calculate-indentation) 0)) + (forward-line 1) + (should (eq (car (python-indent-context)) + :inside-paren-newline-start-from-block)) + (should (= (python-indent-calculate-indentation) 8)) + (forward-line 1) + (should (eq (car (python-indent-context)) :inside-paren-continuation-line)) + (should (= (python-indent-calculate-indentation) 10)))) + +(ert-deftest python-indent-inside-paren-9 () + "Test `:inside-paren-continuation-line'." + (python-tests-with-temp-buffer + " +a = ((( + 1, 2), + 3), # Do not respect the indentation of the previous line + 4) # Do not respect the indentation of the previous line +b = (( + 1, 2), # Manually indented + 3, # Do not respect the indentation of the previous line + 4, # Respect the indentation of the previous line + 5, # Manually indented + 6) # Respect the indentation of the previous line +" + (python-tests-look-at "a = (((") + (should (eq (car (python-indent-context)) :no-indent)) + (should (= (python-indent-calculate-indentation) 0)) + (forward-line 1) + (should (eq (car (python-indent-context)) :inside-paren-newline-start)) + (should (= (python-indent-calculate-indentation) 4)) + (forward-line 1) + (should (eq (car (python-indent-context)) :inside-paren)) + (should (= (python-indent-calculate-indentation) 6)) + (forward-line 1) + (should (eq (car (python-indent-context)) :inside-paren)) + (should (= (python-indent-calculate-indentation) 5)) + (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)) :inside-paren-newline-start)) + (should (= (python-indent-calculate-indentation) 4)) + (forward-line 1) + (should (eq (car (python-indent-context)) :inside-paren)) + (should (= (python-indent-calculate-indentation) 5)) + (forward-line 1) + (should (eq (car (python-indent-context)) :inside-paren-continuation-line)) + (should (= (python-indent-calculate-indentation) 5)) + (forward-line 1) + (should (eq (car (python-indent-context)) :inside-paren-continuation-line)) + (should (= (python-indent-calculate-indentation) 5)) + (forward-line 1) + (should (eq (car (python-indent-context)) :inside-paren-continuation-line)) + (should (= (python-indent-calculate-indentation) 8)))) + (ert-deftest python-indent-inside-paren-block-1 () "`python-indent-block-paren-deeper' set to nil (default). See Bug#62696." @@ -1271,7 +1336,7 @@ python-indent-after-backslash-2 (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-newline-start)) + (should (eq (car (python-indent-context)) :inside-paren-continuation-line)) (should (= (python-indent-calculate-indentation) 27)) (python-tests-look-at ") \\") (should (eq (car (python-indent-context)) :inside-paren-at-closing-paren)) -- 2.34.1 --Multipart_Sun_Jun_18_23:56:49_2023-1--