Selecting content with nodes at the boundaries (TextSelection)

Hey there!

I’ve run into an issue with selections while working with ProseMirror. In our Editor implementation you can select nodes with your cursor either by clicking them (NodeSelection), or by clicking/dragging of the cursor starting with text, and then ending on or after the node. The limitation I’ve encountered is when the user selection starts or ends on the node. Visually this works as expected, the node is in selected state, but as soon as any change occurs in the editor (e.g. external change by a collaborator) the selection ‘collapses’ to not include the node.

I believe this is occurring due to the map implementation in TextSelection, which enforces inline from/to positions. This inline only assumption does not match our UX expectations - we intentionally support users creating selections that mix text and nodes (and from a UX perspective — we want to support the selection from or to being placed at the node level).

Given this use case where this is the intended behaviour, would this be something that you would be open supporting via configuration options to allow non-inline positions?

Naturally, we’d also like to support applying these type of Node bounded selections programatically.

For completeness, these are the workaround I’ve tried (with undesired tradeoffs):

  • Use TextSelection.between (or similar) to find the closest inline positions. This means the user will not see selections on nodes at the selection boundaries. Or, we find a broader selection with inline from/to positions, resulting in over-selecting, especially in the case of back-to-back nodes at the boundaries.
  • Create a custom Selection that maps positions without enforcing inline positions. This adds the complexity of supporting a new selection type in our established Editor + plugin suite.
  • Faking the selection using Decorations. This adds extra state management to add, map, clean up the decorations. Additionally, the selection will not be reflected on the state/transactions for plugins to work with.

Cheers!

TextSelection is implemented (and documented) as always having both its end points in an inline position. Existing code makes use of this invariant, and this really isn’t something we would be able to change without causing new issues.

You could try to implement a custom Selection subclass for your use case. Or, probably more practical, make sure you create valid TextSelection instances by expanding them to the next inline position when possible.

Thank you @marijn for the prompt response. Out of curiosity, what is the history behind the inline-only restriction for TextSelection?

Browsers will often do weird things when you try to start or end a selection range in a block position. And a lot of other editors work the same way, limiting cursor motion to inline positions. Having this as an invariant also allows some commands to make some assumptions about text selections, which can be useful.

Related to my other question, I’ve been thinking about kludging something together to handle this and I wonder if you might have a moment to touch on what one might want to make sure still works? Any non-obvious point of failure we might want to look out for?

I’m guessing this might be a tad more feasible for end-developers to implement since they don’t have to worry about breaking the library for millions of users?