Subject: Re: bug#78159: shell-script indent regression in Emacs 30

From: John C <[email-masked]>
Date: Wed, 7 May 2025 15:48:09 -0400

I tried the patch on Emacs 30.1 sh-script.el and it doesn't seem to work?
Here's what I get:

#!/bin/bash

haveid1 () {
    (id) 2> /dev/null 1>&2
         if [ $? -eq 0 ]; then
             echo "haveid1: yes"
         else
             echo "haveid1: no"
         fi
}

haveid2 () {
    id 2> /dev/null 1>&2
    if [ $? -eq 0 ]; then
        echo "haveid2: yes"
    else
        echo "haveid2: no"
    fi
}

haveid3 () {
    (id)
        if [ $? -eq 0 ]; then
            echo "haveid1: yes"
        else
            echo "haveid1: no"
        fi
}

On Thu, May 1, 2025 at 5:25 PM Stefan Monnier <[email-masked]>
wrote:

> >
> > haveid1 () {
> >     (id) 2> /dev/null 1>&2
> >        if [ $? -eq 0 ]; then
> >              echo "haveid1: yes"
> >          else
> >              echo "haveid1: no"
> >          fi
> > }
> >
> > haveid2 () {
> >     id 2> /dev/null 1>&2
> >     if [ $? -eq 0 ]; then
> >       echo "haveid2: yes"
> >     else
> >         echo "haveid2: no"
> >     fi
> > }
>
> Oh, yes, it's due to the change to accept:
>
>     case FOO {
>       (id) ...;;
>       (other) ...;;
>     }
>
> The previous code accepted only
>
>     case FOO in
>       (id) ...;;
>       (other) ...;;
>     esac
>
> and it checked only the presence of `in` without checking that it's part
> of a case statement, which was sufficient because `in` doesn't seem to
> ever occur elsewhere than in case statements.
>
> The new code similarly fails to check that the `{` is part of a case
> statement but `{` doesn't enjoy this same property as `in`, hence the
> bug you're seeing.
>
> A crude hack could be something like the patch below, but we should try
> to do a more thorough job of deciding if this `{` is part of case
> statement or not.
>
>
>         Stefan
>
>
> diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el
> index 287f0501350..b5ccc5507e5 100644
> --- a/lisp/progmodes/sh-script.el
> +++ b/lisp/progmodes/sh-script.el
> @@ -1065,14 +1065,19 @@ sh-font-lock-paren
>                      ;; a normal command rather than the real `in' keyword.
>                      ;; I.e. we should look back to try and find the
>                      ;; corresponding `case'.
> -                    ;; Also recognize OpenBSD's case X { ... }
> (bug#55764).
> -                    (and (looking-at ";\\(?:;&?\\|[&|]\\)\\|\\_<in\\|.{")
> -                         ;; ";; esac )" is a case that looks
> -                         ;; like a case-pattern but it's really just a
> close
> -                         ;; paren after a case statement.  I.e. if we
> skipped
> -                         ;; over `esac' just now, we're not looking
> -                         ;; at a case-pattern.
> -                         (not (looking-at "..[ \t\n]+esac[^[:word:]_]"))))
> +                    (cond
> +                     ((looking-at ";\\(?:;&?\\|[&|]\\)\\|\\_<in")
> +                      ;; ";; esac )" is a case that looks
> +                      ;; like a case-pattern but it's really just a close
> +                      ;; paren after a case statement.  I.e. if we skipped
> +                      ;; over `esac' just now, we're not looking
> +                      ;; at a case-pattern.
> +                      (not (looking-at "..[ \t\n]+esac[^[:word:]_]")))
> +                     ;; Also recognize OpenBSD's case X { ... }
> (bug#55764).
> +                     ((looking-at ".{")
> +                      (save-excursion
> +                        ;; Try and make sure this is a case statement!
> +                        (re-search-backward "esac" (pos-bol) t)))))
>               (progn
>                 (when open
>                   (put-text-property open (1+ open) 'syntax-table
> sh-st-punc))
>
>