Bug 541696 - [formatter] Chained method calls cannot have staggered indentation levels.
Summary: [formatter] Chained method calls cannot have staggered indentation levels.
Status: NEW
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 4.8   Edit
Hardware: PC Windows 10
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: JDT-Core-Inbox CLA
QA Contact:
URL:
Whiteboard: stalebug
Keywords:
Depends on:
Blocks:
 
Reported: 2018-11-29 07:50 EST by Scott Cameron CLA
Modified: 2023-03-21 10:55 EDT (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Scott Cameron CLA 2018-11-29 07:50:12 EST
We are starting to use a "fluent API" style more frequently now, so I'm trying to get the formatter to keep the code clean.

I'm able to mostly achieve what I want by setting the Line Wrapping of Function Calls->Qualified Invocations to "Wrap all elements, every element on a new line".

The missing piece for us is about indentation level.  Sometimes we have chains of calls that work on different context levels, and it would be really nice to be able to express those different contexts as different indentation levels.  Here is an example:

    MyGraph graph = MyGraph.Builder
        .get()
        .add(MyNodeA.Builder)
            .setProperty("aaa")
        .branch(MyNodeB.Builder)
            .setProperty("bbb")
            .setProperty2("ccc")
            .end()
        .branch(MyNodeB.Builder)
            .setProperty("ddd")
            .setProperty2("eee")
            .end()
        .done()
        .build();

If I'm building a graph I have some calls that add a new node and return that node (e.g. "add" and "branch") so subsequent calls will be in the context of that new node.  Some other calls (e.g. "end" and "done") bring you back to a higher-level context.

The problem is that my Qualified Invocation settings reset all of my indentation back to what was set in the wrapping policy.  So I get this:

    MyGraph graph = MyGraph.Builder
        .get()
        .add(MyNodeA.Builder)
        .setProperty("aaa")
        .branch(MyNodeB.Builder)
        .setProperty("bbb")
        .setProperty2("ccc")
        .end()
        .branch(MyNodeB.Builder)
        .setProperty("ddd")
        .setProperty2("eee")
        .end()
        .done()
        .build();

I was wondering if it would be possible to have a setting that would tell the formatter to preserve indentation.  Similar to how there is a setting to "Never join already wrapped lines", maybe there could be a setting to "Never reduce indentation level of already indented lines" or something like that?
Comment 1 Mateusz Matela CLA 2018-12-01 08:47:25 EST
I'm not convinced this is a reasonable use case. Are there many popular APIs that work like that? Because I'd say it would be better designed to look like this:

MyGraph graph = new MyGraph.Builder()
        .add(new MyNodeA.Builder()
            .setProperty("aaa"))
        .branch(new MyNodeB.Builder()
            .setProperty("bbb")
            .setProperty2("ccc"))
        .build();

It seems more natural to use parenthesis rather than start() and end() methods, it makes it easier to manage more levels of depth and you get nice indentation out of the box :)
Comment 2 Scott Cameron CLA 2018-12-03 11:41:38 EST
I'm not actually sure if this is a common pattern or not, but I will try to find out.  The one example that I can think of right away is a visualization-building API called D3 (https://d3js.org/).  It is a JavaScript API but the pattern is similar in that it has the ability to add new DOM nodes and set properties/attributes on those nodes and the styling convention is to use indentation to distinguish the different contexts.

Regarding your specific counter-example, we did indeed consider using parentheses at first because, as you point out, it has some nice benefits such as not needing end() to return to the branch source. (In fact, perhaps we wouldn't even need to distinguish "add" and "branch" with this approach).

The problem, however, is that this is a graph-building API and it can be used to create graphs of arbitrary depth and complexity.  My example below is trivial but what if you had 3 or 4 branches, each with a chain of 3 or 4 nodes?  It wouldn't take long before you have an unworkable amount of indentation.

For this kind of API, the indentation isn't intended to mirror the shape of the graph itself (which would quickly become awkward) but rather to indicate (a) a change of context, and (b) a grouping of leaf operations (e.g. setProperty) for a particular context.

I'll try to find some other API examples, but I think for an fluent API used for building an open-ended structure, the design we're using does make sense.
Comment 3 Eclipse Genie CLA 2020-11-23 00:48:28 EST
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet.

If you have further information on the current state of the bug, please add it. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.
Comment 4 Eclipse Genie CLA 2023-03-21 10:55:29 EDT
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet.

If you have further information on the current state of the bug, please add it. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.