Closure folding in IntelliJ IDEA 9 (Maia)

IntelliJ IDEA 9 will bring you a clearer code reading experience with the support of folding for probably the most verbose Java constructions — anonymous classes. In IntelliJ IDEA 9 you will have an option to quickly collapse them to compact, easy to read form and back with a single keystroke.

 

Before:

Ordinary anonymous class syntax display

 

After:

Closures folding enabled

 

EAP users will be, as always, the first to try it out when we release the very first Maia builds, so make sure you’re on the list and watch for the news!

This entry was posted in New Features and tagged , . Bookmark the permalink.

20 Responses to Closure folding in IntelliJ IDEA 9 (Maia)

  1. Alexey Efimov says:

    Great, but please add also easy API for such foldings. I have idea to create plugin for folding our functional classes into compact form such as:
    List mappedList = myList map { a -> a.toString()};
    Thanks!

  2. Peter Gromov says:

    Alexey, the API is really simple

  3. Gilles Philippart says:

    This reminds me of a very interesting IntelliJ plugin written by Alain Ravet, the Camouflage plugin (http://plugins.intellij.net/plugin/?id=57).
    Maybe it was a bit overlooked, but it had very nice ideas about making the code more readable ! You should take a look at it :)

  4. Igor Sereda says:

    Hmmm, would you please consider leaving out “ActionListener(ActionEvent e)” as well? Because it doesn’t really bear useful info and might be a little longer: “DynamicPropertyChangeListener<Map>(ChangeEvent<Map> e)”.

    Maybe just:
    myNodeBox.addActionListener({ onNodeChanged(); });
    ?

  5. Igor Sereda says:

    i’m sorry, can’t figure out how to write code in these comments. in my example i meant DynamicPropertyChangeListener<Map<DynamicPropertyKey,DynamicPropertyValue>>(ChangeEvent<Map<DynamicPropertyKey,DynamicPropertyValue>> e), but i think you get the idea

  6. mark says:

    Again IntelliJ steals eclipse plugin’s feature. its classic.

  7. Peter Gromov says:

    Gilles, thanks for the advice! Actually we have more new foldings. We’ll blog about them in near future.

    Igor, in fact the generic class parameters aren’t displayed in placeholder text, so it won’t be so long. I can make an option, but from my personal experience, not having the class name appear in placeholder makes me constantly looking inside to know, which class it is.

    Mark, this feature was done independently of eclipse plugin and earlier than we’ve heard of that. It just wasn’t announced. Anyway, I don’t think that having a feature similar to Eclipse’s is somehow bad.

  8. Michael Parmeley says:

    @Mark That is odd you accuse IntelliJ of copying a Eclipse plugin. Eclipse was started as a cheap imitation of IntelliJ and as far as I can tell it still remains a cheap an inferior copy of IntelliJ. It just copies everything Jetbrains has added to IntelliJ.

    Eclipse may do some of its own innovation now, but it certainly didn’t start out that way.

  9. Igor Sereda says:

    Peter,

    I think if you display “ActionEvent” instead of “ActionEvent<T>” in the placeholder, that would be misleading. But I see what you mean. I think it makes sense to display type when needed and parameters when needed.

    For example:

    1. “ActionEvent e” is used, so we need to display it:

    myNodeBox.addActionListener(ActionListener(ActionEvent e) { onNodeChanged(e); });

    2. ActionEvent is not used, listener type is inferred:

    myNodeBox.addActionListener({ onNodeChanged(); });

    or at least (without type inference)

    myNodeBox.addActionListener(ActionListener(…) { onNodeChanged(); });

    3. Type cannot be inferred:

    Object r = Runnable { doWork(); };

  10. Peter Gromov says:

    Igor,

    Actually the user can type inside these folded closures, so not displaying the parameters probably won’t be correct since they can be still referenced and completed inside the closure. As for generics, instead of hiding them I can shorten them to (if the corresponding folding is enabled). As I’ve already said, I’m strictly against hiding base class name, but this can be made an option if there are many proponents (probably a JIRA issue?).

  11. Ray Cromwell says:

    Why not adopt Neal Gafter’s BGGA closures syntax?

    Then you’d write:

    addActionListener({ActionEvent e => onNodeChanged()});

    Better yet, since some people might prefer the CICE or FCM proposals, why not make it a preference or pluggable?

  12. Ray Cromwell says:

    BTW, this mechanism seems incredibly powerful and could resolve a whole range of issues with Java. It would allow each programmer to visualize source in a way that makes them most productive while keeping the source in a neutral format (i.e. Java). It seems like a sort of Intentional Programming.

    I hope you guys take this and run with it. No need to wait on Sun/Oracle/JCP. You can add support for Closures, Type Inferencing, Operator Overloading, etc now. The editor can be taught to visualize these in a succinct way (e.g. BigDecimal.add() = ‘+’) while preserving source code compatibility on disk.

  13. Jim Popovich says:

    Expanding on this, you should add a config flag to “graphically” distinguish = form ==
    using = means draw (glpyh) onNodeChanged()} | (glyph) ActionListener );

    the | reads AS meaning the {} closure created AS a new ActionListener

    so that if you had many you could see

    pretend that there are 2 of the ActionListener classes

    addActionListener( {ActionEvent e –>(glpyh) onNodeChanged()} | (glyph) ActionListener );

    addActionListener( {ActionEvent e –>(glpyh) onSuperNodeChanged()} | (glyph) ActionListenerSuper );

    And you can use your “show me the Camel’s” flag to just render the camels, which is very concise.

    addActionListener( {AE e –>(glpyh) onNodeChanged()} | (glyph) AL );

    addActionListener( {AE e –>(glpyh) onSuperNodeChanged()} | (glyph) ALS );

  14. Jim Popovich says:

    the first paragraph some how was garbled when submitted on the web site,

    in long hand so it does not get garbled.

    suggest that we can display different single glph chars for EQUAL vs DOUBLE EQUALS vs NOT EQUALS and you see the NOT EQUALS like a mathematical not-equal with a diagonal slash. the single EQUAL is drawn as a LEFT ARROW we use a RIGHT arrow in closure params separator and a graphic equals means equals = = .
    you could have a really long graphical like 3 or 4 equals to swap in method .equals
    so that you can see

    mini = = = = me

    rather than mini.equals(me)

  15. Peter Gromov says:

    Jim, the equals folding sounds reasonable, could you file a JIRA issue for it?

    As for closures, I don’t completely understand which glyphs you want to display. | meaning ‘as’ also seems strange for a Java person.

  16. Aaron Knauf says:

    This sounds like a fantastic feature. Lots of good ideas in these comments. Key things that I would like: let me concentrate on the code – not the boilerplate. Don’t show me the “new EventListener(){ onClick(MouseEvent e){} }” bit, unless necessary to disambiguate. Just show me the code that I need to write. e.g.

    addListener(…{…(MouseEvent e){
    doStuff(e);
    }});

    I would avoid trying to fold anonymous classes into a closure syntax that resembles some other language, like ruby, or C#, or one of the proposals for java closures (which James Gosling reckons are off the cards now anyway). There just isn’t any one of those syntaxes that has a dominant support base. Use the folding syntax that you already use throughout IDEA, which all of your users are already familiar with.

  17. Have a look at closures project.
    http://code.google.com/p/closures/

    It does simple closures in java and with IntelliJ “inject language” feature it works brilliantly!

    Following things are now possible in java:
    ————————————————–

    each(asList(“a”,”b”,”c”), “print ‘Element ‘+it+’n'”)
    will print to the System.out:

    Element a
    Element b
    Element c

    find(asList(“ab”, “abc”, “bc”, “bcd”), “it =~ /.*a.*/; “);
    => “ab”

    findAll(asList(1,2,3,4,5,6,7), “it % 2 == 0″)
    => [2,4,6]

    findAll(asList(“ab”, “abc”, “bc”, “bcd”), “it =~ /.*a.*/”)
    => ["ab", "abc"]

    collect(asList(1,2,3), “it *it”)
    => [1,4,9]

    any(asList(“ab”, “abc”, “bc”, “bcd”), “it =~ /bc/; “)
    => true

    any(asList(“ab”, “abc”, “bc”, “bcd”), “it =~ /bcde/; “)
    =>false

    every(asList(“ab”, “abc”, “bc”, “bcd”), “it.length() > 0″)
    => true

    every(asList(“ab”, “abc”, “bc”, “bcd”), “it =~ /.*a.*/; “)
    => false

  18. Vadim Pesochinskiy says:

    I have following code which have to retry something action. I downloaded Maia in anticipation of tada moment, but nothing happens.

    new Retry(3,5000).run(new Retry.If(new Class[] {RemoteException.class} ) {
    public void exec() throws Exception {
    ; // do something that throws RemoteException
    }
    });

    I was kinda hoping to see this:

    new Retry(3,5000).run(new Retry.If(…) {
    ; // do something that throws RemoteException
    });

    So, where is the trick :-( ? I am bitterly disappointed. Make me smile!

  19. Derek Greer says:

    This is a very old thread, but in playing with IntelliJ Community Edition 9.0, the folding doesn’t look anything like this. It basically collapses down where the body reads { … }. In the example presented in the article, the anonymous noise is collapsed, but the body of the method is revealed which is what I’m looking for. Is this not how it works?

  20. Peter Gromov says:

    Derek, it’s not enabled by default. You need to go to the Folding settings and check ‘Closures’ there

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>