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

[lexical] Feature: Implement Editor.read and EditorState.read with editor argument #6347

Merged
merged 10 commits into from
Jul 14, 2024

Conversation

etrepum
Copy link
Collaborator

@etrepum etrepum commented Jun 27, 2024

Description

Adds an options argument to EditorState.read allowing the activeEditor to be set.

Adds a new Editor.read method as a convenience that can call EditorState.read with the editor set.

Closes #6346

Copy link

vercel bot commented Jun 27, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
lexical ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 12, 2024 2:23pm
lexical-playground ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 12, 2024 2:23pm

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 27, 2024
Copy link

github-actions bot commented Jun 27, 2024

size-limit report 📦

Path Size
lexical - cjs 28.48 KB (0%)
lexical - esm 28.29 KB (0%)
@lexical/rich-text - cjs 36.93 KB (0%)
@lexical/rich-text - esm 28.16 KB (0%)
@lexical/plain-text - cjs 35.6 KB (0%)
@lexical/plain-text - esm 25.34 KB (0%)
@lexical/react - cjs 38.8 KB (0%)
@lexical/react - esm 29.31 KB (0%)

@etrepum etrepum changed the title [WIP] [lexical] Feature: Implement Editor.read and EditorState.read with editor argument [lexical] Feature: Implement Editor.read and EditorState.read with editor argument Jun 28, 2024
@etrepum etrepum marked this pull request as ready for review June 28, 2024 19:32
Copy link
Member

@zurfyx zurfyx left a comment

Choose a reason for hiding this comment

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

Thank you Bob! Added two opinionated comments, I wonder what the rest think, particularly the folks who were involved in the discussion @fantactuka @ivailop7 @StyleT

Comment on lines 1127 to 1129
* @param options.pending - Use the pending editorState. Use this only when
* it is necessary to read the state that has not yet been reconciled (this
* is the state that you would be working with from editor.update).
Copy link
Member

Choose a reason for hiding this comment

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

IMO this behavior is confusing, .read should always grab the pending state just like update. You would not expect an update to be done on top of the reconciler, but rather on top of your previous changes, likewise for

editor.update(() => {
  // append paragraph
});
editor.read(() => {
 // paragraph should be here
});

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't hold any strong opinions on the API, so long as the option exists. I implemented it this way mostly based on the loudest opinions at the time 😆

Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm with Gerard on this one.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm surprised that this would be your preference since the DOM use case would really be best handled with the latest reconciled editor state rather than the pending editor state since the DOM is not necessarily in sync with that yet.

@@ -108,8 +112,12 @@ export class EditorState {
return this._nodeMap.size === 1 && this._selection === null;
}

read<V>(callbackFn: () => V): V {
return readEditorState(this, callbackFn);
read<V>(callbackFn: () => V, options?: EditorStateReadOptions): V {
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 a fan of this option like I stated in #6346 (comment)

While the intent is valid, I feel like it can easily lead to pitfalls where the editor is no longer compatible with the EditorState.

For reference, @ivailop7 mentioned a real-use case around DOM (for tables) and this can potentially fail and be hard to debug with this API, whereas editor.read is intuitive and always does what expected.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm totally open to leaving EditorState.read's signature alone and have Editor.read call readEditorState directly. I'll wait until there seems to be some consensus before changing it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I only implemented it this way because it seemed to be the stated preference of @StyleT on the call and @fantactuka in #6346

Copy link
Contributor

Choose a reason for hiding this comment

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

My point was that we shouldn't have 2 ways of reading the state. But we may sugar coat some API. As mentioned in #6346 (comment) the idea is the following:

  1. editor.read calls EditorState.read while passing correct activeEditor as an option
  2. We promote editor.read in the documentation, while keeping EditorState.read for backward compatibility reasons and for more advanced use cases

This allows to:
a. Achieve API consistency
b. Avoid confusing "regular users" with 2 similar APIs
c. Reduce code duplication and establish relation between related APIs "in code"

Comment on lines 1127 to 1129
* @param options.pending - Use the pending editorState. Use this only when
* it is necessary to read the state that has not yet been reconciled (this
* is the state that you would be working with from editor.update).
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm with Gerard on this one.

@@ -22,6 +22,7 @@ import {
$createNodeSelection,
$createParagraphNode,
$createTextNode,
$getEditor,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could I add a small request to add a test, that checks if '$getNearestNodeFromDOMNode' can be called within the new "read" method.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sure, it does work, just added tests. Noticed that there's a small inconsistency in that the root node is never set in the _keyToDOMMap. Not sure if is really intentional or not.

It does only run the tests with the latest reconciled state (the current default) because that is really what probably makes the most sense for this use case.

@etrepum
Copy link
Collaborator Author

etrepum commented Jun 30, 2024

It seems there's not yet any consensus as to whether Editor.read should choose the pending or previous state.

pending: @zurfyx, @ivailop7
previous: @StyleT, @fantactuka

I don't have any strong preference, as long as both are possible. I lean slightly towards previous state because it matches what's rendered (and after transforms) which is possibly a more generally useful scenario. I think users can be surprised either way ("read doesn't have my latest writes!" or "the dom doesn't match what I'm reading! shouldn't my transforms prevent the state from looking like this?!"). For my personal use cases I mostly just wanted to be able to get a handle to the editor from a read to get access to information that's independent of the current state (config, theme, builder, etc.).

I also don't have any strong preference as to whether we add options to EditorState.read (the current implementation, suggested by @StyleT and @fantactuka), or have Editor.read directly call into the lower-level internal readEditorState (presumably what @zurfyx would prefer?).

Poking around in the implementation of $getNearestNodeFromDOMNode I noticed that we don't ever consider the RootNode in any of those calls. Not sure if that was an intentional decision or just an accident based on the implementation detail that only the children of RootNode are reconciled in the standard way so storeDOMWithKey is never called (but it could be in setRootElement). I think it would mostly only matter in the nested editor case?

@etrepum
Copy link
Collaborator Author

etrepum commented Jul 1, 2024

I think we'd have to add a different method from getEditorState() because flushing there would be a breaking change, it's pretty easy to find situations where it's expected to return a previous state (e.g. editorStateHasDirtySelection is the first one).

Adding a flush-on-read (or flush-on-whatever-read-calls) and having it work nested at the end of an update is probably necessary because command listeners are wrapped in an implicit update but I think a lot of code doesn't really consider that and sets up their own read contexts.

I've implemented flush-on-read in the latest commit.

Copy link
Contributor

@StyleT StyleT left a comment

Choose a reason for hiding this comment

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

LGTM!

@etrepum
Copy link
Collaborator Author

etrepum commented Jul 12, 2024

I updated the branch and the tests are passing again (I guess main was broken earlier this week)

@ivailop7 ivailop7 added the extended-tests Run extended e2e tests on a PR label Jul 14, 2024
@zurfyx zurfyx added this pull request to the merge queue Jul 14, 2024
Merged via the queue into facebook:main with commit 154c5d9 Jul 14, 2024
74 of 75 checks passed
etrepum added a commit to etrepum/lexical that referenced this pull request Jul 14, 2024
2wheeh pushed a commit to 2wheeh/lexical that referenced this pull request Jul 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. extended-tests Run extended e2e tests on a PR
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Feature: Provide a variant of EditorState.read that sets activeEditor
5 participants