Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Contiguous scope #2101

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open

Contiguous scope #2101

wants to merge 39 commits into from

Conversation

AndreasArvidsson
Copy link
Member

@AndreasArvidsson AndreasArvidsson commented Dec 6, 2023

Fixes #1835

Now implemented as a modifier of the comment scope

I decided to implement this as a scope type instead of a modifier. Now you can say things like chuck fat comment or changed next fat alpha. This behavior of having alternatives to existing scopes is already something we started with BoundedNonWhitespaceSequenceStage albeit in a less capacity.

Checklist

  • I have added tests
  • [-] I have updated the docs and cheatsheet
  • I have not broken the cheatsheet

Copy link
Member

@pokey pokey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like a good implementation approach. left some comments. also, please add some tests with "next" / "previous", and be sure to test boundaries on edges of scopes and between constituent scopes

AndreasArvidsson and others added 11 commits December 6, 2023 16:21
…andlers/ContiguousScopeHandler.ts

Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com>
…andlers/ContiguousScopeHandler.ts

Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com>
…andlers/ContiguousScopeHandler.ts

Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com>
…andlers/ContiguousScopeHandler.ts

Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com>
…andlers/ContiguousScopeHandler.ts

Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com>
…andlers/ContiguousScopeHandler.ts

Co-authored-by: Pokey Rule <755842+pokey@users.noreply.github.com>
Copy link
Member

@pokey pokey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well this code simplified nicely 😄. Left a couple more comments

};
}

function* generateTargetRangesInDirection(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not convinced this function is correct. Eg corner cases like single scope not adjacent to others that's last in the file. To me it looks like if you've already yielded anything, then the final yield after the loop body won't fire.

Some proper unit tests for ContiguousScopeHandler would help. See https://github.com/cursorless-dev/cursorless/blob/main/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/BaseScopeHandler.test.ts if you're not sure how to do unit tests for this kinda thing. There's a limit to how far you can get using existing scopes

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually might make sense to hold off on unit tests for the following reasons:

  1. Your code actually looks at document text, unlike the test I linked, so might be harder to mock that properly
  2. On second thought, I think your code is probably correct
  3. I'm actually not convinced of the utility of this contiguous scope thing 😅. The only example I saw that was actually useful was "comment", and tbh I'd be tempted to just automatically merge adjacent single-line comments, as we could target a single one using "line". And I worry users will try "fat arg" to get something like Support "all" modifier #473, and then be surprised when it breaks if there's a comment between args. So maybe we ship this with slightly fewer tests, maybe marked private? idk

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update from discussion today:

  • We agreed "comment" is the only use case we can come up with, and we probably just want that to be default behaviour for line comments
  • If @josharian wants to fight for supporting "fat" for arbitrary scopes, then we might keep it; otherwise it dies
  • We potentially could reuse this code to make contiguous be default behaviour for comment

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If @josharian wants to fight for supporting "fat" for arbitrary scopes, then we might keep it; otherwise it diesau

looking at these test cases, i think it should die

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. Any opinion on making single-line comments behave this way by default? (Ie auto expand to all comments that aren't separated by an empty line)

Copy link
Member

@pokey pokey Jan 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update from meet-up: let's do the following:

(
  (comment) @comment
  (#match? @comment "^//")
  (#contiguous! @comment)
)
(comment) @comment

We both don't love this option or the option in this PR, but it's the best we can do with what we have today. Fwiw we would prefer something like

(
  (comment) @comment
  ($if
    (#match? @comment "^//")
    (#contiguous! @comment)
  )
)

but that's a bit out of scope here 😅

Comment on lines 132 to 135
const [startTarget, endTarget] = getTargetsInDocumentOrder(
scope1.getTargets(false)[0],
scope2.getTargets(false)[0],
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we want to throw an error if there's more than one target? Should we have a utility function function ensureSingleTarget(scope: Scope): Target that checks there's only one target and returns it? That worked out well for ensureSingleEditor I think

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reused our existing and shore single target. They should really never trigger with our current implementation so am fine with using an error message that might not be perfect.

@AndreasArvidsson
Copy link
Member Author

AndreasArvidsson commented Dec 14, 2023

@pokey @josharian The current implementation now only effects the command scope type and there is no spoken form for it.

return true;
}

const [startTarget, endTarget] = getTargetsInDocumentOrder(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is now general enough to be used with most scopes. If we wanted to we could remove a bunch of code here and rely on the fact that we only use it for comments. I could go either way.

@pokey
Copy link
Member

pokey commented Dec 14, 2023

Won't this cause adjacent multiline comments to be merged as well? eg

/*
 * foo
 */
/*
 * bar
 */

Looking at this, it feels like not a particularly big deal 😅. But figured I'd point it out as I don't think it quite matches what we discussed

@AndreasArvidsson
Copy link
Member Author

Yes unless we make this more comment specific and check for single line ranges. Ideally we would have single and multi-line as separate scopes and only use this for single line.

Or add a new predicate for contiguous scope. That is probably the best solution?

@pokey
Copy link
Member

pokey commented Dec 14, 2023

hmm so the predicate would just set a flag on the capture. I think that sounds reasonable?

@AndreasArvidsson
Copy link
Member Author

Yes exactly.

@AndreasArvidsson
Copy link
Member Author

AndreasArvidsson commented Dec 15, 2023

There is no problem really with this implementation except for the fact that in javascript there is no Tree sitter difference between block and line comments. I guess that contiguous predicate could take a boolean that requires single line ranges? Or a regex pattern?

edit: Solved with a regex pattern

Copy link
Member

@pokey pokey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

left a few comments

Comment on lines 154 to 165

/**
* Returns true if the given scope type should use a contiguous scope handler.
*/
function useContiguousScopeHandler(scopeType: ScopeType): boolean {
switch (scopeType.type) {
case "comment":
return true;
default:
return false;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought you were using a query predicate for this now

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use both. I don't know how to check it at this stage. When we actually fetch the matched scope it's to late to inject this handler. Not without a lot of rewrites at least.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I'm not following how you would use both. When do you use one vs the other?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code decides to use the handler. Then the scope.contiguous is used to determine when to merge two scopes. That way we can ignore block comments.

name = "contiguous!" as const;
schema = z.union([z.tuple([q.node]), z.tuple([q.node, q.string])]);

run(nodeInfo: MutableQueryCapture, pattern?: string) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yikes. This is a slippery slope. This operator merges to completely orthogonal concepts. I fear we're sleepwalking into the same problem we had with our old typescript-based matchers, where we had 5 custom functions per language.

Maybe we could add a predicate that rejects a pattern if it doesn't match a regex, and then define the pattern twice: once with positive match, one negative?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still not language specific. Probably more languages that dont separate block comments in tree sitter.

That could also work, but is that really easier to read and maintain?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In what way is the concept of regex pattern related to whether we want a contiguous scope? Feels like we're starting to invent a predicate for every arbitrary combination of abstractions. This feels like the next step from the multi-line ternary thing towards a combinatorial explosion of predicates

I think that would be easier to read, because otherwise I would look at the predicate and be like "why does this have anything to do with a regex pattern?"

Happy to discuss if you'd like

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the pattern matches a single line comment? look how I use it in js.

Lets.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to discuss Plan to discuss at meet-up

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update from meet-up:

  • Remove pattern arg from this predicate
  • use the built-in tree-sitter match predicate, and duplicate the pattern, once with contiguous and once without

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

@AndreasArvidsson AndreasArvidsson added the to discuss Plan to discuss at meet-up label Dec 15, 2023
@pokey pokey removed the to discuss Plan to discuss at meet-up label Jan 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add contiguous scope modifier
3 participants