6

In emacs cperl-mode, ternary operators are not treated specially. If you break them over multiple lines, cperl-mode simply indents each line the same way it indents any continued statement, like this:

$result = ($foo == $bar)  ? 'result1' :
    ($foo == $baz)  ? 'result2' :
        ($foo == $qux)  ? 'result3' :
            ($foo == $quux) ? 'result4' : 
                'fail_result';

This is not very readable. Is there some way that I can convince cperl-mode indent like this?

$result = ($foo == $bar)  ? 'result1' :
          ($foo == $baz)  ? 'result2' :
          ($foo == $qux)  ? 'result3' :
          ($foo == $quux) ? 'result4' : 
                            'fail_result';

By the way, code example from this question.

EDIT

There seems to be a bug in cperl-mode's indentation of ternary operators. Take the following example, which was indented using Emacs 23.1.1, cperl-mode version 5.23:

my $result = ($foo == $bar)  ? 'result1' :
  ($foo == $baz)  ? 'result2' :
  ($foo == $qux)  ? 'result3' :
  ($foo == $quux) ? 'result4' :
  'fail_result';

{
  my $result = ($foo == $bar)  ? 'result1' :
    ($foo == $baz)  ? 'result2' :
      ($foo == $qux)  ? 'result3' :
        ($foo == $quux) ? 'result4' :
          'fail_result';
}

Notice that outside any braces, I basically get the indentation I want. But inside braces, the ternary operator is indented badly. Is there a fix for this?

Community
  • 1
  • 1
Ryan C. Thompson
  • 40,856
  • 28
  • 97
  • 159
  • +1 good question, I've been wondering about this myself. – friedo May 05 '10 at 19:19
  • To be clear, I'd like to be able to do this with emacs's built-in indentation facilities, so that using TAB does the right thing. But failing that, other solutions are welcome. – Ryan C. Thompson May 05 '10 at 20:01

2 Answers2

3

I don't know about auto-indentation in Cperl-mode but M-1 M-S-| perltidy (if you have Perl::Tidy installed) will tidy a marked region (including ternary statements) nicely. By default it won't look precisely like your example but I believe you can customise it to do what you want in its .perltidyrc.

I didn't figure this out myself btw - I read it somewhere - I thought PBP but I've just checked & it doesn't seem to be that, but anyway I use it all the time & find it very useful.

Edit: It was on the cperl page in the emacs wiki

fod
  • 258
  • 2
  • 8
3

What version of cperl-mode and Emacs are you using? In GNU Emacs 23.1, cperl-version 5.23, with no init file, I get:

$result = ($foo == $bar)  ? 'result1' :
  ($foo == $baz)  ? 'result2' :
  ($foo == $qux)  ? 'result3' :
  ($foo == $quux) ? 'result4' :
  fail_result;

If I want them to line up under the first, I'd add an extra set of parens:

$result = (($foo == $bar)  ? 'result1' :
           ($foo == $baz)  ? 'result2' :
           ($foo == $qux)  ? 'result3' :
           ($foo == $quux) ? 'result4' :
           fail_result);

I'm pretty sure that achieving your requested indentation (with fail_result lining up with the 'result' strings) would require some non-trivial changes to cperl-mode's indentation engine. You're welcome to try it, though. :-)

cjm
  • 61,471
  • 9
  • 126
  • 175
  • Hmm. I think I've found a bug in cperl-mode's indentation. I'll edit my question with details. – Ryan C. Thompson May 09 '10 at 19:44
  • Wait a minute, wouldn't an extra set of parents force list context? http://stackoverflow.com/questions/2886751/how-can-i-tell-if-a-set-of-parens-in-perl-code-will-act-as-grouping-parens-or-for – Ryan C. Thompson May 22 '10 at 02:43
  • @Ryan Thompson: No. To get list context, you'd have to put the parens around `$result`, not around the expression you're assigning to `$result`. – cjm May 22 '10 at 07:56
  • I realize that the assignment would happen in scalar context, but an extra pair of parens could still end up creating a one-element list and thereby assigning the number 1 to `$result`. – Ryan C. Thompson May 22 '10 at 22:33
  • @Ryan Thompson: No. Parens on the right-hand side of an assignment operator do not create list context. `$x = ((((func))));` is exactly equivalent to `$x = func;`. To get list context, you put the parens to the left of the assignment: `($x) = func;` calls `func` in list context. – cjm May 22 '10 at 23:26
  • Grr. This solution isn't compatible with setting `cperl-indent-parens-as-block`, which I like. – Ryan C. Thompson Jun 04 '10 at 21:32