Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

How would you indent this?

by no longer just digit (Beadle)
on Mar 18, 2021 at 00:53 UTC ( #11129870=perlquestion: print w/replies, xml ) Need Help??

no longer just digit has asked for the wisdom of the Perl Monks concerning the following question:

I'm interested to know what people think is a good default indentation for the following input:

my @headings = ( {key => 'k', name => 'Kanji', class => 'kanji'}, {key => 'skip', name => 'SKIP', class => 'skip-code'}, {key => 'co', name => 'Suggestion', class => 'skip-code'}, {key => 'disc', name => 'Discussion'}, );

Emacs' cperl-mode in its default setting (as run with emacs -Q) indents the above as follows:

my @headings = ( { key => 'k', name => 'Kanji', class => 'kanji'}, { key => 'skip', name => 'SKIP', class => 'skip-code'}, { key => 'co', name => 'Suggestion', class => 'skip-code'}, { key => 'disc', name => 'Discussion'}, );

I think most people would agree that this is a little eccentric. I'm trying to set up a better default, but what indentations do people actually use for these types of things?

Replies are listed 'Best First'.
Re: How would you indent this?
by ikegami (Patriarch) on Mar 18, 2021 at 00:58 UTC

    I don't think I've ever seen a style where a { on its own line isn't matched by a } on its own line. The code you posted is very jarring. At a glance, I would suspect improper indentation or a missing closing braces. You're reader's first impression shouldn't be that it's broken code.

    I'd use this:

    my @headings = ( { key => 'k', name => 'Kanji', class => 'kanji' }, { key => 'skip', name => 'SKIP', class => 'skip-code' }, { key => 'co', name => 'Suggestion', class => 'skip-code' }, { key => 'disc', name => 'Discussion' }, );

    The idea is to make the differences between the lines stand out.

    Seeking work! You can reach me at ikegami@adaelis.com

Re: How would you indent this?
by GrandFather (Saint) on Mar 18, 2021 at 02:02 UTC

    Using perltidy configured to my preferences I get:

    my @headings = ( {key => 'k', name => 'Kanji', class => 'kanji'}, {key => 'skip', name => 'SKIP', class => 'skip-code'}, {key => 'co', name => 'Suggestion', class => 'skip-code'}, {key => 'disc', name => 'Discussion'}, );

    which I'm pretty happy with. The cperl-mode default is awful.

    Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
      The cperl-mode default is awful.

      It's probably a bug or at least unintentional behaviour. It seems like the consensus is that one should either have

      {key => 'k', class => 'kanji'},

      or

      { key => 'k', class => 'kanji', },

      but not

      { key => 'k', class => 'kanji' },

      so what I should do is to stop it from adding the newline after the {.

        { key => 'k', class => 'kanji' },

        is best here because it makes it easy to see the differences the rows at a glance.


        { key => 'k', class => 'kanji' },

        isn't too bad, since it's almost as good at showing the differences between rows. The extra spacing is a needless hindrance, though, so it's not as good.


        But it's definitely not as bad as what you posted in the OP.

        { key => 'k', class => 'kanji' },

        This is just plain weird.


        Finally, we have this:

        { key => 'k', class => 'kanji', },

        It offers no readability benefits over the styles, and makes it even harder to see differences between the lines. No good here. This would be used when dealing with with very long records, and when adjacent lines are substantially different (e.g. in a multi-level structure).

        Seeking work! You can reach me at ikegami@adaelis.com

        My issue with first and last styles is that changes needlessly become cumbersome. That may be preferred to set infrequently changing defaults, or if one needs to save precious vertical space.

Re: How would you indent this?
by choroba (Cardinal) on Mar 18, 2021 at 10:24 UTC
    I'd probably use this style:
    my @headings = ({key => 'k', name => 'Kanji', class => 'kanji' +}, {key => 'skip', name => 'SKIP', class => 'skip-c +ode'}, {key => 'co', name => 'Suggestion', class => 'skip-c +ode'}, {key => 'disc', name => 'Discussion'});
    But that's just my personal preference (you can guess, I prefer compactness).

    At work, we don't align at all, and use tabs to indent, so we'd get the boring

    my @headings = ( { key => 'k', name => 'Kanji', class => 'kanji' }, { key => 'skip', name => 'SKIP', class => 'skip-code' }, ... );
    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: How would you indent this?
by davies (Prior) on Mar 18, 2021 at 13:44 UTC

    Left to myself, I'd do it Ikegami's way. But I'm not always left to myself. If there is a house style, I would use that.

    On one occasion, I was handing over work to someone who said, "I can see that lining up text like that makes it easier to read, but why do you do it?" Some questions are unanswerable. If you are working alone and find another style easier to read, use that style.

    Regards,

    John Davies

Re: How would you indent this? Which identation style do you prefer?
by Anonymous Monk on Mar 18, 2021 at 02:35 UTC

    Which identation style do you prefer?

    Over the years I've tried typing it up different styles, ONCE, each :D

    For this guy its a tossup between "## hand style" and "## perltidy edited" (shift indentation left)

    But, once @static is populated, mostly I let dd() handle it

    ## original as posted by no longer just digit my @headings = ( {key => 'k', name => 'Kanji', class => 'kanji'}, {key => 'skip', name => 'SKIP', class => 'skip-code'}, {key => 'co', name => 'Suggestion', class => 'skip-code'}, {key => 'disc', name => 'Discussion'}, ); ## perltidy defaults my @headings = ( { key => 'k', name => 'Kanji', class => 'kanji' }, { key => 'skip', name => 'SKIP', class => 'skip-code' }, { key => 'co', name => 'Suggestion', class => 'skip-code' }, { key => 'disc', name => 'Discussion' }, ); ## perltidy edited my @headings = ( { key => 'k', name => 'Kanji', class => 'kanji' }, { key => 'skip', name => 'SKIP', class => 'skip-code' }, { key => 'co', name => 'Suggestion', class => 'skip-code' }, { key => 'disc', name => 'Discussion' }, ); ## Data::Dump::dd my @headings = ( { class => "kanji", key => "k", name => "Kanji" }, { class => "skip-code", key => "skip", name => "SKIP" }, { class => "skip-code", key => "co", name => "Suggestion" }, { key => "disc", name => "Discussion" }, ) ; ## rehohy my @headings; $headings[0]{name} = "Kanji"; #d1 $headings[0]{class} = "kanji"; #d1 $headings[0]{key} = "k"; #d1 $headings[1]{name} = "SKIP"; #d1 $headings[1]{class} = "skip-code"; #d1 $headings[1]{key} = "skip"; #d1 $headings[2]{name} = "Suggestion"; #d1 $headings[2]{class} = "skip-code"; #d1 $headings[2]{key} = "co"; #d1 $headings[3]{name} = "Discussion"; #d1 $headings[3]{key} = "disc"; #d1 ## Data::Dumper->new([@_])->Useqq(1)->Indent(1)->Dump my @headings = ( { "name" => "Kanji", "class" => "kanji", "key" => "k" }, { "name" => "SKIP", "class" => "skip-code", "key" => "skip" }, { "name" => "Suggestion", "class" => "skip-code", "key" => "co" }, { "name" => "Discussion", "key" => "disc" } ); ## Data::Dumper->new([@_])->Useqq(1)->Indent(2)->Dump my @headings = ( { "name" => "Kanji", "class" => "kanji", "key" => "k" }, { "name" => "SKIP", "class" => "skip-code", "key" => "skip" }, { "name" => "Suggestion", "class" => "skip-code", "key" => "co" }, { "name" => "Discussion", "key" => "disc" } ); ## hand style my @headings = ( { "name" => "Kanji", "class" => "kanji", "key" => "k" }, { "name" => "SKIP", "class" => "skip-code", "key" => "skip" }, { "name" => "Suggestion", "class" => "skip-code", "key" => "co" }, { "name" => "Discussion", "key" => "disc" } );
Re: How would you indent this? (emacs)
by LanX (Sage) on Mar 18, 2021 at 12:12 UTC
    If I want compactness in emacs I usually break into the next line
    my @headings = ( { key => 'k', name => 'Kanji', class => 'kanji'}, { key => 'skip', name => 'SKIP', class => 'skip-code'}, { key => 'co', name => 'Suggestion', class => 'skip-code'}, { key => 'disc', name => 'Discussion'}, );

    I don't know where you got the break after the opening curly from

    { key => 'disc', name => 'Discussion'},

    (Update: *I can't reproduce this!* I can now, see reply )

    BUT cperl-mode offers 8 indent-styles (also available from the menu)

    M-x cperl-set-style BSD C++ CPerl Current GNU K&R PerlStyle Whitesmith

    ... and plenty of customizations

    Cperl Indentation Details group: Indentation. State : something in this group has been changed outside custom +ize. Show Value Cperl Brace Imaginary Offset Imagined indentation of a Perl open brace that actually follows a s +tatement. Hide An open brace following other text is treated as if it were this fa +r to the right of the start of its line. Show Value Cperl Brace Offset Extra indentation for braces, compared with other text in same cont +ext. Show Value Cperl Break One Line Blocks When Indent Non-nil means that one-line if/unless/while/until/for/foreach BLOCK +s Hide need to be reformatted into multiline ones when indenting a region. Show Value Cperl Close Paren Offset Extra indent for substatements that start with close-parenthesis. Hide Cperl Comment Column: 40 State : SAVED and set. Column to put comments in CPerl (use M-x cperl-indent to lineup wit +h code). Show Value Cperl Continued Brace Offset Extra indent for substatements that start with open-braces. Hide This is in addition to cperl-continued-statement-offset. Show Value Cperl Continued Statement Offset Extra indent for lines not starting new statements. Show Value Cperl Fix Hanging Brace When Indent Non-nil means that BLOCK-end `}' may be put on a separate line Hide when indenting a region. Braces followed by else/elsif/while/until are excepted. Show Value Cperl Indent Comment At Column 0 Non-nil means that comment started at column 0 should be indentable +. Show Value Cperl Indent Left Aligned Comments Non-nil means that the comment starting in leftmost column should i +ndent. Hide Cperl Indent Level: 2 State : CHANGED outside Customize. Indentation of CPerl statements with respect to containing block. Show Value Cperl Indent Parens As Block Non-nil means that non-block ()-, {}- and []-groups are indented as + blocks, Hide but for trailing "," inside the group, which won't increase indenta +tion. One should tune up `cperl-close-paren-offset' as well. Show Value Cperl Indent Region Fix Constructs Amount of space to insert between `}' and `else' or `elsif' Hide in `cperl-indent-region'. Set to nil to leave as is. Values other than 1 and nil will probably not work. Show Value Cperl Indent Wrt Brace Non-nil means indent statements in if/etc block relative brace, not + if/etc. Hide Versions 5.2 ... 5.20 behaved as if this were `nil'. Show Value Cperl Label Offset Offset of CPerl label lines relative to usual indentation. Show Value Cperl Lineup Step `cperl-lineup' will always lineup at multiple of this number. Hide If nil, the value of `cperl-indent-level' will be used. Show Value Cperl Merge Trailing Else Non-nil means that BLOCK-end `}' followed by else/elsif/continue Hi +de may be merged to be on the same line when indenting a region. Show Value Cperl Min Label Indent Minimal offset of CPerl label lines. Show Value Cperl Regexp Indent Step Indentation used when beautifying regexps. Hide If nil, the value of `cperl-indent-level' will be used. Show Value Cperl Tab Always Indent Non-nil means TAB in CPerl mode should always reindent the current +line, Hide regardless of where in the line point is when the TAB command is us +ed.

    you might wanna read the micro-docs in the menu and experiment.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

      > (Update: I can't reproduce this! )

      OK, I only used TAB indentation for each line so far. (emacs default is that TAB indents a line)

      If I mark the region and apply an explicit M-x cperl-indent-region I'm getting your result. That's also available from the "Perl" menu as indent region .

      Tested in 24.3 and 27.1 on Windows.

      From my experience does cperl not attempt to format nested data structures, there are many styles and interfering with the styles of Data::Dumper , Data::Dump, Data::Printer or Perl::Tidy is cumbersome.

      Cperl-mode only does indenting!

      It is also NOT attempting to vertically align elements in a 2D structure. It's a requirement close to AI to DWIM nested data.

      My preference here is Data::Dump, but it's also resorting hash-keys alphabetically and I wouldn't want my editor to take away this decision from me.

      The best format depends on the POV.

      Anyway ... what you are experiencing looks to me like a misinterpretation of the left curly as a block start like in for (...) {

      Please note how replacing the hashes with arrays is fixing this:

      (NB: I'd personally line-break right of the assignment)

      my @headings = ( [key => 'k', name => 'Kanji', class => 'kanji' ], [key => 'skip', name => 'SKIP', class => 'skip-code'], [key => 'co', name => 'Suggestion', class => 'skip-cod +e'], [key => 'disc', name => 'Discussion'], );

      workarounds

      A) as quick workaround I'd suggest manually undoing nested data structures when indenting a large block of code.

      Since emacs has "regional undo" of marked regions this should be easy and you can use the formatter of your taste for nested datastructures.

      B) If you don't wanna use an external formatter, you can also appease yourself with this format by manually introducing line-breaks before right curlies } , b/c cperl-indent-region won't change this. ( I also manually did a vertical alignment)

      my @headings = ( { key => 'k', name => 'Kanji', class => 'kanji' }, { key => 'skip', name => 'SKIP', class => 'skip-code' }, { key => 'co', name => 'Suggestion', class => 'skip-code' }, { key => 'disc', name => 'Discussion' }, );

      Breaking the hash elements into individual lines works too.

      C) UPDATE Or don't use cperl-indent-*

      If you are working in a team, you'll need to use an RCS like GIT and chances are low everybody is using the same IDE.

      Resorting to a fixed Perl::Tidy setting hooked into your RCS is the only way here. Otherwise all reindented lines will show up as diffs.

      what's next

      I'll try to write a test for this problem in Perl to figure out the root cause.

      Could also be that the emacs maintainers changed some fundamentals which are sabotaging cperl-mode.

      HTH! :)

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

      ) I haven't seen any other editor yet offering this.

        From my experience does cperl not attempt to format nested data structures

        It shouldn't, but it does. As you suspect, CPerl mode mistakes the nested data structure as a control structure, which it reformats intentionally (see example here). It just takes a wrong turn in the function cperl-fix-line-spacing. Therefore, it is rather obvious that the issue doesn't occur with arrays: square brackets can't be confused with control structures.

        If you want M-x cperl-indent-region and M-x cperl-indent-exp to do indentation and nothing else, set cperl-indent-region-fix-constructs to nil.

Re: How would you indent this?
by haj (Priest) on Mar 18, 2021 at 13:49 UTC

    I'd consider this indentation of CPerl to be a bug.

    CPerl mode's M-x indent-region does more than just indent, it also tries to "fix" spacing. But in this case it turns out just horribly wrong. Here's an example to show the intention: If you type the following:

    if($fee){fie()}else{foe($foo)}

    ...and then process this with M-x indent-region, then you get (depending on your style, I'm using PBP here):

    if ($fee) { fie(); } else { foe($foo); }

    Nifty, eh? A real life saver... but only if your space key is broken.

    The customization variable responsible for this is cperl-indent-region-fix-constructs (on by default), which is out of the scope of the predefined indentation styles. The documentation for that variable shows what it should do, but not what it actually does. So, if you don't need CPerl mode to reformat your code like this, then I suggest you just turn off that variable as a workaround (and, if you like, write a bug report).

    By the way: Emacs can help with aligning the keys as suggested by ikegami and GrandFather, unrelated to CPerl mode. It is hardly worth the effort for four lines, though: Mark the assignment as a region, and then

    align-regexp<RET>name<RET> align-regexp<RET>class<RET>

    This yields:

    my @headings = ( {key => 'k', name => 'Kanji', class => 'kanji'}, {key => 'skip', name => 'SKIP', class => 'skip-code'}, {key => 'co', name => 'Suggestion', class => 'skip-code'}, {key => 'disc', name => 'Discussion'}, )

      Generally speaking I don't indent code, so I use mark-whole-buffer and then indent-region. In Go code I actually use

      (add-hook 'before-save-hook #'gofmt-before-save)

      instead so the whole buffer is formatted for me.

      I haven't been able to do that with Perl because of numerous oddities in cperl-mode. Probably the most annoying one is that it turns

      };
      

      into

      }
      ;
      
      So, if you don't need CPerl mode to reformat your code like this, then I suggest you just turn off that variable as a workaround (and, if you like, write a bug report).

      Thanks. I have reported bugs in cperl-mode in the past but things don't seem to have improved, and in at least one case things have got much worse, the case where it immediately throws an error about unbalanced parentheses whenever one presses the m or s keys. That used to be annoying when it was just an error message, but with recent Emacs it also scrolls the window, so in cperl-mode each time I press "m" or "s" the Emacs window scrolls in a random direction. It is truly exhausting when reading a piece of code in one place and then writing in another, and then the Emacs suddenly scrolls due to typing my or sub

      What I'm trying to do is to set up my own version of cperl-mode with better defaults and fewer bugs. In the case of this hash reference case, I wasn't sure what sort of indentation to use, although I was pretty sure I was going to use something other than the default behaviour, so I thought I'd ask here what people thought. It's been very interesting to get feedback from everyone, so thank you to all participants.

      By the way: Emacs can help with aligning the keys as suggested by ikegami and GrandFather, unrelated to CPerl mode. It is hardly worth the effort for four lines, though: Mark the assignment as a region, and then

      align-regexp<RET>name<RET> align-regexp<RET>class<RET>

      Thank you, that is extremely useful to know about. This page has more.

        Probably the most annoying one is that it turns
        };
        into
        } ;
        Hm. I can't reproduce that one. Do you have a example snippet for that behavior?
        I have reported bugs in cperl-mode in the past but things don't seem to have improved,...

        Well, not yet visible for users of released versions of Emacs. About half a year ago I started to work through the open bugs of CPerl mode, and a dozen or so have been fixed.

        If you are running Emacs 26.1 or newer, I'd like to invite you to try the "hot" cperl-mode.el from the Emacs repository. There has been quite some activity recently.

        Actually, I am currently working on a patch for indenting issues in CPerl mode. That patch has grown to 600 lines by now and is supposed to cover bugs #11773, #42169, and by accident, also #8077. It is kinda annoying but also kinda nice that new test cases are popping up right now!

        ... and in at least one case things have got much worse, the case where it immediately throws an error about unbalanced parentheses whenever one presses the m or s keys.

        That should not happen and I can not reproduce it. The error message appears only after typing m and an opening delimiter where it is at least technically correct. I haven't experienced any scrolling, though. Do you have some customization for the echo area?

        It's been a few days, but...

        That used to be annoying when it was just an error message, but with recent Emacs it also scrolls the window, so in cperl-mode each time I press "m" or "s" the Emacs window scrolls in a random direction.

        A recent Emacs bug report #47549 prompted me to revisit that issue, and eventually I've been able to reproduce the scrolling. I've submitted a patch, and the Emacs maintainers have merged it into the master branch.

        So, the current cperl-mode.el from the Emacs repository should no longer jump around.

Re: How would you indent this?
by Perlbotics (Bishop) on Mar 19, 2021 at 18:33 UTC

    Not directly addressing your question, but perhaps avoidance is an option?

    my @headings2 = map { { key => $_->[0], name => $_->[1], class => $_-> +[2] } } #-- KEY NAME CLASS ---- [ qw(k Kanji kanji ) ], [ qw(skip SKIP skip-code) ], [ qw(co Suggestion skip-code) ], [ qw(disc Discussion), undef ];

    The difference between your AoH and @headings2 is, that $heading2[3]->{class} exists and is undef. My Emacs (cperl-mode) does not re-indent this block.

Re: How would you indent this?
by parv (Vicar) on Mar 18, 2021 at 04:44 UTC

    Since you asked for it ...

    # vim : tabstop=3 shiftwidth=3 softtabstop=3 shiftround textwidth=90 a +utoindent expandtab my @headings = ( { key => 'k', name => 'Kanji', class => 'kanji' }, { key => 'skip', name => 'SKIP', class => 'skip-code' }, { key => 'co', name => 'Suggestion', class => 'skip-code' }, { key => 'disc', name => 'Discussion' } );

      The problem with this is style is that the nether the braces nor the fields line up.

      This is really bad.

      We use indentation to identify where something starts and ends, but there's no } matching the row's {. It's ended up off by one.

      We use indentation to indicate what is part of the same block, but name isn't at same level as key even though they're in the same block.

      With both false positives and false negatives, this is an complete indentation failure.

      And that's not even getting into the fact that it completely dropped the ball on offering the ability to spot differences between the lines at a glance when it could easily have done so.

      Seeking work! You can reach me at ikegami@adaelis.com

        In my own code I have given up on lining up either fields or the braces. As long as closing brace (or a key|field) is placed in a same column or after of opening brace (or previous key|field) (via indentation of expanding tabs), I am good.

        Anyone or anything (commit hook for example) else is more than free to mangle that to their own liking.

        I do not understand this part if you care to explain ...

        And that's not even getting into the fact that it completely dropped the ball on offering the ability to spot differences between the lines at a glance when it could easily have done so.
      A reply falls below the community's threshold of quality. You may see it by logging in.
A reply falls below the community's threshold of quality. You may see it by logging in.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11129870]
Approved by marto
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others having an uproarious good time at the Monastery: (2)
As of 2023-02-09 06:53 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    I prefer not to run the latest version of Perl because:







    Results (44 votes). Check out past polls.

    Notices?