unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#62696: python.el: Extra indentation for conditionals
@ 2023-04-06 12:47 Gustaf Waldemarson
  2023-04-09 12:11 ` kobarity
  0 siblings, 1 reply; 14+ messages in thread
From: Gustaf Waldemarson @ 2023-04-06 12:47 UTC (permalink / raw)
  To: 62696


[-- Attachment #1.1: Type: text/plain, Size: 15629 bytes --]

Hello,

The follow can be considered either a feature request or a bug and was
originally asked as question on the Emacs StackOverflow page:

<
https://emacs.stackexchange.com/questions/76257/python-mode-multi-line-conditional-indentation
>

But I'll reproduce it in full here anyways:

Given the following somewhat silly Python code with a multi-line condition:

    if ("VALUE" in my_uncessarily_long_dictionary and
        some_other_long_condition_case):
        do_something()
    else:
        pass

This is written as Emacs typically indents each line, which I'm normally
fine
with. However, quite a few linters prefer the condition to be indented
visually
different from the next (non-conditional) line, e.g., something like this:

    if ("VALUE" in my_uncessarily_long_dictionary and
            some_other_long_condition_case):
        do_something():
    else:
        pass

As was helpfully provided by Tobias in the answer to this question on
StackOverflow, this was solved on Emacs-28.1. I went ahead and refactored
and
tested it on Emacs-master and it appears to be working fine there as well.
The
code for this is attached to this email.

One noticeable caveat is that **any** parenthesis can now be additionally
indented, e.g., the follow is now also possible:

    this_is_a_tuple = (long_variable_name_here,
                                      also_a_long_variable_name)

Although, given that this can be cycled at will by the user, I'm not sure
if it
is a bad additional feature or not.

Ideally, I suppose that `python-indent-context` could be modified to add a
`:inside-cond-paren` symbol that signals that the parenthesis is for a
conditional expression and thus the extra indentation should be applied,
and not
in any other case. That does seem a bit harder for me to fix at a cursory
glance
however, so maybe this fix is enough?

Best regards,
Gustaf


In GNU Emacs 30.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version
 3.24.33, cairo version 1.16.0) of 2023-03-26 built on ShadowX
Repository revision: 248aabd9514c9eb21f4aa2e9062f8ce927d9fe54
Repository branch: master
System Description: Ubuntu 22.04.2 LTS

Configured using:
 'configure --prefix=/home/xaldew/.local
 '--program-transform-name=s/^ctags$/ctags.emacs/' --without-makeinfo
 --with-xpm=ifavailable --with-jpeg=ifavailable --with-gif=ifavailable
 --with-tiff=ifavailable'

Configured features:
CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS GSETTINGS HARFBUZZ JPEG LCMS2
LIBSELINUX LIBXML2 MODULES NOTIFY INOTIFY PDUMPER PNG RSVG SECCOMP SOUND
THREADS TIFF TOOLKIT_SCROLL_BARS WEBP X11 XDBE XIM XINPUT2 XPM GTK3 ZLIB

Important settings:
  value of $LANG: sv_SE.UTF-8
  value of $XMODIFIERS: @im=ibus
  locale-coding-system: utf-8-unix

Major mode: Magit

Minor modes in effect:
  pyvenv-mode: t
  dap-tooltip-mode: t
  dap-ui-many-windows-mode: t
  dap-ui-controls-mode: t
  dap-ui-mode: t
  gdb-many-windows: t
  treemacs-filewatch-mode: t
  treemacs-follow-mode: t
  treemacs-git-mode: t
  treemacs-fringe-indicator-mode: t
  dap-auto-configure-mode: t
  dap-mode: t
  global-git-commit-mode: t
  magit-auto-revert-mode: t
  shell-dirtrack-mode: t
  beacon-mode: t
  server-mode: t
  hes-mode: t
  projectile-mode: t
  global-display-fill-column-indicator-mode: t
  global-display-line-numbers-mode: t
  global-so-long-mode: t
  yas-global-mode: t
  yas-minor-mode: t
  global-company-mode: t
  company-mode: t
  global-undo-tree-mode: t
  undo-tree-mode: t
  global-anzu-mode: t
  anzu-mode: t
  global-atomic-chrome-edit-mode: t
  windmove-mode: t
  which-key-mode: t
  anyclip-mode: t
  override-global-mode: t
  electric-pair-mode: t
  save-place-mode: t
  global-subword-mode: t
  subword-mode: t
  winner-mode: t
  global-auto-revert-mode: t
  xterm-mouse-mode: t
  savehist-mode: t
  ido-everywhere: t
  tooltip-mode: t
  global-eldoc-mode: t
  show-paren-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  tool-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  blink-cursor-mode: t
  buffer-read-only: t
  column-number-mode: t
  line-number-mode: t
  transient-mark-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t

Load-path shadows:
/home/xaldew/.config/emacs/elpa/transient-20230315.1520/transient hides
/home/xaldew/.local/share/emacs/30.0.50/lisp/transient

Features:
(shadow sort bbdb-message mail-extr warnings emacsbug cdlatex reftex
reftex-loaddefs reftex-vars org-element org-persist org-id org-refile
avl-tree oc-basic ol-eww eww xdg url-queue mm-url ol-rmail ol-mhe ol-irc
ol-info ol-gnus nnselect gnus-art mm-uu mml2015 mm-view mml-smime smime
dig gnus-sum shr pixel-fill kinsoku url-file svg gnus-group gnus-undo
gnus-start gnus-dbus dbus gnus-cloud nnimap nnmail mail-source utf7 nnoo
gnus-spec gnus-int gnus-range gnus-win gnus nnheader range ol-docview
doc-view jka-compr ol-bibtex bibtex ol-bbdb ol-w3m org-tempo tempo
cus-start ob-latex ob-plantuml ob-org ob-shell ob-gnuplot ob-C ob-python
ob-ditaa ob-dot org ob ob-tangle ob-ref ob-lob ob-table ob-exp org-macro
org-src ob-comint org-pcomplete org-list org-footnote org-faces
org-entities ob-emacs-lisp ob-core ob-eval org-cycle org-table ol
org-fold org-fold-core org-keys oc org-loaddefs cal-menu calendar
cal-loaddefs org-version org-compat org-macs shortdoc help-fns
radix-tree magit-annex magit-patch magit-subtree magit-gitignore
magit-ediff ediff ediff-merg ediff-mult ediff-wind ediff-diff ediff-help
ediff-init ediff-util image-mode exif guess-language pyvenv eshell
esh-cmd esh-ext esh-opt esh-proc esh-io esh-arg esh-module esh-groups
esh-util highlight-indentation anaconda-mode pythonic tramp
tramp-loaddefs trampver tramp-integration files-x tramp-compat
parse-time iso8601 ls-lisp python-mode/.yas-setup.el dap-python python
treesit c++-genostream cmake-mode rst ace-window all-the-icons
all-the-icons-faces data-material data-weathericons data-octicons
data-fileicons data-faicons data-alltheicons vc-hg vc-bzr vc-src vc-sccs
vc-svn vc-cvs vc-rcs log-view vc bug-reference misearch multi-isearch
lsp-diagnostics lsp-headerline lsp-icons lsp-modeline dap-mouse dap-ui
gdb-mi bui bui-list bui-info bui-entry bui-core bui-history bui-button
bui-utils lsp-lens vc-git vc-dispatcher modern-cpp-font-lock
.yas-setup.el cc-mode/.yas-setup.el view lsp-zig lsp-tilt lsp-steep
lsp-svelte lsp-sqls lsp-ruby-syntax-tree lsp-ruby-lsp lsp-yaml lsp-xml
lsp-vimscript lsp-vhdl lsp-volar lsp-vetur lsp-html lsp-verilog lsp-vala
lsp-v lsp-typeprof lsp-ttcn3 lsp-toml lsp-terraform lsp-tex lsp-sorbet
lsp-solargraph lsp-rust lsp-rf lsp-ruff-lsp lsp-remark lsp-racket lsp-r
lsp-purescript lsp-pylsp lsp-pyls lsp-pwsh lsp-php lsp-pls
lsp-perlnavigator lsp-perl lsp-openscad lsp-ocaml lsp-magik lsp-nix
lsp-nim lsp-nginx lsp-mint lsp-marksman lsp-markdown lsp-lua lsp-ltex
lsp-kotlin lsp-json lsp-javascript lsp-idris lsp-haxe lsp-groovy
lsp-hack lsp-graphql lsp-gleam lsp-go lsp-completion lsp-gdscript
lsp-fsharp lsp-fortran lsp-eslint lsp-erlang lsp-emmet lsp-elixir
lsp-elm lsp-dockerfile lsp-dhall lsp-d lsp-css lsp-csharp gnutls
lsp-crystal lsp-cmake lsp-clojure lsp-treemacs lsp-treemacs-generic
lsp-treemacs-themes treemacs-treelib treemacs treemacs-header-line
treemacs-compatibility treemacs-mode treemacs-interface
treemacs-persistence treemacs-filewatch-mode treemacs-follow-mode
treemacs-rendering treemacs-annotations treemacs-async
treemacs-workspaces treemacs-dom treemacs-visuals
treemacs-fringe-indicator treemacs-scope pulse treemacs-faces
treemacs-icons treemacs-themes treemacs-core-utils pfuture hl-line
treemacs-logging treemacs-customization treemacs-macros
lsp-semantic-tokens lsp-clangd lsp-beancount lsp-bash lsp-astro
lsp-ansible lsp-angular lsp-ada lsp-actionscript dap-gdb-lldb dap-utils
dom xml dap-mode dap-tasks dap-launch lsp-docker yaml posframe
dap-overlays magit-extras magit-submodule magit-blame magit-stash
magit-reflog magit-bisect magit-push magit-pull magit-fetch magit-clone
magit-remote magit-commit magit-sequence magit-notes magit-worktree
magit-tag magit-merge magit-branch magit-reset magit-files magit-refs
magit-status magit magit-repos magit-apply magit-wip magit-log
which-func magit-diff smerge-mode diff-mode git-commit log-edit message
sendmail yank-media rfc822 mml mml-sec epa derived gnus-util time-date
mm-decode mm-bodies mm-encode mail-parse rfc2231 rfc2047 rfc2045 mm-util
ietf-drums mail-prsvr mailabbrev mail-utils gmm-utils mailheader
pcvs-util add-log magit-core magit-autorevert magit-margin
magit-transient magit-process with-editor shell pcomplete magit-mode
transient magit-git magit-base magit-section format-spec crm compat
dired-aux dired dired-loaddefs term/tmux term/xterm xterm pinentry
beacon server form-feed paredit nameless flyspell ispell whitespace
rainbow-delimiters highlight-escape-sequences projectile lisp-mnt grep
ibuf-ext ibuffer ibuffer-loaddefs lsp-ui lsp-ui-flycheck lsp-ui-doc
goto-addr lsp-ui-imenu lsp-ui-peek lsp-ui-sideline flycheck lsp-mode
tree-widget spinner network-stream puny nsm markdown-mode noutline
outline inline imenu f f-shortdoc ewoc epg rfc6068 epg-config compile
text-property-search lsp-ui-util face-remap find-func lsp-protocol s ht
dash display-fill-column-indicator display-line-numbers so-long
py-snippets yasnippet-radical-snippets yasnippet-snippets yasnippet
company-oddmuse company-keywords company-etags etags fileloop generator
xref project company-gtags company-dabbrev-code company-dabbrev
company-files company-clang company-capf company-cmake company-semantic
company-template company-bbdb company undo-tree diff queue anzu
thingatpt atomic-chrome websocket bindat let-alist
color-theme-approximate advice color delim-col hydra-examples windmove
rect hydra lv bbdb bbdb-site timezone cus-edit pp cus-load icons
wid-edit ace-link avy which-key anyclip-mode cl-extra help-mode edmacro
kmacro diminish use-package use-package-ensure use-package-delight
use-package-diminish use-package-bind-key bind-key easy-mmode
use-package-core finder-inf local-autoloads cwarn cc-mode cc-fonts
cc-guess cc-menus cc-cmds cc-styles cc-align cc-engine cc-vars cc-defs
elec-pair saveplace cap-words superword subword winner autorevert
filenotify xt-mouse tango-dark-theme savehist ido gud comint ansi-osc
ansi-color ring keybinds terminals ivy-autoloads
spacemacs-theme-autoloads ace-link-autoloads kotlin-mode-autoloads
company-auctex-autoloads gl-conf-mode-autoloads css-eldoc-autoloads
beacon-autoloads lsp-ltex-autoloads paredit-autoloads
glsl-mode-autoloads highlight-escape-sequences-autoloads
powershell-autoloads gnuplot-mode-autoloads cuda-mode-autoloads
ein-autoloads request-autoloads ssh-config-mode-autoloads
git-modes-autoloads mmm-mode-autoloads ebdb-autoloads rmsbolt-autoloads
debbugs-autoloads dart-mode-autoloads sublime-themes-autoloads
coffee-mode-autoloads graphviz-dot-mode-autoloads magit-annex-autoloads
opencl-mode-autoloads json-mode-autoloads rx company-anaconda-autoloads
pinentry-autoloads rainbow-mode-autoloads htmlize-autoloads
projectile-autoloads flycheck-rust-autoloads bbdb-vcard-autoloads
jira-markup-mode-autoloads browse-kill-ring-autoloads
helm-dash-autoloads sx-autoloads jenkins-autoloads smart-jump-autoloads
bbdb-autoloads which-key-autoloads anzu-autoloads x86-lookup-autoloads
cmake-mode-autoloads flycheck-kotlin-autoloads undo-tree-autoloads
rust-mode-autoloads plantuml-mode-autoloads magit-gerrit-autoloads
lua-mode-autoloads yaml-mode-autoloads evil-autoloads deferred-autoloads
iedit-autoloads unfill-autoloads srefactor-autoloads abc-mode-autoloads
autoinsert solarized-theme-autoloads alert-autoloads gntp-autoloads
emms-autoloads cider-autoloads sesman-autoloads queue-autoloads
parseedn-autoloads parseclj-autoloads clojure-mode-autoloads
flycheck-package-autoloads package-lint-autoloads
atomic-chrome-autoloads websocket-autoloads powerthesaurus-autoloads
jeison-autoloads dash-docs-autoloads rainbow-delimiters-autoloads
form-feed-autoloads yasnippet-radical-snippets-autoloads
pyimport-autoloads shut-up-autoloads json-snatcher-autoloads
helm-lsp-autoloads helm-autoloads clang-format-autoloads
poly-markdown-autoloads polymode-autoloads toml-mode-autoloads
diminish-autoloads cdlatex-autoloads eclim-autoloads anaphora-autoloads
py-snippets-autoloads yasnippet-snippets-autoloads
zenburn-theme-autoloads dap-mode-autoloads lsp-docker-autoloads
lsp-treemacs-autoloads treemacs-autoloads cfrs-autoloads
posframe-autoloads pfuture-autoloads ace-window-autoloads avy-autoloads
bui-autoloads magit-svn-autoloads magit-autoloads pcase
git-commit-autoloads with-editor-autoloads transient-autoloads
go-mode-autoloads goto-chg-autoloads log4e-autoloads
color-theme-approximate-autoloads cargo-autoloads calfw-autoloads
company-quickhelp-autoloads helm-core-autoloads async-autoloads
ecb-autoloads modern-cpp-font-lock-autoloads dts-mode-autoloads
nov-autoloads esxml-autoloads kv-autoloads company-math-autoloads
math-symbol-lists-autoloads elpy-autoloads pyvenv-autoloads
highlight-indentation-autoloads ob-ipython-autoloads
dash-functional-autoloads hydra-autoloads auto-complete-auctex-autoloads
auto-complete-autoloads yasnippet-autoloads auctex-latexmk-autoloads
auctex-autoloads tex-site yaml-autoloads ahk-mode-autoloads
magit-section-autoloads compat-autoloads cask-mode-autoloads
company-c-headers-autoloads company-autoloads csv-mode-autoloads
gnuplot-autoloads anaconda-mode-autoloads pythonic-autoloads
pos-tip-autoloads zerodark-theme-autoloads all-the-icons-autoloads
image+-autoloads expand-region-autoloads gnus-desktop-notify-autoloads
flycheck-autoloads pkg-info-autoloads epl-autoloads ggtags-autoloads
web-mode-autoloads lsp-ui-autoloads lsp-mode-autoloads lv-autoloads
markdown-mode-autoloads spinner-autoloads ht-autoloads f-autoloads
uimage-autoloads guess-language-autoloads dumb-jump-autoloads
popup-autoloads dash-autoloads s-autoloads google-c-style-autoloads
nameless-autoloads info slime-autoloads macrostep-autoloads package
browse-url url url-proxy url-privacy url-expand url-methods url-history
url-cookie generate-lisp-file url-domsuf url-util mailcap url-handlers
url-parse auth-source cl-seq eieio eieio-core cl-macs password-cache
json subr-x map byte-opt gv bytecomp byte-compile url-vars cl-loaddefs
cl-lib rmc iso-transl tooltip cconv eldoc paren electric uniquify
ediff-hook vc-hooks lisp-float-type elisp-mode mwheel term/x-win x-win
term/common-win x-dnd tool-bar dnd fontset image regexp-opt fringe
tabulated-list replace newcomment text-mode lisp-mode prog-mode register
page tab-bar menu-bar rfn-eshadow isearch easymenu timer select
scroll-bar mouse jit-lock font-lock syntax font-core term/tty-colors
frame minibuffer nadvice seq simple cl-generic indonesian philippine
cham georgian utf-8-lang misc-lang vietnamese tibetan thai tai-viet lao
korean japanese eucjp-ms cp51932 hebrew greek romanian slovak czech
european ethiopic indian cyrillic chinese composite emoji-zwj charscript
charprop case-table epa-hook jka-cmpr-hook help abbrev obarray oclosure
cl-preloaded button loaddefs theme-loaddefs faces cus-face macroexp
files window text-properties overlay sha1 md5 base64 format env
code-pages mule custom widget keymap hashtable-print-readable backquote
threads dbusbind inotify lcms2 dynamic-setting system-font-setting
font-render-setting cairo move-toolbar gtk x-toolkit xinput2 x multi-tty
make-network-process emacs)

Memory information:
((conses 16 817895 212638)
 (symbols 48 62999 0)
 (strings 32 249530 19612)
 (string-bytes 1 8101995)
 (vectors 16 160320)
 (vector-slots 8 3855694 143961)
 (floats 8 1381 10031)
 (intervals 56 21064 2868)
 (buffers 976 49))

[-- Attachment #1.2: Type: text/html, Size: 16950 bytes --]

[-- Attachment #2: 0001-Allow-indent-in-long-python-mode-conditionals.patch --]
[-- Type: text/x-patch, Size: 1538 bytes --]

From 8dc2e7275d68d2749822b59e4748794ab27621c5 Mon Sep 17 00:00:00 2001
From: Gustaf Waldemarson <gustaf.waldemarson@gmail.com>
Date: Tue, 4 Apr 2023 14:04:44 +0200
Subject: [PATCH] Allow indent in long python-mode conditionals.

* lisp/progmodes/python.el: Allow additional indentation in
parentheses to more easily satisfy various Python linters.
---
 lisp/progmodes/python.el | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index bbabce80b4d..d7c6a9218c7 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -1248,6 +1248,11 @@ python-indent-offset
   :type 'integer
   :safe 'integerp)
 
+(defcustom python-indent-add-after-paren python-indent-offset
+  "Additional indent after paren."
+  :type 'number
+  :group 'python)
+
 (defcustom python-indent-guess-indent-offset t
   "Non-nil tells Python mode to guess `python-indent-offset' value."
   :type 'boolean
@@ -1580,7 +1585,10 @@ python-indent--calculate-indentation
                ;; Add one level to avoid same indent as next logical line.
                (+ column python-indent-offset)
              column)))
-        (`(,(or :inside-paren
+        (`(:inside-paren . ,start)
+         (goto-char start)
+         (+ (current-column) python-indent-add-after-paren))
+        (`(,(or :after-backslash-block-continuation
                 :after-backslash-dotted-continuation) . ,start)
          ;; Use the column given by the context.
          (goto-char start)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* bug#62696: python.el: Extra indentation for conditionals
  2023-04-06 12:47 bug#62696: python.el: Extra indentation for conditionals Gustaf Waldemarson
@ 2023-04-09 12:11 ` kobarity
  2023-04-10 11:11   ` Gustaf Waldemarson
  0 siblings, 1 reply; 14+ messages in thread
From: kobarity @ 2023-04-09 12:11 UTC (permalink / raw)
  To: Gustaf Waldemarson; +Cc: 62696

[-- Attachment #1: Type: text/plain, Size: 3323 bytes --]


Gustaf Waldemarson wrote:
> One noticeable caveat is that **any** parenthesis can now be additionally
> indented, e.g., the follow is now also possible:
> 
>     this_is_a_tuple = (long_variable_name_here,
>                                       also_a_long_variable_name)
> 
> Although, given that this can be cycled at will by the user, I'm not sure if it
> is a bad additional feature or not.
> 
> Ideally, I suppose that `python-indent-context` could be modified to add a
> `:inside-cond-paren` symbol that signals that the parenthesis is for a
> conditional expression and thus the extra indentation should be applied, and not
> in any other case. That does seem a bit harder for me to fix at a cursory glance
> however, so maybe this fix is enough?

Hi Gustaf,

I agree with you in that it's better to have a new indent context, and
I tried to implement it.

At first, I thought that it would be enough to add a counterpart of
the user option `python-indent-def-block-scale' and corresponding
`:inside-paren-newline-start-from-block' context.
`python-indent-def-block-scale' can be used to customize the following
code

#+begin_src python
if (
        "VALUE" in my_unnecessarily_long_dictionary
        and some_other_long_condition_case
        ):
    do_something()
#+end_src

to be indented as follows (with a TAB at "):" line):

#+begin_src python
if (
    "VALUE" in my_unnecessarily_long_dictionary
    and some_other_long_condition_case
):
    do_something()
#+end_src

This is the style used by the popular formatter "black".

From the name `python-indent-def-block-scale' and its docstring, it is
easy to assume that it only works for def block, but in fact it works
for every blocks.  As `python-indent-def-block-scale' works only when
there is no item on the same line following the opening paren, I tried
to add a similar user option and an indent context for the opening
paren followed by some items on the same line.  It could indent as
follows:

#+begin_src python
if ("VALUE" in my_unnecessarily_long_dictionary
        and some_other_long_condition_case):
    do_something()
#+end_src

However, it could not handle correctly the following example:

#+begin_src python
elif (some_case or
          another_case):
    do_another()
#+end_src

The extra indentation is not needed here.

So I think it is best to increase the indentation only if the
calculated indentation equals to the indentation of the contents of
the block ("do_something()" in the above example).  This is similar to
the way I fixed Bug#57262.

Unlike Bug#57262, the current indentation shown below is not a
violation of the latest PEP8:

#+begin_src python
if ("VALUE" in my_unnecessarily_long_dictionary
    and some_other_long_condition_case):
    do_something()
#+end_src

Although pycodestyle reports E129 "visually indented line with same
indent as next logical line," PEP8 was changed to allow this.  This is
explained in the following issue, for example:
https://github.com/PyCQA/pycodestyle/issues/474

So changing this indentation should be a user option.  Attached is my
implementation of this.  The user option
`python-indent-block-paren-deeper' is added to customize this
indentation.  I would be glad if you could try it.

[-- Attachment #2: 0001-Add-a-new-user-option-in-Python-mode-to-improve-the-.patch --]
[-- Type: application/octet-stream, Size: 11074 bytes --]

From acec03331413b621843fb1489e9aeb6db927614a Mon Sep 17 00:00:00 2001
From: kobarity <kobarity@gmail.com>
Date: Sun, 9 Apr 2023 20:48:00 +0900
Subject: [PATCH] Add a new user option in Python mode to improve the
 indentation

* lisp/progmodes/python.el (python-indent-block-paren-deeper): New
user option.
(python-indent-context): Add a new context :inside-paren-from-block.
(python-indent--calculate-indentation): Modify according to
`python-indent-block-paren-deeper' and :inside-paren-from-block.
* test/lisp/progmodes/python-tests.el
(python-indent-inside-paren-block-1)
(python-indent-inside-paren-block-2)
(python-indent-inside-paren-block-3)
(python-indent-inside-paren-block-4): New tests.
(python-indent-inside-paren-5, python-indent-dedenters-8): Modify
according to the new context.
* etc/NEWS: Document the new user option.  (Bug#62696)
---
 etc/NEWS                            |   8 ++
 lisp/progmodes/python.el            |  43 +++++++++--
 test/lisp/progmodes/python-tests.el | 116 +++++++++++++++++++++++++++-
 3 files changed, 158 insertions(+), 9 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index d20d9f65ac9..61736e8edf4 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -261,6 +261,14 @@ When non-nil, it will automatically register every package as a
 project, that you can quickly select using 'project-switch-project'
 ('C-x p p').
 
+** Python mode
+
+*** New user option 'python-indent-block-paren-deeper'.
+If this variable is set to non-nil, and some items follow the opening
+paren on the same line in a block statement, and the calculated
+indentation of a line inside the parens equals to the indentation of
+the block contents, the indentation level of the line is increased.
+
 \f
 * New Modes and Packages in Emacs 30.1
 
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index bbabce80b4d..dbc48038554 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -1270,6 +1270,17 @@ python-indent-def-block-scale
   :type 'integer
   :safe 'natnump)
 
+(defcustom python-indent-block-paren-deeper nil
+  "Non-nil means to increase indentation inside parens of a block.
+If this variable is set to non-nil, and some items follow the
+opening paren on the same line in a block statement, and the
+calculated indentation of a line inside the parens equals to the
+indentation of the block contents, the indentation level of the
+line is increased."
+  :version "30.1"
+  :type 'boolean
+  :safe 'booleanp)
+
 (defvar python-indent-current-level 0
   "Deprecated var available for compatibility.")
 
@@ -1367,6 +1378,10 @@ python-indent-context
  - Point is inside a paren with items starting in their own line
    from a block start.
  - START is the position of the open paren.
+:inside-paren-from-block
+ - 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.
 
 :after-backslash
  - Fallback case when point is after backslash.
@@ -1450,12 +1465,16 @@ python-indent-context
              (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))))))))
+             (t (let ((after-start (save-excursion
+                               (goto-char (1+ start))
+                               (skip-syntax-forward "(" 1)
+                               (skip-syntax-forward " ")
+                               (point))))
+                  (if (save-excursion
+                        (python-nav-beginning-of-statement)
+                        (python-info-looking-at-beginning-of-block))
+                      (cons :inside-paren-from-block after-start)
+                    (cons :inside-paren after-start))))))))
        ;; After backslash.
        ((let ((start (when (not (python-syntax-comment-or-string-p ppss))
                        (python-info-line-ends-backslash-p
@@ -1603,7 +1622,17 @@ python-indent--calculate-indentation
         (`(,(or :inside-paren-newline-start-from-block) . ,start)
          (goto-char start)
          (+ (current-indentation)
-            (* python-indent-offset python-indent-def-block-scale))))))
+            (* python-indent-offset python-indent-def-block-scale)))
+        (`(,:inside-paren-from-block . ,start)
+         (goto-char start)
+         (let ((column (current-column)))
+           (if (and python-indent-block-paren-deeper
+                    (= column (+ (save-excursion
+                                   (python-nav-beginning-of-statement)
+                                   (current-indentation))
+                                 python-indent-offset)))
+               (+ column python-indent-offset)
+             column))))))
 
 (defun python-indent--calculate-levels (indentation)
   "Calculate levels list given INDENTATION.
diff --git a/test/lisp/progmodes/python-tests.el b/test/lisp/progmodes/python-tests.el
index 50153e66da5..60b11d572cf 100644
--- a/test/lisp/progmodes/python-tests.el
+++ b/test/lisp/progmodes/python-tests.el
@@ -1139,7 +1139,7 @@ python-indent-inside-paren-5
    (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))
+   (should (eq (car (python-indent-context)) :inside-paren-from-block))
    (should (= (python-indent-calculate-indentation) 7))
    (forward-line 1)
    (should (eq (car (python-indent-context)) :after-block-start))
@@ -1174,6 +1174,118 @@ 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-block-1 ()
+  "`python-indent-block-paren-deeper' set to nil (default).
+See Bug#62696."
+  (python-tests-with-temp-buffer
+   "
+if ('VALUE' in my_unnecessarily_long_dictionary and
+    some_other_long_condition_case):
+    do_something()
+elif (some_case or
+      another_case):
+    do_another()
+"
+   (python-tests-look-at "if")
+   (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-from-block))
+   (should (= (python-indent-calculate-indentation) 4))
+   (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)) :at-dedenter-block-start))
+   (should (= (python-indent-calculate-indentation) 0))
+   (forward-line 1)
+   (should (eq (car (python-indent-context)) :inside-paren-from-block))
+   (should (= (python-indent-calculate-indentation) 6))
+   (forward-line 1)
+   (should (eq (car (python-indent-context)) :after-block-start))
+   (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-inside-paren-block-2 ()
+  "`python-indent-block-paren-deeper' set to t.
+See Bug#62696."
+  (python-tests-with-temp-buffer
+   "
+if ('VALUE' in my_unnecessarily_long_dictionary and
+        some_other_long_condition_case):
+    do_something()
+elif (some_case or
+      another_case):
+    do_another()
+"
+   (let ((python-indent-block-paren-deeper t))
+     (python-tests-look-at "if")
+     (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-from-block))
+     (should (= (python-indent-calculate-indentation) 8))
+     (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)) :at-dedenter-block-start))
+     (should (= (python-indent-calculate-indentation) 0))
+     (forward-line 1)
+     (should (eq (car (python-indent-context)) :inside-paren-from-block))
+     (should (= (python-indent-calculate-indentation) 6))
+     (forward-line 1)
+     (should (eq (car (python-indent-context)) :after-block-start))
+     (should (= (python-indent-calculate-indentation) 4)))))
+
+(ert-deftest python-indent-inside-paren-block-3 ()
+  "With backslash.  `python-indent-block-paren-deeper' set to nil (default).
+See Bug#62696."
+  (python-tests-with-temp-buffer
+   "
+if 'VALUE' in my_uncessarily_long_dictionary and\\
+   (some_other_long_condition_case or
+    another_case):
+    do_something()
+"
+   (python-tests-look-at "if")
+   (should (eq (car (python-indent-context)) :no-indent))
+   (should (= (python-indent-calculate-indentation) 0))
+   (forward-line 1)
+   (should (eq (car (python-indent-context))
+               :after-backslash-block-continuation))
+   (should (= (python-indent-calculate-indentation) 3))
+   (forward-line 1)
+   (should (eq (car (python-indent-context)) :inside-paren-from-block))
+   (should (= (python-indent-calculate-indentation) 4))
+   (forward-line 1)
+   (should (eq (car (python-indent-context)) :after-block-start))
+   (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-inside-paren-block-4 ()
+  "With backslash.  `python-indent-block-paren-deeper' set to t.
+See Bug#62696."
+  (python-tests-with-temp-buffer
+   "
+if 'VALUE' in my_uncessarily_long_dictionary and\\
+   (some_other_long_condition_case or
+        another_case):
+    do_something()
+"
+   (let ((python-indent-block-paren-deeper t))
+     (python-tests-look-at "if")
+     (should (eq (car (python-indent-context)) :no-indent))
+     (should (= (python-indent-calculate-indentation) 0))
+     (forward-line 1)
+     (should (eq (car (python-indent-context))
+                 :after-backslash-block-continuation))
+     (should (= (python-indent-calculate-indentation) 3))
+     (forward-line 1)
+     (should (eq (car (python-indent-context)) :inside-paren-from-block))
+     (should (= (python-indent-calculate-indentation) 8))
+     (forward-line 1)
+     (should (eq (car (python-indent-context)) :after-block-start))
+     (should (= (python-indent-calculate-indentation) 4)))))
+
 (ert-deftest python-indent-after-block-1 ()
   "The most simple after-block case that shouldn't fail."
   (python-tests-with-temp-buffer
@@ -1670,7 +1782,7 @@ python-indent-dedenters-8
    (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-from-block))
    (should (= (python-indent-calculate-indentation) 6))
    (python-indent-line)
    (should (= (python-indent-calculate-indentation t) 4))
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* bug#62696: python.el: Extra indentation for conditionals
  2023-04-09 12:11 ` kobarity
@ 2023-04-10 11:11   ` Gustaf Waldemarson
  2023-04-10 15:21     ` kobarity
  0 siblings, 1 reply; 14+ messages in thread
From: Gustaf Waldemarson @ 2023-04-10 11:11 UTC (permalink / raw)
  To: kobarity; +Cc: 62696

[-- Attachment #1: Type: text/plain, Size: 3991 bytes --]

Hello!

Thanks a lot for this! I had no idea that they had changed the relevant
PEPs to account
for this, but as mentioned it is still a good idea to have the option to
change this depending
on your preference. And as far as I have been able to tell from coding with
this patch for a
few hours, it seems to work beautifully and I would love to see this as an
official feature!

Best regards,
Gustaf

Den sön 9 apr. 2023 kl 14:11 skrev kobarity <kobarity@gmail.com>:

>
> Gustaf Waldemarson wrote:
> > One noticeable caveat is that **any** parenthesis can now be additionally
> > indented, e.g., the follow is now also possible:
> >
> >     this_is_a_tuple = (long_variable_name_here,
> >                                       also_a_long_variable_name)
> >
> > Although, given that this can be cycled at will by the user, I'm not
> sure if it
> > is a bad additional feature or not.
> >
> > Ideally, I suppose that `python-indent-context` could be modified to add
> a
> > `:inside-cond-paren` symbol that signals that the parenthesis is for a
> > conditional expression and thus the extra indentation should be applied,
> and not
> > in any other case. That does seem a bit harder for me to fix at a
> cursory glance
> > however, so maybe this fix is enough?
>
> Hi Gustaf,
>
> I agree with you in that it's better to have a new indent context, and
> I tried to implement it.
>
> At first, I thought that it would be enough to add a counterpart of
> the user option `python-indent-def-block-scale' and corresponding
> `:inside-paren-newline-start-from-block' context.
> `python-indent-def-block-scale' can be used to customize the following
> code
>
> #+begin_src python
> if (
>         "VALUE" in my_unnecessarily_long_dictionary
>         and some_other_long_condition_case
>         ):
>     do_something()
> #+end_src
>
> to be indented as follows (with a TAB at "):" line):
>
> #+begin_src python
> if (
>     "VALUE" in my_unnecessarily_long_dictionary
>     and some_other_long_condition_case
> ):
>     do_something()
> #+end_src
>
> This is the style used by the popular formatter "black".
>
> From the name `python-indent-def-block-scale' and its docstring, it is
> easy to assume that it only works for def block, but in fact it works
> for every blocks.  As `python-indent-def-block-scale' works only when
> there is no item on the same line following the opening paren, I tried
> to add a similar user option and an indent context for the opening
> paren followed by some items on the same line.  It could indent as
> follows:
>
> #+begin_src python
> if ("VALUE" in my_unnecessarily_long_dictionary
>         and some_other_long_condition_case):
>     do_something()
> #+end_src
>
> However, it could not handle correctly the following example:
>
> #+begin_src python
> elif (some_case or
>           another_case):
>     do_another()
> #+end_src
>
> The extra indentation is not needed here.
>
> So I think it is best to increase the indentation only if the
> calculated indentation equals to the indentation of the contents of
> the block ("do_something()" in the above example).  This is similar to
> the way I fixed Bug#57262.
>
> Unlike Bug#57262, the current indentation shown below is not a
> violation of the latest PEP8:
>
> #+begin_src python
> if ("VALUE" in my_unnecessarily_long_dictionary
>     and some_other_long_condition_case):
>     do_something()
> #+end_src
>
> Although pycodestyle reports E129 "visually indented line with same
> indent as next logical line," PEP8 was changed to allow this.  This is
> explained in the following issue, for example:
> https://github.com/PyCQA/pycodestyle/issues/474
>
> So changing this indentation should be a user option.  Attached is my
> implementation of this.  The user option
> `python-indent-block-paren-deeper' is added to customize this
> indentation.  I would be glad if you could try it.
>

[-- Attachment #2: Type: text/html, Size: 4884 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#62696: python.el: Extra indentation for conditionals
  2023-04-10 11:11   ` Gustaf Waldemarson
@ 2023-04-10 15:21     ` kobarity
  2023-04-11 11:11       ` Gustaf Waldemarson
  0 siblings, 1 reply; 14+ messages in thread
From: kobarity @ 2023-04-10 15:21 UTC (permalink / raw)
  To: Gustaf Waldemarson; +Cc: 62696


Gustaf Waldemarson wrote:
> Thanks a lot for this! I had no idea that they had changed the relevant PEPs to account
> for this, but as mentioned it is still a good idea to have the option to change this depending
> on your preference. And as far as I have been able to tell from coding with this patch for a
> few hours, it seems to work beautifully and I would love to see this as an official feature!

Hi Gustaf,

Thank you for testing my patch.  I am relieved that it seems to have
fulfilled your request.

As I am not a native English speaker, I welcome any feedback from
anyone on the name and/or docstrings of the new user option as well as
on the behavior and coding.





^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#62696: python.el: Extra indentation for conditionals
  2023-04-10 15:21     ` kobarity
@ 2023-04-11 11:11       ` Gustaf Waldemarson
  2023-04-12 15:26         ` kobarity
  0 siblings, 1 reply; 14+ messages in thread
From: Gustaf Waldemarson @ 2023-04-11 11:11 UTC (permalink / raw)
  To: kobarity; +Cc: 62696


[-- Attachment #1.1: Type: text/plain, Size: 1592 bytes --]

Hello Kobarity,

Being Swedish, I am hardly an authority on the English language. That said,
I went
ahead and double-checked the docstring and such, and the only thing that I
found a
bit confusing is the `python-indent-block-paren-deeper` description as
follows:

"Increase indentation inside parens of a block.
When this variable is set to non-nil and the contents of a block
inside parens are indented to the same level as outside the same
block, increase the indentation of the line."

This is really nitpicking though, and I may be using some terms incorrectly
since
since I don't know a lot of the internals here, so feel free to disregard
any of this. These
changes are also available in attached patch, feel free to use it if need
be!

Best regards,
Gustaf

Den mån 10 apr. 2023 kl 17:21 skrev kobarity <kobarity@gmail.com>:

>
> Gustaf Waldemarson wrote:
> > Thanks a lot for this! I had no idea that they had changed the relevant
> PEPs to account
> > for this, but as mentioned it is still a good idea to have the option to
> change this depending
> > on your preference. And as far as I have been able to tell from coding
> with this patch for a
> > few hours, it seems to work beautifully and I would love to see this as
> an official feature!
>
> Hi Gustaf,
>
> Thank you for testing my patch.  I am relieved that it seems to have
> fulfilled your request.
>
> As I am not a native English speaker, I welcome any feedback from
> anyone on the name and/or docstrings of the new user option as well as
> on the behavior and coding.
>

[-- Attachment #1.2: Type: text/html, Size: 2068 bytes --]

[-- Attachment #2: 0001-Add-a-new-user-option-in-Python-mode-to-improve-indent-2.patch --]
[-- Type: text/x-patch, Size: 10909 bytes --]

From ea2de07164cee80a85226e68b83692d6eeda4f46 Mon Sep 17 00:00:00 2001
From: kobarity <kobarity@gmail.com>
Date: Sun, 9 Apr 2023 20:48:00 +0900
Subject: [PATCH] Add a new user option in Python mode to improve the
 indentation

* lisp/progmodes/python.el (python-indent-block-paren-deeper): New
user option.
(python-indent-context): Add a new context :inside-paren-from-block.
(python-indent--calculate-indentation): Modify according to
`python-indent-block-paren-deeper' and :inside-paren-from-block.
* test/lisp/progmodes/python-tests.el
(python-indent-inside-paren-block-1)
(python-indent-inside-paren-block-2)
(python-indent-inside-paren-block-3)
(python-indent-inside-paren-block-4): New tests.
(python-indent-inside-paren-5, python-indent-dedenters-8): Modify
according to the new context.
* etc/NEWS: Document the new user option.  (Bug#62696)
---
 etc/NEWS                            |   8 ++
 lisp/progmodes/python.el            |  41 ++++++++--
 test/lisp/progmodes/python-tests.el | 116 +++++++++++++++++++++++++++-
 3 files changed, 156 insertions(+), 9 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index 5e1fd76e99e..1fd97c35208 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -266,6 +266,14 @@ When non-nil, it will automatically register every package as a
 project, that you can quickly select using 'project-switch-project'
 ('C-x p p').
 
+** Python mode
+
+*** New user option 'python-indent-block-paren-deeper'.
+Increase indentation inside parens of a block.
+When this variable is set to non-nil and the contents of a block
+inside parens are indented to the same level as outside the same
+block, increase the indentation of the line.
+
 \f
 * New Modes and Packages in Emacs 30.1
 
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index bbabce80b4d..0868b77f201 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -1270,6 +1270,15 @@ python-indent-def-block-scale
   :type 'integer
   :safe 'natnump)
 
+(defcustom python-indent-block-paren-deeper nil
+  "Increase indentation inside parens of a block.
+When this variable is set to non-nil and the contents of a block
+inside parens are indented to the same level as outside the same
+block, increase the indentation of the line."
+  :version "30.1"
+  :type 'boolean
+  :safe 'booleanp)
+
 (defvar python-indent-current-level 0
   "Deprecated var available for compatibility.")
 
@@ -1367,6 +1376,10 @@ python-indent-context
  - Point is inside a paren with items starting in their own line
    from a block start.
  - START is the position of the open paren.
+:inside-paren-from-block
+ - 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.
 
 :after-backslash
  - Fallback case when point is after backslash.
@@ -1450,12 +1463,16 @@ python-indent-context
              (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))))))))
+             (t (let ((after-start (save-excursion
+                               (goto-char (1+ start))
+                               (skip-syntax-forward "(" 1)
+                               (skip-syntax-forward " ")
+                               (point))))
+                  (if (save-excursion
+                        (python-nav-beginning-of-statement)
+                        (python-info-looking-at-beginning-of-block))
+                      (cons :inside-paren-from-block after-start)
+                    (cons :inside-paren after-start))))))))
        ;; After backslash.
        ((let ((start (when (not (python-syntax-comment-or-string-p ppss))
                        (python-info-line-ends-backslash-p
@@ -1603,7 +1620,17 @@ python-indent--calculate-indentation
         (`(,(or :inside-paren-newline-start-from-block) . ,start)
          (goto-char start)
          (+ (current-indentation)
-            (* python-indent-offset python-indent-def-block-scale))))))
+            (* python-indent-offset python-indent-def-block-scale)))
+        (`(,:inside-paren-from-block . ,start)
+         (goto-char start)
+         (let ((column (current-column)))
+           (if (and python-indent-block-paren-deeper
+                    (= column (+ (save-excursion
+                                   (python-nav-beginning-of-statement)
+                                   (current-indentation))
+                                 python-indent-offset)))
+               (+ column python-indent-offset)
+             column))))))
 
 (defun python-indent--calculate-levels (indentation)
   "Calculate levels list given INDENTATION.
diff --git a/test/lisp/progmodes/python-tests.el b/test/lisp/progmodes/python-tests.el
index 50153e66da5..60b11d572cf 100644
--- a/test/lisp/progmodes/python-tests.el
+++ b/test/lisp/progmodes/python-tests.el
@@ -1139,7 +1139,7 @@ python-indent-inside-paren-5
    (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))
+   (should (eq (car (python-indent-context)) :inside-paren-from-block))
    (should (= (python-indent-calculate-indentation) 7))
    (forward-line 1)
    (should (eq (car (python-indent-context)) :after-block-start))
@@ -1174,6 +1174,118 @@ 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-block-1 ()
+  "`python-indent-block-paren-deeper' set to nil (default).
+See Bug#62696."
+  (python-tests-with-temp-buffer
+   "
+if ('VALUE' in my_unnecessarily_long_dictionary and
+    some_other_long_condition_case):
+    do_something()
+elif (some_case or
+      another_case):
+    do_another()
+"
+   (python-tests-look-at "if")
+   (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-from-block))
+   (should (= (python-indent-calculate-indentation) 4))
+   (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)) :at-dedenter-block-start))
+   (should (= (python-indent-calculate-indentation) 0))
+   (forward-line 1)
+   (should (eq (car (python-indent-context)) :inside-paren-from-block))
+   (should (= (python-indent-calculate-indentation) 6))
+   (forward-line 1)
+   (should (eq (car (python-indent-context)) :after-block-start))
+   (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-inside-paren-block-2 ()
+  "`python-indent-block-paren-deeper' set to t.
+See Bug#62696."
+  (python-tests-with-temp-buffer
+   "
+if ('VALUE' in my_unnecessarily_long_dictionary and
+        some_other_long_condition_case):
+    do_something()
+elif (some_case or
+      another_case):
+    do_another()
+"
+   (let ((python-indent-block-paren-deeper t))
+     (python-tests-look-at "if")
+     (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-from-block))
+     (should (= (python-indent-calculate-indentation) 8))
+     (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)) :at-dedenter-block-start))
+     (should (= (python-indent-calculate-indentation) 0))
+     (forward-line 1)
+     (should (eq (car (python-indent-context)) :inside-paren-from-block))
+     (should (= (python-indent-calculate-indentation) 6))
+     (forward-line 1)
+     (should (eq (car (python-indent-context)) :after-block-start))
+     (should (= (python-indent-calculate-indentation) 4)))))
+
+(ert-deftest python-indent-inside-paren-block-3 ()
+  "With backslash.  `python-indent-block-paren-deeper' set to nil (default).
+See Bug#62696."
+  (python-tests-with-temp-buffer
+   "
+if 'VALUE' in my_uncessarily_long_dictionary and\\
+   (some_other_long_condition_case or
+    another_case):
+    do_something()
+"
+   (python-tests-look-at "if")
+   (should (eq (car (python-indent-context)) :no-indent))
+   (should (= (python-indent-calculate-indentation) 0))
+   (forward-line 1)
+   (should (eq (car (python-indent-context))
+               :after-backslash-block-continuation))
+   (should (= (python-indent-calculate-indentation) 3))
+   (forward-line 1)
+   (should (eq (car (python-indent-context)) :inside-paren-from-block))
+   (should (= (python-indent-calculate-indentation) 4))
+   (forward-line 1)
+   (should (eq (car (python-indent-context)) :after-block-start))
+   (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-inside-paren-block-4 ()
+  "With backslash.  `python-indent-block-paren-deeper' set to t.
+See Bug#62696."
+  (python-tests-with-temp-buffer
+   "
+if 'VALUE' in my_uncessarily_long_dictionary and\\
+   (some_other_long_condition_case or
+        another_case):
+    do_something()
+"
+   (let ((python-indent-block-paren-deeper t))
+     (python-tests-look-at "if")
+     (should (eq (car (python-indent-context)) :no-indent))
+     (should (= (python-indent-calculate-indentation) 0))
+     (forward-line 1)
+     (should (eq (car (python-indent-context))
+                 :after-backslash-block-continuation))
+     (should (= (python-indent-calculate-indentation) 3))
+     (forward-line 1)
+     (should (eq (car (python-indent-context)) :inside-paren-from-block))
+     (should (= (python-indent-calculate-indentation) 8))
+     (forward-line 1)
+     (should (eq (car (python-indent-context)) :after-block-start))
+     (should (= (python-indent-calculate-indentation) 4)))))
+
 (ert-deftest python-indent-after-block-1 ()
   "The most simple after-block case that shouldn't fail."
   (python-tests-with-temp-buffer
@@ -1670,7 +1782,7 @@ python-indent-dedenters-8
    (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-from-block))
    (should (= (python-indent-calculate-indentation) 6))
    (python-indent-line)
    (should (= (python-indent-calculate-indentation t) 4))
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* bug#62696: python.el: Extra indentation for conditionals
  2023-04-11 11:11       ` Gustaf Waldemarson
@ 2023-04-12 15:26         ` kobarity
  2023-04-16 13:24           ` kobarity
  0 siblings, 1 reply; 14+ messages in thread
From: kobarity @ 2023-04-12 15:26 UTC (permalink / raw)
  To: Gustaf Waldemarson; +Cc: 62696


Gustaf Waldemarson wrote:
> "Increase indentation inside parens of a block.
> When this variable is set to non-nil and the contents of a block
> inside parens are indented to the same level as outside the same
> block, increase the indentation of the line."

Hi Gustaf,

Thank you for your suggestion.  I think this is more readable and
would like to make changes in this direction.

Just to be clear, what I wanted to express by "some items follow the
opening paren on the same line in a block statement" is that it only
works with the code like the following:

#+begin_src python
if ("VALUE" in my_unnecessarily_long_dictionary
        and some_other_long_condition_case):
    do_something()
#+end_src

And it does not work for the code like the following:

#+begin_src python
if (
    "VALUE" in my_unnecessarily_long_dictionary
    and some_other_long_condition_case):
    do_something()
#+end_src

This is intentional.  Because this code is usually written as follows:

#+begin_src python
if (
    "VALUE" in my_unnecessarily_long_dictionary
    and some_other_long_condition_case
):
    do_something()
#+end_src

In this case, there is no need for extra indentation.  As I wrote in
the previous mail, this case is processed by the existing code and can
be customized with `python-indent-def-block-scale'.

However, this may be a marginal difference.  In any case, I will wait
a few more days for other comments and hope to revise the patch over
the weekend.

Best regards,
kobarity





^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#62696: python.el: Extra indentation for conditionals
  2023-04-12 15:26         ` kobarity
@ 2023-04-16 13:24           ` kobarity
  2023-04-16 15:49             ` Gustaf Waldemarson
  0 siblings, 1 reply; 14+ messages in thread
From: kobarity @ 2023-04-16 13:24 UTC (permalink / raw)
  To: Gustaf Waldemarson; +Cc: 62696

[-- Attachment #1: Type: text/plain, Size: 1237 bytes --]


kobarity wrote:
> Gustaf Waldemarson wrote:
> > "Increase indentation inside parens of a block.
> > When this variable is set to non-nil and the contents of a block
> > inside parens are indented to the same level as outside the same
> > block, increase the indentation of the line."
> 
> Thank you for your suggestion.  I think this is more readable and
> would like to make changes in this direction.

I have concluded that it's better to include an example in the
docstring of `python-indent-block-paren-deeper'.  How about the
following?  I also attached the updated patch.

"Increase indentation inside parens of a block.
If non-nil, increase the indentation of the lines inside parens
in a header of a block when they are indented to the same level
as the body of the block:

    if (some_expression
            and another_expression):
        do_something()

instead of:

    if (some_expression
        and another_expression):
        do_something()

This variable only works if the opening paren is followed by
non-whitespace characters on the same line.  Modify
`python-indent-def-block-scale' to customize the case where
non-whitespace character does not follow the opening paren on the
same line."

Best regards,
kobarity

[-- Attachment #2: 0001-Add-a-new-user-option-in-Python-mode-to-improve-the-.patch --]
[-- Type: application/octet-stream, Size: 11489 bytes --]

From 6bc15443e9f16b5bfd51254ab069afb20d9b21df Mon Sep 17 00:00:00 2001
From: kobarity <kobarity@gmail.com>
Date: Sun, 16 Apr 2023 22:18:39 +0900
Subject: [PATCH] Add a new user option in Python mode to improve the
 indentation

* lisp/progmodes/python.el (python-indent-block-paren-deeper): New
user option.
(python-indent-context): Add a new context :inside-paren-from-block.
(python-indent--calculate-indentation): Modify according to
`python-indent-block-paren-deeper' and :inside-paren-from-block.
* test/lisp/progmodes/python-tests.el
(python-indent-inside-paren-block-1)
(python-indent-inside-paren-block-2)
(python-indent-inside-paren-block-3)
(python-indent-inside-paren-block-4): New tests.
(python-indent-inside-paren-5, python-indent-dedenters-8): Modify
according to the new context.
* etc/NEWS: Document the new user option.  (Bug#62696)
---
 etc/NEWS                            |  17 ++++
 lisp/progmodes/python.el            |  57 ++++++++++++--
 test/lisp/progmodes/python-tests.el | 116 +++++++++++++++++++++++++++-
 3 files changed, 181 insertions(+), 9 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index b121002b246..9e318812714 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -276,6 +276,23 @@ distracting and easily confused with actual code, or a significant
 early aid that relieves you from moving the buffer or reaching for the
 mouse to consult an error message.
 
+** Python mode
+
+*** New user option 'python-indent-block-paren-deeper'.
+If non-nil, increase the indentation of the lines inside parens in a
+header of a block when they are indented to the same level as the body
+of the block:
+
+    if (some_expression
+            and another_expression):
+        do_something()
+
+instead of:
+
+    if (some_expression
+        and another_expression):
+        do_something()
+
 \f
 * New Modes and Packages in Emacs 30.1
 
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index bbabce80b4d..9ae95ecfc1c 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -1270,6 +1270,31 @@ python-indent-def-block-scale
   :type 'integer
   :safe 'natnump)
 
+(defcustom python-indent-block-paren-deeper nil
+  "Increase indentation inside parens of a block.
+If non-nil, increase the indentation of the lines inside parens
+in a header of a block when they are indented to the same level
+as the body of the block:
+
+    if (some_expression
+            and another_expression):
+        do_something()
+
+instead of:
+
+    if (some_expression
+        and another_expression):
+        do_something()
+
+This variable only works if the opening paren is followed by
+non-whitespace characters on the same line.  Modify
+`python-indent-def-block-scale' to customize the case where
+non-whitespace character does not follow the opening paren on the
+same line."
+  :version "30.1"
+  :type 'boolean
+  :safe 'booleanp)
+
 (defvar python-indent-current-level 0
   "Deprecated var available for compatibility.")
 
@@ -1367,6 +1392,10 @@ python-indent-context
  - Point is inside a paren with items starting in their own line
    from a block start.
  - START is the position of the open paren.
+:inside-paren-from-block
+ - 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.
 
 :after-backslash
  - Fallback case when point is after backslash.
@@ -1450,12 +1479,16 @@ python-indent-context
              (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))))))))
+             (t (let ((after-start (save-excursion
+                               (goto-char (1+ start))
+                               (skip-syntax-forward "(" 1)
+                               (skip-syntax-forward " ")
+                               (point))))
+                  (if (save-excursion
+                        (python-nav-beginning-of-statement)
+                        (python-info-looking-at-beginning-of-block))
+                      (cons :inside-paren-from-block after-start)
+                    (cons :inside-paren after-start))))))))
        ;; After backslash.
        ((let ((start (when (not (python-syntax-comment-or-string-p ppss))
                        (python-info-line-ends-backslash-p
@@ -1603,7 +1636,17 @@ python-indent--calculate-indentation
         (`(,(or :inside-paren-newline-start-from-block) . ,start)
          (goto-char start)
          (+ (current-indentation)
-            (* python-indent-offset python-indent-def-block-scale))))))
+            (* python-indent-offset python-indent-def-block-scale)))
+        (`(,:inside-paren-from-block . ,start)
+         (goto-char start)
+         (let ((column (current-column)))
+           (if (and python-indent-block-paren-deeper
+                    (= column (+ (save-excursion
+                                   (python-nav-beginning-of-statement)
+                                   (current-indentation))
+                                 python-indent-offset)))
+               (+ column python-indent-offset)
+             column))))))
 
 (defun python-indent--calculate-levels (indentation)
   "Calculate levels list given INDENTATION.
diff --git a/test/lisp/progmodes/python-tests.el b/test/lisp/progmodes/python-tests.el
index 50153e66da5..60b11d572cf 100644
--- a/test/lisp/progmodes/python-tests.el
+++ b/test/lisp/progmodes/python-tests.el
@@ -1139,7 +1139,7 @@ python-indent-inside-paren-5
    (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))
+   (should (eq (car (python-indent-context)) :inside-paren-from-block))
    (should (= (python-indent-calculate-indentation) 7))
    (forward-line 1)
    (should (eq (car (python-indent-context)) :after-block-start))
@@ -1174,6 +1174,118 @@ 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-block-1 ()
+  "`python-indent-block-paren-deeper' set to nil (default).
+See Bug#62696."
+  (python-tests-with-temp-buffer
+   "
+if ('VALUE' in my_unnecessarily_long_dictionary and
+    some_other_long_condition_case):
+    do_something()
+elif (some_case or
+      another_case):
+    do_another()
+"
+   (python-tests-look-at "if")
+   (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-from-block))
+   (should (= (python-indent-calculate-indentation) 4))
+   (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)) :at-dedenter-block-start))
+   (should (= (python-indent-calculate-indentation) 0))
+   (forward-line 1)
+   (should (eq (car (python-indent-context)) :inside-paren-from-block))
+   (should (= (python-indent-calculate-indentation) 6))
+   (forward-line 1)
+   (should (eq (car (python-indent-context)) :after-block-start))
+   (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-inside-paren-block-2 ()
+  "`python-indent-block-paren-deeper' set to t.
+See Bug#62696."
+  (python-tests-with-temp-buffer
+   "
+if ('VALUE' in my_unnecessarily_long_dictionary and
+        some_other_long_condition_case):
+    do_something()
+elif (some_case or
+      another_case):
+    do_another()
+"
+   (let ((python-indent-block-paren-deeper t))
+     (python-tests-look-at "if")
+     (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-from-block))
+     (should (= (python-indent-calculate-indentation) 8))
+     (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)) :at-dedenter-block-start))
+     (should (= (python-indent-calculate-indentation) 0))
+     (forward-line 1)
+     (should (eq (car (python-indent-context)) :inside-paren-from-block))
+     (should (= (python-indent-calculate-indentation) 6))
+     (forward-line 1)
+     (should (eq (car (python-indent-context)) :after-block-start))
+     (should (= (python-indent-calculate-indentation) 4)))))
+
+(ert-deftest python-indent-inside-paren-block-3 ()
+  "With backslash.  `python-indent-block-paren-deeper' set to nil (default).
+See Bug#62696."
+  (python-tests-with-temp-buffer
+   "
+if 'VALUE' in my_uncessarily_long_dictionary and\\
+   (some_other_long_condition_case or
+    another_case):
+    do_something()
+"
+   (python-tests-look-at "if")
+   (should (eq (car (python-indent-context)) :no-indent))
+   (should (= (python-indent-calculate-indentation) 0))
+   (forward-line 1)
+   (should (eq (car (python-indent-context))
+               :after-backslash-block-continuation))
+   (should (= (python-indent-calculate-indentation) 3))
+   (forward-line 1)
+   (should (eq (car (python-indent-context)) :inside-paren-from-block))
+   (should (= (python-indent-calculate-indentation) 4))
+   (forward-line 1)
+   (should (eq (car (python-indent-context)) :after-block-start))
+   (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-inside-paren-block-4 ()
+  "With backslash.  `python-indent-block-paren-deeper' set to t.
+See Bug#62696."
+  (python-tests-with-temp-buffer
+   "
+if 'VALUE' in my_uncessarily_long_dictionary and\\
+   (some_other_long_condition_case or
+        another_case):
+    do_something()
+"
+   (let ((python-indent-block-paren-deeper t))
+     (python-tests-look-at "if")
+     (should (eq (car (python-indent-context)) :no-indent))
+     (should (= (python-indent-calculate-indentation) 0))
+     (forward-line 1)
+     (should (eq (car (python-indent-context))
+                 :after-backslash-block-continuation))
+     (should (= (python-indent-calculate-indentation) 3))
+     (forward-line 1)
+     (should (eq (car (python-indent-context)) :inside-paren-from-block))
+     (should (= (python-indent-calculate-indentation) 8))
+     (forward-line 1)
+     (should (eq (car (python-indent-context)) :after-block-start))
+     (should (= (python-indent-calculate-indentation) 4)))))
+
 (ert-deftest python-indent-after-block-1 ()
   "The most simple after-block case that shouldn't fail."
   (python-tests-with-temp-buffer
@@ -1670,7 +1782,7 @@ python-indent-dedenters-8
    (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-from-block))
    (should (= (python-indent-calculate-indentation) 6))
    (python-indent-line)
    (should (= (python-indent-calculate-indentation t) 4))
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* bug#62696: python.el: Extra indentation for conditionals
  2023-04-16 13:24           ` kobarity
@ 2023-04-16 15:49             ` Gustaf Waldemarson
  2023-04-18 14:23               ` kobarity
  0 siblings, 1 reply; 14+ messages in thread
From: Gustaf Waldemarson @ 2023-04-16 15:49 UTC (permalink / raw)
  To: kobarity; +Cc: 62696

[-- Attachment #1: Type: text/plain, Size: 1660 bytes --]

Hello,

I think that's a really good idea actually. It might also be a good idea to
add a
negative example (i.e., the black-indentation style you mentioned earlier),
or
a reference to one (maybe to the tests?)

Best regards,
Gustaf

Den sön 16 apr. 2023 kl 15:24 skrev kobarity <kobarity@gmail.com>:

>
> kobarity wrote:
> > Gustaf Waldemarson wrote:
> > > "Increase indentation inside parens of a block.
> > > When this variable is set to non-nil and the contents of a block
> > > inside parens are indented to the same level as outside the same
> > > block, increase the indentation of the line."
> >
> > Thank you for your suggestion.  I think this is more readable and
> > would like to make changes in this direction.
>
> I have concluded that it's better to include an example in the
> docstring of `python-indent-block-paren-deeper'.  How about the
> following?  I also attached the updated patch.
>
> "Increase indentation inside parens of a block.
> If non-nil, increase the indentation of the lines inside parens
> in a header of a block when they are indented to the same level
> as the body of the block:
>
>     if (some_expression
>             and another_expression):
>         do_something()
>
> instead of:
>
>     if (some_expression
>         and another_expression):
>         do_something()
>
> This variable only works if the opening paren is followed by
> non-whitespace characters on the same line.  Modify
> `python-indent-def-block-scale' to customize the case where
> non-whitespace character does not follow the opening paren on the
> same line."
>
> Best regards,
> kobarity
>

[-- Attachment #2: Type: text/html, Size: 2206 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#62696: python.el: Extra indentation for conditionals
  2023-04-16 15:49             ` Gustaf Waldemarson
@ 2023-04-18 14:23               ` kobarity
  2023-04-20  8:22                 ` Eli Zaretskii
  0 siblings, 1 reply; 14+ messages in thread
From: kobarity @ 2023-04-18 14:23 UTC (permalink / raw)
  To: Gustaf Waldemarson; +Cc: 62696, Dmitry Gutov, Ross Donaldson, Noam Postavsky

[-- Attachment #1: Type: text/plain, Size: 1493 bytes --]


Gustaf Waldemarson wrote:
> I think that's a really good idea actually. It might also be a good idea to add a
> negative example (i.e., the black-indentation style you mentioned earlier), or
> a reference to one (maybe to the tests?)

I think the best way is to update the docstring of
`python-indent-def-block-scale'.  So, I am also CC'ing this mail to
the members of Bug#28475, where `python-indent-def-block-scale' was
introduced.

I made a separate patch to update the docstring of
`python-indent-def-block-scale'.  I think the name
`python-indent-def-block-scale' itself is a bit misleading, but I have
left it as is for compatibility.  The following is my proposal of the
docstring.  What do you think?

"Multiplier applied to indentation inside multi-line blocks.
The indentation in parens in the block header will be the current
indentation plus `python-indent-offset' multiplied by this
variable.  For example, the arguments are indented as follows if
this variable is 1:

    def do_something(
        arg1,
        arg2
    ):
        print('hello')

if this variable is 2 (default):

    def do_something(
            arg1,
            arg2):
        print('hello')

This variable has an effect on all blocks, not just def block.
This variable only works if the opening paren is not followed by
non-whitespace characters on the same line.  Modify
`python-indent-block-paren-deeper' to customize the case where
non-whitespace characters follow the opening paren on the same
line."

[-- Attachment #2: 0001-Add-a-new-user-option-in-Python-mode-to-improve-the-.patch --]
[-- Type: application/octet-stream, Size: 11493 bytes --]

From ee243dc685cb0cddeb66c53018633682791dbd0a Mon Sep 17 00:00:00 2001
From: kobarity <kobarity@gmail.com>
Date: Sun, 16 Apr 2023 22:18:39 +0900
Subject: [PATCH 1/2] Add a new user option in Python mode to improve the
 indentation

* lisp/progmodes/python.el (python-indent-block-paren-deeper): New
user option.
(python-indent-context): Add a new context :inside-paren-from-block.
(python-indent--calculate-indentation): Modify according to
`python-indent-block-paren-deeper' and :inside-paren-from-block.
* test/lisp/progmodes/python-tests.el
(python-indent-inside-paren-block-1)
(python-indent-inside-paren-block-2)
(python-indent-inside-paren-block-3)
(python-indent-inside-paren-block-4): New tests.
(python-indent-inside-paren-5, python-indent-dedenters-8): Modify
according to the new context.
* etc/NEWS: Document the new user option.  (Bug#62696)
---
 etc/NEWS                            |  17 ++++
 lisp/progmodes/python.el            |  57 ++++++++++++--
 test/lisp/progmodes/python-tests.el | 116 +++++++++++++++++++++++++++-
 3 files changed, 181 insertions(+), 9 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index b121002b246..9e318812714 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -276,6 +276,23 @@ distracting and easily confused with actual code, or a significant
 early aid that relieves you from moving the buffer or reaching for the
 mouse to consult an error message.
 
+** Python mode
+
+*** New user option 'python-indent-block-paren-deeper'.
+If non-nil, increase the indentation of the lines inside parens in a
+header of a block when they are indented to the same level as the body
+of the block:
+
+    if (some_expression
+            and another_expression):
+        do_something()
+
+instead of:
+
+    if (some_expression
+        and another_expression):
+        do_something()
+
 \f
 * New Modes and Packages in Emacs 30.1
 
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index bbabce80b4d..9ae95ecfc1c 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -1270,6 +1270,31 @@ python-indent-def-block-scale
   :type 'integer
   :safe 'natnump)
 
+(defcustom python-indent-block-paren-deeper nil
+  "Increase indentation inside parens of a block.
+If non-nil, increase the indentation of the lines inside parens
+in a header of a block when they are indented to the same level
+as the body of the block:
+
+    if (some_expression
+            and another_expression):
+        do_something()
+
+instead of:
+
+    if (some_expression
+        and another_expression):
+        do_something()
+
+This variable only works if the opening paren is followed by
+non-whitespace characters on the same line.  Modify
+`python-indent-def-block-scale' to customize the case where
+non-whitespace character does not follow the opening paren on the
+same line."
+  :version "30.1"
+  :type 'boolean
+  :safe 'booleanp)
+
 (defvar python-indent-current-level 0
   "Deprecated var available for compatibility.")
 
@@ -1367,6 +1392,10 @@ python-indent-context
  - Point is inside a paren with items starting in their own line
    from a block start.
  - START is the position of the open paren.
+:inside-paren-from-block
+ - 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.
 
 :after-backslash
  - Fallback case when point is after backslash.
@@ -1450,12 +1479,16 @@ python-indent-context
              (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))))))))
+             (t (let ((after-start (save-excursion
+                               (goto-char (1+ start))
+                               (skip-syntax-forward "(" 1)
+                               (skip-syntax-forward " ")
+                               (point))))
+                  (if (save-excursion
+                        (python-nav-beginning-of-statement)
+                        (python-info-looking-at-beginning-of-block))
+                      (cons :inside-paren-from-block after-start)
+                    (cons :inside-paren after-start))))))))
        ;; After backslash.
        ((let ((start (when (not (python-syntax-comment-or-string-p ppss))
                        (python-info-line-ends-backslash-p
@@ -1603,7 +1636,17 @@ python-indent--calculate-indentation
         (`(,(or :inside-paren-newline-start-from-block) . ,start)
          (goto-char start)
          (+ (current-indentation)
-            (* python-indent-offset python-indent-def-block-scale))))))
+            (* python-indent-offset python-indent-def-block-scale)))
+        (`(,:inside-paren-from-block . ,start)
+         (goto-char start)
+         (let ((column (current-column)))
+           (if (and python-indent-block-paren-deeper
+                    (= column (+ (save-excursion
+                                   (python-nav-beginning-of-statement)
+                                   (current-indentation))
+                                 python-indent-offset)))
+               (+ column python-indent-offset)
+             column))))))
 
 (defun python-indent--calculate-levels (indentation)
   "Calculate levels list given INDENTATION.
diff --git a/test/lisp/progmodes/python-tests.el b/test/lisp/progmodes/python-tests.el
index 50153e66da5..60b11d572cf 100644
--- a/test/lisp/progmodes/python-tests.el
+++ b/test/lisp/progmodes/python-tests.el
@@ -1139,7 +1139,7 @@ python-indent-inside-paren-5
    (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))
+   (should (eq (car (python-indent-context)) :inside-paren-from-block))
    (should (= (python-indent-calculate-indentation) 7))
    (forward-line 1)
    (should (eq (car (python-indent-context)) :after-block-start))
@@ -1174,6 +1174,118 @@ 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-block-1 ()
+  "`python-indent-block-paren-deeper' set to nil (default).
+See Bug#62696."
+  (python-tests-with-temp-buffer
+   "
+if ('VALUE' in my_unnecessarily_long_dictionary and
+    some_other_long_condition_case):
+    do_something()
+elif (some_case or
+      another_case):
+    do_another()
+"
+   (python-tests-look-at "if")
+   (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-from-block))
+   (should (= (python-indent-calculate-indentation) 4))
+   (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)) :at-dedenter-block-start))
+   (should (= (python-indent-calculate-indentation) 0))
+   (forward-line 1)
+   (should (eq (car (python-indent-context)) :inside-paren-from-block))
+   (should (= (python-indent-calculate-indentation) 6))
+   (forward-line 1)
+   (should (eq (car (python-indent-context)) :after-block-start))
+   (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-inside-paren-block-2 ()
+  "`python-indent-block-paren-deeper' set to t.
+See Bug#62696."
+  (python-tests-with-temp-buffer
+   "
+if ('VALUE' in my_unnecessarily_long_dictionary and
+        some_other_long_condition_case):
+    do_something()
+elif (some_case or
+      another_case):
+    do_another()
+"
+   (let ((python-indent-block-paren-deeper t))
+     (python-tests-look-at "if")
+     (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-from-block))
+     (should (= (python-indent-calculate-indentation) 8))
+     (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)) :at-dedenter-block-start))
+     (should (= (python-indent-calculate-indentation) 0))
+     (forward-line 1)
+     (should (eq (car (python-indent-context)) :inside-paren-from-block))
+     (should (= (python-indent-calculate-indentation) 6))
+     (forward-line 1)
+     (should (eq (car (python-indent-context)) :after-block-start))
+     (should (= (python-indent-calculate-indentation) 4)))))
+
+(ert-deftest python-indent-inside-paren-block-3 ()
+  "With backslash.  `python-indent-block-paren-deeper' set to nil (default).
+See Bug#62696."
+  (python-tests-with-temp-buffer
+   "
+if 'VALUE' in my_uncessarily_long_dictionary and\\
+   (some_other_long_condition_case or
+    another_case):
+    do_something()
+"
+   (python-tests-look-at "if")
+   (should (eq (car (python-indent-context)) :no-indent))
+   (should (= (python-indent-calculate-indentation) 0))
+   (forward-line 1)
+   (should (eq (car (python-indent-context))
+               :after-backslash-block-continuation))
+   (should (= (python-indent-calculate-indentation) 3))
+   (forward-line 1)
+   (should (eq (car (python-indent-context)) :inside-paren-from-block))
+   (should (= (python-indent-calculate-indentation) 4))
+   (forward-line 1)
+   (should (eq (car (python-indent-context)) :after-block-start))
+   (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-inside-paren-block-4 ()
+  "With backslash.  `python-indent-block-paren-deeper' set to t.
+See Bug#62696."
+  (python-tests-with-temp-buffer
+   "
+if 'VALUE' in my_uncessarily_long_dictionary and\\
+   (some_other_long_condition_case or
+        another_case):
+    do_something()
+"
+   (let ((python-indent-block-paren-deeper t))
+     (python-tests-look-at "if")
+     (should (eq (car (python-indent-context)) :no-indent))
+     (should (= (python-indent-calculate-indentation) 0))
+     (forward-line 1)
+     (should (eq (car (python-indent-context))
+                 :after-backslash-block-continuation))
+     (should (= (python-indent-calculate-indentation) 3))
+     (forward-line 1)
+     (should (eq (car (python-indent-context)) :inside-paren-from-block))
+     (should (= (python-indent-calculate-indentation) 8))
+     (forward-line 1)
+     (should (eq (car (python-indent-context)) :after-block-start))
+     (should (= (python-indent-calculate-indentation) 4)))))
+
 (ert-deftest python-indent-after-block-1 ()
   "The most simple after-block case that shouldn't fail."
   (python-tests-with-temp-buffer
@@ -1670,7 +1782,7 @@ python-indent-dedenters-8
    (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-from-block))
    (should (= (python-indent-calculate-indentation) 6))
    (python-indent-line)
    (should (= (python-indent-calculate-indentation t) 4))
-- 
2.34.1


[-- Attachment #3: 0002-Improve-docstring-of-python-indent-def-block-scale.patch --]
[-- Type: application/octet-stream, Size: 1660 bytes --]

From 3b3a0dd9394b7905ad47401e4ce2166d5cb79c10 Mon Sep 17 00:00:00 2001
From: kobarity <kobarity@gmail.com>
Date: Tue, 18 Apr 2023 22:52:18 +0900
Subject: [PATCH 2/2] Improve docstring of python-indent-def-block-scale

* lisp/progmodes/python.el (python-indent-def-block-scale): Improve
docstring.  (Bug#62696)
---
 lisp/progmodes/python.el | 26 +++++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 9ae95ecfc1c..3bec400006e 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -1265,7 +1265,31 @@ python-indent-trigger-commands
   :type '(repeat symbol))
 
 (defcustom python-indent-def-block-scale 2
-  "Multiplier applied to indentation inside multi-line def blocks."
+  "Multiplier applied to indentation inside multi-line blocks.
+The indentation in parens in the block header will be the current
+indentation plus `python-indent-offset' multiplied by this
+variable.  For example, the arguments are indented as follows if
+this variable is 1:
+
+    def do_something(
+        arg1,
+        arg2
+    ):
+        print('hello')
+
+if this variable is 2 (default):
+
+    def do_something(
+            arg1,
+            arg2):
+        print('hello')
+
+This variable has an effect on all blocks, not just def block.
+This variable only works if the opening paren is not followed by
+non-whitespace characters on the same line.  Modify
+`python-indent-block-paren-deeper' to customize the case where
+non-whitespace characters follow the opening paren on the same
+line."
   :version "26.1"
   :type 'integer
   :safe 'natnump)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* bug#62696: python.el: Extra indentation for conditionals
  2023-04-18 14:23               ` kobarity
@ 2023-04-20  8:22                 ` Eli Zaretskii
  2023-04-20 13:40                   ` Gustaf Waldemarson
  0 siblings, 1 reply; 14+ messages in thread
From: Eli Zaretskii @ 2023-04-20  8:22 UTC (permalink / raw)
  To: kobarity; +Cc: 62696, npostavs, gustaf.waldemarson, gastove, dgutov

> Cc: 62696@debbugs.gnu.org, Dmitry Gutov <dgutov@yandex.ru>,
>  Ross Donaldson <gastove@gmail.com>,
>  Noam Postavsky <npostavs@users.sourceforge.net>
> Date: Tue, 18 Apr 2023 23:23:35 +0900
> From: kobarity <kobarity@gmail.com>
> 
> Gustaf Waldemarson wrote:
> > I think that's a really good idea actually. It might also be a good idea to add a
> > negative example (i.e., the black-indentation style you mentioned earlier), or
> > a reference to one (maybe to the tests?)
> 
> I think the best way is to update the docstring of
> `python-indent-def-block-scale'.  So, I am also CC'ing this mail to
> the members of Bug#28475, where `python-indent-def-block-scale' was
> introduced.
> 
> I made a separate patch to update the docstring of
> `python-indent-def-block-scale'.  I think the name
> `python-indent-def-block-scale' itself is a bit misleading, but I have
> left it as is for compatibility.  The following is my proposal of the
> docstring.  What do you think?
> 
> "Multiplier applied to indentation inside multi-line blocks.
> The indentation in parens in the block header will be the current
> indentation plus `python-indent-offset' multiplied by this
> variable.  For example, the arguments are indented as follows if
> this variable is 1:
> 
>     def do_something(
>         arg1,
>         arg2
>     ):
>         print('hello')
> 
> if this variable is 2 (default):
> 
>     def do_something(
>             arg1,
>             arg2):
>         print('hello')
> 
> This variable has an effect on all blocks, not just def block.
> This variable only works if the opening paren is not followed by
> non-whitespace characters on the same line.  Modify
> `python-indent-block-paren-deeper' to customize the case where
> non-whitespace characters follow the opening paren on the same
> line."

Do these two patches replace every other patch posted in this
discussion?

Are these two patches ready to be installed, or are you still
discussing the issue?

Thanks.





^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#62696: python.el: Extra indentation for conditionals
  2023-04-20  8:22                 ` Eli Zaretskii
@ 2023-04-20 13:40                   ` Gustaf Waldemarson
  2023-04-20 14:19                     ` kobarity
  0 siblings, 1 reply; 14+ messages in thread
From: Gustaf Waldemarson @ 2023-04-20 13:40 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 62696, kobarity, npostavs, gastove, dgutov

[-- Attachment #1: Type: text/plain, Size: 3084 bytes --]

Apologies, I think Kobarity was waiting for a response from me and
thus held these up:

> I made a separate patch to update the docstring of
> `python-indent-def-block-scale'.  I think the name
> `python-indent-def-block-scale' itself is a bit misleading, but I have
> left it as is for compatibility.  The following is my proposal of the
> docstring.  What do you think?

It mostly looks good to me, but perhaps it would make sense to still output
the closing paren on the same line as the first example? I suppose it might
not make sense from a style-perspective, but I guess someone reading through
this mostly care about comparing indentation depths

> Do these two patches replace every other patch posted in this
> discussion?

> Are these two patches ready to be installed, or are you still
> discussing the issue?

I believe that's the case, but I would wait for a response from Kobarity
following this.

Best regards,
Gustaf

Den tors 20 apr. 2023 kl 10:22 skrev Eli Zaretskii <eliz@gnu.org>:

> > Cc: 62696@debbugs.gnu.org, Dmitry Gutov <dgutov@yandex.ru>,
> >  Ross Donaldson <gastove@gmail.com>,
> >  Noam Postavsky <npostavs@users.sourceforge.net>
> > Date: Tue, 18 Apr 2023 23:23:35 +0900
> > From: kobarity <kobarity@gmail.com>
> >
> > Gustaf Waldemarson wrote:
> > > I think that's a really good idea actually. It might also be a good
> idea to add a
> > > negative example (i.e., the black-indentation style you mentioned
> earlier), or
> > > a reference to one (maybe to the tests?)
> >
> > I think the best way is to update the docstring of
> > `python-indent-def-block-scale'.  So, I am also CC'ing this mail to
> > the members of Bug#28475, where `python-indent-def-block-scale' was
> > introduced.
> >
> > I made a separate patch to update the docstring of
> > `python-indent-def-block-scale'.  I think the name
> > `python-indent-def-block-scale' itself is a bit misleading, but I have
> > left it as is for compatibility.  The following is my proposal of the
> > docstring.  What do you think?
> >
> > "Multiplier applied to indentation inside multi-line blocks.
> > The indentation in parens in the block header will be the current
> > indentation plus `python-indent-offset' multiplied by this
> > variable.  For example, the arguments are indented as follows if
> > this variable is 1:
> >
> >     def do_something(
> >         arg1,
> >         arg2
> >     ):
> >         print('hello')
> >
> > if this variable is 2 (default):
> >
> >     def do_something(
> >             arg1,
> >             arg2):
> >         print('hello')
> >
> > This variable has an effect on all blocks, not just def block.
> > This variable only works if the opening paren is not followed by
> > non-whitespace characters on the same line.  Modify
> > `python-indent-block-paren-deeper' to customize the case where
> > non-whitespace characters follow the opening paren on the same
> > line."
>
> Do these two patches replace every other patch posted in this
> discussion?
>
> Are these two patches ready to be installed, or are you still
> discussing the issue?
>
> Thanks.
>

[-- Attachment #2: Type: text/html, Size: 4431 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#62696: python.el: Extra indentation for conditionals
  2023-04-20 13:40                   ` Gustaf Waldemarson
@ 2023-04-20 14:19                     ` kobarity
  2023-04-20 17:44                       ` Gustaf Waldemarson
  2023-04-22  9:33                       ` Eli Zaretskii
  0 siblings, 2 replies; 14+ messages in thread
From: kobarity @ 2023-04-20 14:19 UTC (permalink / raw)
  To: Gustaf Waldemarson; +Cc: 62696, Eli Zaretskii, npostavs, gastove, dgutov

[-- Attachment #1: Type: text/plain, Size: 1086 bytes --]


Gustaf Waldemarson wrote:
> It mostly looks good to me, but perhaps it would make sense to still output
> the closing paren on the same line as the first example? I suppose it might
> not make sense from a style-perspective, but I guess someone reading through
> this mostly care about comparing indentation depths

Do you mean

    def do_something(
        arg1,
        arg2):
        print('hello')

is better than the following?

    def do_something(
        arg1,
        arg2
    ):
        print('hello')

If so, I think it is still good.  I updated the patches.

> > Do these two patches replace every other patch posted in this
> > discussion?
> > Are these two patches ready to be installed, or are you still
> > discussing the issue?
> 
> I believe that's the case, but I would wait for a response from Kobarity
> following this.

Yes, I'm attaching the patches which replace every other patches in
old mails.  I was just waiting to see if anyone had any comments or
feedback.  So, unless there is an objection, the patches are ready to
be installed on the master branch.

[-- Attachment #2: 0001-Add-a-new-user-option-in-Python-mode-to-improve-the-.patch --]
[-- Type: application/octet-stream, Size: 11493 bytes --]

From 71471c48aced70e52568feebb2bc40ebf02d6e7e Mon Sep 17 00:00:00 2001
From: kobarity <kobarity@gmail.com>
Date: Sun, 16 Apr 2023 22:18:39 +0900
Subject: [PATCH 1/2] Add a new user option in Python mode to improve the
 indentation

* lisp/progmodes/python.el (python-indent-block-paren-deeper): New
user option.
(python-indent-context): Add a new context :inside-paren-from-block.
(python-indent--calculate-indentation): Modify according to
`python-indent-block-paren-deeper' and :inside-paren-from-block.
* test/lisp/progmodes/python-tests.el
(python-indent-inside-paren-block-1)
(python-indent-inside-paren-block-2)
(python-indent-inside-paren-block-3)
(python-indent-inside-paren-block-4): New tests.
(python-indent-inside-paren-5, python-indent-dedenters-8): Modify
according to the new context.
* etc/NEWS: Document the new user option.  (Bug#62696)
---
 etc/NEWS                            |  17 ++++
 lisp/progmodes/python.el            |  57 ++++++++++++--
 test/lisp/progmodes/python-tests.el | 116 +++++++++++++++++++++++++++-
 3 files changed, 181 insertions(+), 9 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index b121002b246..9e318812714 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -276,6 +276,23 @@ distracting and easily confused with actual code, or a significant
 early aid that relieves you from moving the buffer or reaching for the
 mouse to consult an error message.
 
+** Python mode
+
+*** New user option 'python-indent-block-paren-deeper'.
+If non-nil, increase the indentation of the lines inside parens in a
+header of a block when they are indented to the same level as the body
+of the block:
+
+    if (some_expression
+            and another_expression):
+        do_something()
+
+instead of:
+
+    if (some_expression
+        and another_expression):
+        do_something()
+
 \f
 * New Modes and Packages in Emacs 30.1
 
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index bbabce80b4d..9ae95ecfc1c 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -1270,6 +1270,31 @@ python-indent-def-block-scale
   :type 'integer
   :safe 'natnump)
 
+(defcustom python-indent-block-paren-deeper nil
+  "Increase indentation inside parens of a block.
+If non-nil, increase the indentation of the lines inside parens
+in a header of a block when they are indented to the same level
+as the body of the block:
+
+    if (some_expression
+            and another_expression):
+        do_something()
+
+instead of:
+
+    if (some_expression
+        and another_expression):
+        do_something()
+
+This variable only works if the opening paren is followed by
+non-whitespace characters on the same line.  Modify
+`python-indent-def-block-scale' to customize the case where
+non-whitespace character does not follow the opening paren on the
+same line."
+  :version "30.1"
+  :type 'boolean
+  :safe 'booleanp)
+
 (defvar python-indent-current-level 0
   "Deprecated var available for compatibility.")
 
@@ -1367,6 +1392,10 @@ python-indent-context
  - Point is inside a paren with items starting in their own line
    from a block start.
  - START is the position of the open paren.
+:inside-paren-from-block
+ - 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.
 
 :after-backslash
  - Fallback case when point is after backslash.
@@ -1450,12 +1479,16 @@ python-indent-context
              (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))))))))
+             (t (let ((after-start (save-excursion
+                               (goto-char (1+ start))
+                               (skip-syntax-forward "(" 1)
+                               (skip-syntax-forward " ")
+                               (point))))
+                  (if (save-excursion
+                        (python-nav-beginning-of-statement)
+                        (python-info-looking-at-beginning-of-block))
+                      (cons :inside-paren-from-block after-start)
+                    (cons :inside-paren after-start))))))))
        ;; After backslash.
        ((let ((start (when (not (python-syntax-comment-or-string-p ppss))
                        (python-info-line-ends-backslash-p
@@ -1603,7 +1636,17 @@ python-indent--calculate-indentation
         (`(,(or :inside-paren-newline-start-from-block) . ,start)
          (goto-char start)
          (+ (current-indentation)
-            (* python-indent-offset python-indent-def-block-scale))))))
+            (* python-indent-offset python-indent-def-block-scale)))
+        (`(,:inside-paren-from-block . ,start)
+         (goto-char start)
+         (let ((column (current-column)))
+           (if (and python-indent-block-paren-deeper
+                    (= column (+ (save-excursion
+                                   (python-nav-beginning-of-statement)
+                                   (current-indentation))
+                                 python-indent-offset)))
+               (+ column python-indent-offset)
+             column))))))
 
 (defun python-indent--calculate-levels (indentation)
   "Calculate levels list given INDENTATION.
diff --git a/test/lisp/progmodes/python-tests.el b/test/lisp/progmodes/python-tests.el
index 50153e66da5..60b11d572cf 100644
--- a/test/lisp/progmodes/python-tests.el
+++ b/test/lisp/progmodes/python-tests.el
@@ -1139,7 +1139,7 @@ python-indent-inside-paren-5
    (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))
+   (should (eq (car (python-indent-context)) :inside-paren-from-block))
    (should (= (python-indent-calculate-indentation) 7))
    (forward-line 1)
    (should (eq (car (python-indent-context)) :after-block-start))
@@ -1174,6 +1174,118 @@ 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-block-1 ()
+  "`python-indent-block-paren-deeper' set to nil (default).
+See Bug#62696."
+  (python-tests-with-temp-buffer
+   "
+if ('VALUE' in my_unnecessarily_long_dictionary and
+    some_other_long_condition_case):
+    do_something()
+elif (some_case or
+      another_case):
+    do_another()
+"
+   (python-tests-look-at "if")
+   (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-from-block))
+   (should (= (python-indent-calculate-indentation) 4))
+   (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)) :at-dedenter-block-start))
+   (should (= (python-indent-calculate-indentation) 0))
+   (forward-line 1)
+   (should (eq (car (python-indent-context)) :inside-paren-from-block))
+   (should (= (python-indent-calculate-indentation) 6))
+   (forward-line 1)
+   (should (eq (car (python-indent-context)) :after-block-start))
+   (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-inside-paren-block-2 ()
+  "`python-indent-block-paren-deeper' set to t.
+See Bug#62696."
+  (python-tests-with-temp-buffer
+   "
+if ('VALUE' in my_unnecessarily_long_dictionary and
+        some_other_long_condition_case):
+    do_something()
+elif (some_case or
+      another_case):
+    do_another()
+"
+   (let ((python-indent-block-paren-deeper t))
+     (python-tests-look-at "if")
+     (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-from-block))
+     (should (= (python-indent-calculate-indentation) 8))
+     (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)) :at-dedenter-block-start))
+     (should (= (python-indent-calculate-indentation) 0))
+     (forward-line 1)
+     (should (eq (car (python-indent-context)) :inside-paren-from-block))
+     (should (= (python-indent-calculate-indentation) 6))
+     (forward-line 1)
+     (should (eq (car (python-indent-context)) :after-block-start))
+     (should (= (python-indent-calculate-indentation) 4)))))
+
+(ert-deftest python-indent-inside-paren-block-3 ()
+  "With backslash.  `python-indent-block-paren-deeper' set to nil (default).
+See Bug#62696."
+  (python-tests-with-temp-buffer
+   "
+if 'VALUE' in my_uncessarily_long_dictionary and\\
+   (some_other_long_condition_case or
+    another_case):
+    do_something()
+"
+   (python-tests-look-at "if")
+   (should (eq (car (python-indent-context)) :no-indent))
+   (should (= (python-indent-calculate-indentation) 0))
+   (forward-line 1)
+   (should (eq (car (python-indent-context))
+               :after-backslash-block-continuation))
+   (should (= (python-indent-calculate-indentation) 3))
+   (forward-line 1)
+   (should (eq (car (python-indent-context)) :inside-paren-from-block))
+   (should (= (python-indent-calculate-indentation) 4))
+   (forward-line 1)
+   (should (eq (car (python-indent-context)) :after-block-start))
+   (should (= (python-indent-calculate-indentation) 4))))
+
+(ert-deftest python-indent-inside-paren-block-4 ()
+  "With backslash.  `python-indent-block-paren-deeper' set to t.
+See Bug#62696."
+  (python-tests-with-temp-buffer
+   "
+if 'VALUE' in my_uncessarily_long_dictionary and\\
+   (some_other_long_condition_case or
+        another_case):
+    do_something()
+"
+   (let ((python-indent-block-paren-deeper t))
+     (python-tests-look-at "if")
+     (should (eq (car (python-indent-context)) :no-indent))
+     (should (= (python-indent-calculate-indentation) 0))
+     (forward-line 1)
+     (should (eq (car (python-indent-context))
+                 :after-backslash-block-continuation))
+     (should (= (python-indent-calculate-indentation) 3))
+     (forward-line 1)
+     (should (eq (car (python-indent-context)) :inside-paren-from-block))
+     (should (= (python-indent-calculate-indentation) 8))
+     (forward-line 1)
+     (should (eq (car (python-indent-context)) :after-block-start))
+     (should (= (python-indent-calculate-indentation) 4)))))
+
 (ert-deftest python-indent-after-block-1 ()
   "The most simple after-block case that shouldn't fail."
   (python-tests-with-temp-buffer
@@ -1670,7 +1782,7 @@ python-indent-dedenters-8
    (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-from-block))
    (should (= (python-indent-calculate-indentation) 6))
    (python-indent-line)
    (should (= (python-indent-calculate-indentation t) 4))
-- 
2.34.1


[-- Attachment #3: 0002-Improve-docstring-of-python-indent-def-block-scale.patch --]
[-- Type: application/octet-stream, Size: 1653 bytes --]

From 0c235d8a82db8881fad3f5c4bc78ab0d9ccf4fc2 Mon Sep 17 00:00:00 2001
From: kobarity <kobarity@gmail.com>
Date: Thu, 20 Apr 2023 22:55:29 +0900
Subject: [PATCH 2/2] Improve docstring of python-indent-def-block-scale

* lisp/progmodes/python.el (python-indent-def-block-scale): Improve
docstring.  (Bug#62696)
---
 lisp/progmodes/python.el | 25 ++++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 9ae95ecfc1c..5bb15c60956 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -1265,7 +1265,30 @@ python-indent-trigger-commands
   :type '(repeat symbol))
 
 (defcustom python-indent-def-block-scale 2
-  "Multiplier applied to indentation inside multi-line def blocks."
+  "Multiplier applied to indentation inside multi-line blocks.
+The indentation in parens in the block header will be the current
+indentation plus `python-indent-offset' multiplied by this
+variable.  For example, the arguments are indented as follows if
+this variable is 1:
+
+    def do_something(
+        arg1,
+        arg2):
+        print('hello')
+
+if this variable is 2 (default):
+
+    def do_something(
+            arg1,
+            arg2):
+        print('hello')
+
+This variable has an effect on all blocks, not just def block.
+This variable only works if the opening paren is not followed by
+non-whitespace characters on the same line.  Modify
+`python-indent-block-paren-deeper' to customize the case where
+non-whitespace characters follow the opening paren on the same
+line."
   :version "26.1"
   :type 'integer
   :safe 'natnump)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 14+ messages in thread

* bug#62696: python.el: Extra indentation for conditionals
  2023-04-20 14:19                     ` kobarity
@ 2023-04-20 17:44                       ` Gustaf Waldemarson
  2023-04-22  9:33                       ` Eli Zaretskii
  1 sibling, 0 replies; 14+ messages in thread
From: Gustaf Waldemarson @ 2023-04-20 17:44 UTC (permalink / raw)
  To: kobarity; +Cc: 62696, Eli Zaretskii, npostavs, gastove, dgutov

[-- Attachment #1: Type: text/plain, Size: 1400 bytes --]

> If so, I think it is still good.  I updated the patches.

Yes, as long as the examples are both consistent with the parenthesis
they are good to me!

Best regards,
Gustaf

Den tors 20 apr. 2023 kl 16:19 skrev kobarity <kobarity@gmail.com>:

>
> Gustaf Waldemarson wrote:
> > It mostly looks good to me, but perhaps it would make sense to still
> output
> > the closing paren on the same line as the first example? I suppose it
> might
> > not make sense from a style-perspective, but I guess someone reading
> through
> > this mostly care about comparing indentation depths
>
> Do you mean
>
>     def do_something(
>         arg1,
>         arg2):
>         print('hello')
>
> is better than the following?
>
>     def do_something(
>         arg1,
>         arg2
>     ):
>         print('hello')
>
> If so, I think it is still good.  I updated the patches.
>
> > > Do these two patches replace every other patch posted in this
> > > discussion?
> > > Are these two patches ready to be installed, or are you still
> > > discussing the issue?
> >
> > I believe that's the case, but I would wait for a response from Kobarity
> > following this.
>
> Yes, I'm attaching the patches which replace every other patches in
> old mails.  I was just waiting to see if anyone had any comments or
> feedback.  So, unless there is an objection, the patches are ready to
> be installed on the master branch.
>

[-- Attachment #2: Type: text/html, Size: 2124 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* bug#62696: python.el: Extra indentation for conditionals
  2023-04-20 14:19                     ` kobarity
  2023-04-20 17:44                       ` Gustaf Waldemarson
@ 2023-04-22  9:33                       ` Eli Zaretskii
  1 sibling, 0 replies; 14+ messages in thread
From: Eli Zaretskii @ 2023-04-22  9:33 UTC (permalink / raw)
  To: kobarity; +Cc: 62696-done, npostavs, gustaf.waldemarson, gastove, dgutov

> Date: Thu, 20 Apr 2023 23:19:51 +0900
> From: kobarity <kobarity@gmail.com>
> Cc: Eli Zaretskii <eliz@gnu.org>,
> 	62696@debbugs.gnu.org,
> 	dgutov@yandex.ru,
> 	gastove@gmail.com,
> 	npostavs@users.sourceforge.net
> 
> > > Do these two patches replace every other patch posted in this
> > > discussion?
> > > Are these two patches ready to be installed, or are you still
> > > discussing the issue?
> > 
> > I believe that's the case, but I would wait for a response from Kobarity
> > following this.
> 
> Yes, I'm attaching the patches which replace every other patches in
> old mails.  I was just waiting to see if anyone had any comments or
> feedback.  So, unless there is an objection, the patches are ready to
> be installed on the master branch.

Thanks, installed on the master branch, and closing the bug.





^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2023-04-22  9:33 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-04-06 12:47 bug#62696: python.el: Extra indentation for conditionals Gustaf Waldemarson
2023-04-09 12:11 ` kobarity
2023-04-10 11:11   ` Gustaf Waldemarson
2023-04-10 15:21     ` kobarity
2023-04-11 11:11       ` Gustaf Waldemarson
2023-04-12 15:26         ` kobarity
2023-04-16 13:24           ` kobarity
2023-04-16 15:49             ` Gustaf Waldemarson
2023-04-18 14:23               ` kobarity
2023-04-20  8:22                 ` Eli Zaretskii
2023-04-20 13:40                   ` Gustaf Waldemarson
2023-04-20 14:19                     ` kobarity
2023-04-20 17:44                       ` Gustaf Waldemarson
2023-04-22  9:33                       ` Eli Zaretskii

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).