Subject: bug#21526: 24.5; prolog-mode: broken indentation for if-then-else construct

From: Markus Triska <[email-masked]>
Date: Sun, 20 Sep 2015 21:33:00 +0200

Stefan Monnier <[email-masked]> writes:

> Thanks.  We indeed have some problems with the new indentation code, and
> I hope you can help me figure out what it should do.

Thank you for looking into this! I will help in every way I can to
achieve the excellent indentation of Stefan Bruda's original version.

> Could you give me some examples of what prolog-paren-indent-p
> should do?  Currently it's simply unused :-(

Basically, when I set prolog-paren-indent-p to t (and the other settings
I posted), then, after I have already typed a Prolog clause like:

   test :-
           HERE

(throughout the following, "HERE" is a placeholder that only denotes the
location of point, with point at the "H"), and then I press "(", then I
expect to get:

   test :-
           (   HERE

with point again being at the "H". In other words, the electric
parenthesis indents `prolog-paren-indent', which is typically 4. This
way, you spare users having to enter spaces manually to achieve the
typical indentation of the if-then-else construct.

>> test :-
>>         (   a ->
>>                     b
>>          ;c
>>                   ).
>
> I see a few different problems:
> - The indentation of ) is wrong simply because nothing caused it to be
>   reindented (hitting TAB or RET at the end brings the close paren to
>   the right spot).  I guess "." should cause re-indentation.  Adding it
>   to electric-indent-chars should do the trick.

In Stefan Bruda's original version, after I typed:

   test :-
           (   a ->
               b,
               cHERE

with point again at the "H", and then I press RET, I get:

   test :-
           (   a ->
               b,
               c
           HERE

That is, notice in particular that this new line is NOT indented the
same amount as the immediately preceding Prolog goals. There is a clear
reason for this: The immediately preceding Prolog goal, namely c, is
NOT followed by a comma. This means that where HERE appears, only two
things can realistically follow:

   1) ";", denoting a further disjunct
   2) ")", the closing parenthesis for the whole construct

For example, the preceding block can either end with:

   test :-
           (   a ->
               b,
               c
           ;   HERE

(notice the next indentation), or with:

   test :-
           (   a ->
               b,
               c
           )

after which the whole construct is finished. This is exactly the
behaviour users expect from a good indentation engine for Prolog, like
PceEmacs that ships with SWI-Prolog, and also Stefan Bruda's prolog.el.

To (unexpectedly) first indent the block and then only re-indent it when
"." is pressed would in my view be a regression compared to that.

>
> - there are two desired indentations for "b".  IIUC You want
>
>     (a ->
>      b1,
>      b2
>     ;c1,
>      c2)
>
>   whereas the current code tries to accommodate
>
>     (a ->
>          b1,
>          b2;
>      c1,
>      c2)

The two most common ways to indent Prolog's if-then-else are:

        (   a ->
            b,
            c
        ;   d,
            e
        ),

and:

        (   a
        ->  b,
            c
        ;   d,
            e
        )

Notice that only the placement of the arrow is different.

I am using the first style throughout all Prolog code I write. The
second version is also common in the Prolog community, and notably a lot
of library code of SWI-Prolog is written in that style. I made sure (via
patches that are integrated as of version 1.15) that both styles are
well supported in the prolog.el supplied directly by Stefan Bruda, even
in the same program and predicate, if you set electric parentheses and
the other settings I posted. Please try it for reference to see the
expected indentation of Prolog code written in either style.

Relatedly, notice that the indentation must also work for disjunctions:

        (   a,
            b
        ;   c,
            d
        )

i.e., in such cases, there is no arrow at all.

>
> - After hitting "b RET" you get indented to a bogus column.  This is
>   because SMIE thinks that
>
>        predicate
>            (arg2, arg2)
>
>   is a possibility, so after "b RET" it thinks you might be about to
>   enter a list of arguments to "b".  This is a general problem with
>   SMIE's handling of empty lines (where it's often valid but unlikely),
>   and even more so here in Prolog where such things aren't even valid.

Exactly, the main point here is that this would be invalid.

> The patch below fixes some of those problems.  After this patch, you
> should hopefully see something more like
>
>     (   a ->
>             b
>      ;  c
>     ).

I've never seen this indentation style in a decade of heavy Prolog use
including publishing, so my guess is that this will not help many users.

Many thanks for looking into this!

All the best,
Markus