This is an archive of the discontinued LLVM Phabricator instance.

[clangd] Plan features for FoldingRanges
AbandonedPublic

Authored by kbobyrev on Jul 15 2020, 3:09 PM.

Details

Reviewers
sammccall
Summary

This is not an actual patch, this is an RFC + TDD plan for future RAV
implementation. Hopefully, the implementation would gradually make all of the
tests from this snippet pass.

Diff Detail

Event Timeline

kbobyrev created this revision.Jul 15 2020, 3:09 PM
Herald added a project: Restricted Project. · View Herald TranscriptJul 15 2020, 3:09 PM
kbobyrev updated this revision to Diff 278327.Jul 15 2020, 3:16 PM

Fix some typos.

kbobyrev updated this revision to Diff 278329.Jul 15 2020, 3:19 PM

Add some missing ranges.

A few different types of feedback mixed up here :-)

Some nitpicks about behavior: these are probably worth discussing.

Places where the coverage is incomplete: obviously until we get closer to implementation, how complete this is is up to you.

Looking at examples mostly reinforced to me that foldability should have some threshold for what's inside as well as rules about grammatically what's foldable. I just can't see editors doing a good job of discarding uninteresting ranges - they're going to consider that our job.
If "only things spanning newlines" is too strict, we could consider maybe other complexity heuristics.

At least for the AST-based folds, I'd like some formalism like:

  • we build a tree of potentially-foldable regions based on the AST
  • starting at the bottom, we decide whether each potentially-foldable region is actually folded
    • any region containing a non-folded newline is folded
    • any bracket-delimited region containing a non-folded comma or semicolon token is folded

Regarding implementation and RAV: we talked about the possibility of using syntax trees, is that still on the table?

clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp
237

nit: the subexpression tests are mixed in with the if cases

246

confused about [[++I]] - why is this foldable but not I > 42 etc - is this because of the containing parens?

FWIW, Personally, I'd want the whole if condition to be foldable (maybe only if it spans lines), and only paren subexpressions, arg lists etc that span lines to be foldable. It's hard to imagine a UX that could expose folding small parens in a way that's not annoying. (e.g. shows lots of extra UI elements, or prevents folding the outer expression)

277

this is a really nice model, but I worry that having multiple folding ranges starting at the same point within a line is going to be a UX nightmare.
I'd suggest picking one.

(Note that other control flow statements can look like for loops too these days, with initializer statements)

329

missing lambdas, blocks, objc methods.
cover bodies in the same tests?
capture lists of lambdas?

342

linkage specs probably belong here too

414

why is the template argument list not foldable here? (it is for the class example above)

552

I think clang-format messed some of these up for you

578

I think consistent with how you handle braces and comments, the range should end just before the #endif

580

I don't think you want the condition as part of the same of the same fold as the content, it might be important

What about

#if [[some_condition]][[
body
]]#elif [[some_condition]][[
body
]]#else[[
body
]]#endif

Rendering (partially folded) as:
#if some_condition ... #elif some_condition ... #else ... #endif

583

here I'd expect folding before # so you can see it's a directive when folded

605

I think YAGNI here - not sure how this could help, and it's an obscure feature to start with

613

Similarly.
*Maybe* generically folding directives that span multiple lines with \. But I think they're rare enough nobody cares.

628

would be lovely to cover objc @interface/@implementation too...

656

You're missing the case where there's stuff in the implicit initial section, and then another accessor section.

It's an interesting one because if you allow the implicit section to be folded, and also the whole class to be folded, then you have two folds triggered at the same location. (Currently there's only one [[[[ in this file, which i've flagged)

718

nit: these are "member initializer lists", not to be confused with "initializer lists" which are the braced things (and should also be covered, I guess!)

kbobyrev planned changes to this revision.Jul 17 2020, 5:42 AM

Will resolve the comments and add some missing examples.

kbobyrev updated this revision to Diff 279132.Jul 19 2020, 11:54 PM
kbobyrev marked 13 inline comments as done.

Resolve most comments, make sure everything builds.

kbobyrev added inline comments.Jul 20 2020, 12:13 AM
clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp
246

Sounds good! Added a separate expressions test to Misc, too.

329

What kind of blocks? Do you mean statement groups? What do you mean by "cover bodies in the same tests"? What kind of bodies?

sammccall added inline comments.Jul 20 2020, 12:30 AM
clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp
329

What kind of blocks?

Parallel-universe lambdas

https://clang.llvm.org/doxygen/classclang_1_1BlockDecl.html#details

(Commonly used in the apple world I think, though it's not actually an extension to objc but rather c)

What do you mean by "cover bodies in the same tests"?

You have to go through the different ways to write functions here, I was just suggesting making sure they are definitions so you can test their body ranges too...

hokein added a subscriber: hokein.Jul 22 2020, 12:12 AM

just some random comments from my side.

At least for the AST-based folds, I'd like some formalism like:

+1. I think we need some general rules (mental model) to guide us how the folding works.

Some editors (e.g. VSCode) have their builtin folding-range, it is worth to investigate how the built folding-range interacts with one provided by the language server.

From my experience, VSCode's built folding-range kind of works though this is not perfect, would be nice to have some concrete examples see the improvements, we may want to prioritize those during the implementation.

clang-tools-extra/clangd/unittests/SemanticSelectionTests.cpp
261

this example seems vague, it is folded because

  1. it is cross-line
  2. it is wrapped by ()

or both?

284

nit: for completeness, add a for-range case.

326

not sure about this case.

if we decide to support this (btw, the test on L242 should be adjusted too), would be nice to support the following usages.

// forward decls
[[class A;
class B;
class C;]]

// using decls;
[[using ns::A;
using ns::B;
using ns::C;]]
556

this [[ seems mismatched, if I read the code correctly.

btw, this example contains too many brackets, considering split into simpler ones.

598

another option: we could just fold the whole preamble region, it seems easier, and provides a visible to see preamble range :), the downside is that any comments/macros will be folded together.

601

These macros are quite simple, I'm not sure this is useful to fold them. I'd just not fold it, but if the macro body is complicated (multi-lines), it is worth folding it.

679

looks like we're missing case of inheritance, e.g. a class inherits multiple classes

class B : public A,
  private C {};
kbobyrev planned changes to this revision.Jul 24 2020, 3:12 PM

Need to resolve the comments.

kbobyrev abandoned this revision.May 19 2022, 5:52 AM
Herald added projects: Restricted Project, Restricted Project. · View Herald TranscriptMay 19 2022, 5:52 AM