Typescript: Some outlining spans dropped for chained method call

Created on 10 Jul 2019  路  6Comments  路  Source: microsoft/TypeScript


TypeScript Version: [email protected]


Search Terms:

  • folding / fold / outlining
  • getOutliningSpans

Code
For the code:

 router
    .get("/", async(ctx) => {
        ctx.body = "base";
    })
    .post("/a", async(ctx) => {
        //a
    })
    .post("/b", async(ctx) => {
        //b
    })
    .post("/c", async(ctx) => {
        //c
    })
    .get("/d", async(ctx) => {
        //d
    }) 
    .get("/e", async(ctx) => {
        //e
    })
    .get("/f", async(ctx) => {
        //f
    })
    .get("/g", async(ctx) => {
        //g
    })
    .patch("/h", async(ctx) => {
        //h
    })
    .patch("/i", async(ctx) => {
        //i
    })
    .get("/j", async(ctx) => {
        //j
    })
    .delete("/k", async(ctx) => {
        //k
    })
    .post("/l", async(ctx) => {
        //l
    })
    .get("/m", async(ctx) => {
        //m
    })
    .delete("/n", async(ctx) => {
        //n
    })
    .patch("/o", async(ctx) => {
        //o
    })
    .patch("/p", async(ctx) => {
        //p
    })
    .patch("/q", async(ctx) => {
        //q
    })
    .patch("/r", async(ctx) => {
        //r
    })
    .get("/s", async(ctx) => {
        //s
    })
    .get("/t", async(ctx) => {
        //t
    })
    .get("/u", async(ctx) => {
        //u
    })
    .post("/v", async(ctx) => {
        //v
    })
    .patch("/w", async(ctx) => {
        //w
    })
    .get("/x", async(ctx) => {
        //x
    })
    .patch("/y", async(ctx) => {
        //y
    })
    .get("/z", async(ctx) => {
        //z
    })
    .post("/a1", async(ctx) => {
        //a1
    })
    .get("/b1", async(ctx) => {
        //b1
    })
    .put("/c1", async(ctx) => {
        //c1
    })
    .get("/d1", async(ctx) => {
        //d1
    })
    .get("/e1", async(ctx) => {
        //e1
    })
    .post("/f1", async(ctx) => {
        //f1
    })

Try folding everything

Actual behavior:
The getOutliningSpans response doesn't include the initial spans

Trace  - 9:46:15 AM] <syntax> Response received: getOutliningSpans (5). Request took 288 ms. Success: true 
Result: [
    {
        "textSpan": {
            "start": {
                "line": 44,
                "offset": 32
            },
            "end": {
                "line": 46,
                "offset": 6
            }
        },
        "hintSpan": {
            "start": {
                "line": 44,
                "offset": 19
            },
            "end": {
                "line": 46,
                "offset": 6
            }
        },
        "bannerText": "...",
        "autoCollapse": false,
        "kind": "code"
    },
    {
        "textSpan": {
            "start": {
                "line": 47,
                "offset": 31
            },
            "end": {
                "line": 49,
                "offset": 6
            }
        },
        "hintSpan": {
            "start": {
                "line": 47,
                "offset": 18
            },
            "end": {
                "line": 49,
                "offset": 6
            }
        },
        "bannerText": "...",
        "autoCollapse": false,
        "kind": "code"
    },
    {
        "textSpan": {
            "start": {
                "line": 50,
                "offset": 31
            },
            "end": {
                "line": 52,
                "offset": 6
            }
        },
        "hintSpan": {
            "start": {
                "line": 50,
                "offset": 18
            },
            "end": {
                "line": 52,
                "offset": 6
            }
        },
        "bannerText": "...",
        "autoCollapse": false,
        "kind": "code"
    },
    {
        "textSpan": {
            "start": {
                "line": 53,
                "offset": 31
            },
            "end": {
                "line": 55,
                "offset": 6
            }
        },
        "hintSpan": {
            "start": {
                "line": 53,
                "offset": 18
            },
            "end": {
                "line": 55,
                "offset": 6
            }
        },
        "bannerText": "...",
        "autoCollapse": false,
        "kind": "code"
    },
    {
        "textSpan": {
            "start": {
                "line": 56,
                "offset": 31
            },
            "end": {
                "line": 58,
                "offset": 6
            }
        },
        "hintSpan": {
            "start": {
                "line": 56,
                "offset": 18
            },
            "end": {
                "line": 58,
                "offset": 6
            }
        },
        "bannerText": "...",
        "autoCollapse": false,
        "kind": "code"
    },
...

Playground Link:

Related Issues:

Bug Outlining Fixed

Most helpful comment

@hzb Well I mean, those other file types don't use the TypeScript language service for parsing so it's not surprising that they would behave differently :smile:

All 6 comments

Outlining only goes to a finite depth for performance reasons. @mjbvz thoughts on exactly how deep we want to go? This example seems pathological.

That's fine in general and makes sense from an AST standpoint, but from the user's point of view there are two things:

  • We drop folds near the top of the file (see the results starting on line 44). If we dropped spans at the end of the structure instead it'd be more clear what is going on.
  • The code looks flat. Not sure if/how we can make it clear why structures like these may not have folding for all elements

Digging in the code a bit more, we do have some places where we cheat the AST depth when limiting our probing. For example, if (e1) { } else if (e2) { } puts the second if block at the "same" depth as the the first one so that long chains of if-else pairs don't cause the depth limiter to bottom out at code that is visually at the same depth.

This example seems pretty similar; if we have

 * Method call
   * Operand
     * Method call
       * Operand
         * Method call
           * Operand
           * Arguments
         * Arguments
       * Arguments
     * Arguments
   * Arguments

we could treat the operand to be the same depth as the parent method call.

This has the disadvantage that things like a.b(c).d(e).f(g)... will get deeply probed, but that seems more rare than the OP's example.

I tried so many times, and I got something, if the file's extension name is .js, (e.g. 1.js a.js), this bug will appear, but if the file's extension name is not .js, (e.g. 1.html, a.py), this bug will not appear .

Hope this can help.

@hzb Well I mean, those other file types don't use the TypeScript language service for parsing so it's not surprising that they would behave differently :smile:

Thanks @dragomirtitian!

Was this page helpful?
0 / 5 - 0 ratings