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-history][lexical-selection][lexical-react] Fix: #6409 TextNode change detection #26

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

Conversation

korbit-ai[bot]
Copy link

@korbit-ai korbit-ai bot commented Aug 15, 2024

User description

Description

  • Targeted fix for lexical-history's TextNode change detection when only one text leaf is dirty but unchanged
  • Add delay option to HistoryPlugin
  • Fix $cloneWithProperties - the previous implementation did not correctly handle __textFormat from ParagraphNode. Also updated the very incorrect doc string.
  • Clean up lint and test warning issues elsewhere
  • Move $cloneWithProperties to the lexical package because the correct implementation was already inlined in LexicalNode.getWritable()

A more general fix may be possible, it's unclear whether this hack is really necessary for MaxLengthPlugin but other text node transforms may have similar issues so removing the hack could be backwards incompatible.

Closes #6409

Test plan

Before

History's TextNode change detection did not work correctly for subclasses where only properties that did not exist on TextNode changed.

After

The tests show that the behavior is consistent with expectations

Description by Korbit AI

Note

This feature is in early access. You can enable or disable it in the Korbit Console.

What change is being made?

Fix text node change detection by updating the isTextNodeUnchanged function, adding a new CustomTextNode class, and modifying related test cases and utility functions.

Why are these changes being made?

These changes address issue #6409 by ensuring that changes to text nodes, including text content and class modifications, are properly detected and reflected in the undo stack. This improves the accuracy of the editor's history tracking and ensures consistent behavior when text nodes are updated.


PR Type

Bug fix, Enhancement, Tests


Description

  • Fixed text node change detection by refining the isTextNodeUnchanged function.
  • Added a new CustomTextNode class and related tests to ensure proper change detection.
  • Enhanced the HistoryPlugin with a delay option for better control.
  • Improved node cloning logic across multiple files to ensure accurate property copying.
  • Fixed typos and removed unnecessary comments for better code clarity.

Changes walkthrough 📝

Relevant files
Bug fix
clipboard.ts
Fix typos and update node cloning logic                                   

packages/lexical-clipboard/src/clipboard.ts

  • Corrected typo in comments.
  • Updated cloning logic for nodes.
+3/-3     
index.ts
Refine text node unchanged detection logic                             

packages/lexical-history/src/index.ts

  • Refined isTextNodeUnchanged function logic.
  • Improved detection of unchanged text nodes.
  • +18/-17 
    index.ts
    Update node cloning logic                                                               

    packages/lexical-html/src/index.ts

    • Updated node cloning logic.
    +1/-1     
    utils.tsx
    Add tags dependency to useFilteredExamples                             

    packages/lexical-website/src/components/Gallery/utils.tsx

    • Added tags dependency to useFilteredExamples.
    +1/-1     
    Tests
    LexicalHistory.test.tsx
    Add tests for CustomTextNode change detection                       

    packages/lexical-history/src/tests/unit/LexicalHistory.test.tsx

  • Added CustomTextNode class for testing.
  • Implemented tests for text node change detection.
  • Added utility functions for CustomTextNode.
  • +133/-9 
    Miscellaneous
    LexicalContentEditable.tsx
    Remove unnecessary TypeScript lint comments                           

    packages/lexical-react/src/LexicalContentEditable.tsx

    • Removed unnecessary TypeScript lint comments.
    +0/-3     
    Enhancement
    LexicalHistoryPlugin.ts
    Add delay option to HistoryPlugin                                               

    packages/lexical-react/src/LexicalHistoryPlugin.ts

    • Added delay option to HistoryPlugin.
    +3/-1     
    lexical-node.ts
    Improve node cloning and property update logic                     

    packages/lexical-selection/src/lexical-node.ts

  • Modified $cloneWithProperties to improve node cloning.
  • Removed return statements in property update functions.
  • +11/-15 

    💡 PR-Agent usage:
    Comment /help on the PR to get a list of all available PR-Agent tools and their descriptions

    Summary by CodeRabbit

    • New Features

      • Introduced a CustomTextNode for enhanced text management with custom CSS classes.
      • Added an optional delay parameter to the HistoryPlugin for improved control over history state updates.
    • Improvements

      • Streamlined the cloning and updating processes for nodes to enhance clarity and efficiency.
      • Enhanced the useFilteredExamples function to react to changes in tags, improving filtering accuracy in the Gallery component.
    • Bug Fixes

      • Corrected grammatical errors in comments for better documentation clarity.
    • Refactor

      • Simplified various functions to focus on procedural updates without returning modified nodes, improving maintainability.

    etrepum and others added 2 commits July 18, 2024 15:56
    Add delay option to HistoryPlugin
    Add test for #6409
    Fix lint issues
    Fix $cloneWithProperties and test expectations
    Targeted fix for TextNode leaf change
    Copy link
    Author

    korbit-ai bot commented Aug 15, 2024

    Clone of the PR facebook/lexical#6420

    Copy link
    Author

    korbit-ai bot commented Aug 15, 2024

    My review is in progress 📖 - I will have feedback for you in a few minutes!

    Copy link

    coderabbitai bot commented Aug 15, 2024

    Important

    Review skipped

    Auto reviews are disabled on this repository.

    Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

    You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

    Walkthrough

    This update enhances the Lexical codebase by streamlining node cloning processes, improving type definitions, and refining history management functionalities. Key additions include the CustomTextNode class for better text node customization and an optional delay parameter for the HistoryPlugin. The changes emphasize better practices, efficiency, and flexibility while maintaining core functionalities across various components.

    Changes

    Files Change Summary
    packages/lexical-clipboard/src/clipboard.ts Modified $appendNodesToJSON function to streamline cloning by removing generic type parameter from $cloneWithProperties. Adjusted comments for clarity.
    packages/lexical-history/src/__tests__/unit/LexicalHistory.test.tsx Introduced CustomTextNode class for managing custom CSS classes and added utility functions for node creation and type checking. Enhanced tests to validate changes in text content and class attributes.
    packages/lexical-history/src/index.ts Refactored isTextNodeUnchanged for improved readability and efficiency in line deletion checks. Simplified node comparison using JSON.stringify.
    packages/lexical-html/src/index.ts Removed type parameter from $cloneWithProperties in $appendNodesToHTML, allowing for improved type inference.
    packages/lexical-react/src/LexicalContentEditable.tsx Updated Props type to remove deprecated property, enhancing type clarity and compliance with current best practices.
    packages/lexical-react/src/LexicalHistoryPlugin.ts Added optional delay parameter to HistoryPlugin for better control over history state updates.
    packages/lexical-selection/src/lexical-node.ts Updated return types of several node update functions to void, emphasizing procedural updates over object returns. Streamlined cloning process.
    packages/lexical-website/src/components/Gallery/utils.tsx Updated dependencies in useFilteredExamples to include tags, enhancing responsiveness to filtering changes.

    Sequence Diagram(s)

    sequenceDiagram
        participant User
        participant Editor
        participant History
        User->>Editor: Make changes to text node
        Editor->>History: Log changes
        History-->>Editor: Update history state
        User->>Editor: Undo changes
        Editor->>History: Fetch previous state
        History-->>Editor: Restore text node
    
    Loading

    Poem

    🐇 In a world of nodes and lines,
    The rabbit hops, where code aligns.
    With custom text and history's sway,
    We dance and code, in joyful play.
    Cloning made simple, oh what a treat,
    Hopping through changes, so nimble and neat! 🌼


    Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

    Share
    Tips

    Chat

    There are 3 ways to chat with CodeRabbit:

    • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
      • I pushed a fix in commit <commit_id>.
      • Generate unit testing code for this file.
      • Open a follow-up GitHub issue for this discussion.
    • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
      • @coderabbitai generate unit testing code for this file.
      • @coderabbitai modularize this function.
    • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
      • @coderabbitai generate interesting stats about this repository and render them as a table.
      • @coderabbitai show all the console.log statements in this repository.
      • @coderabbitai read src/utils.ts and generate unit testing code.
      • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
      • @coderabbitai help me debug CodeRabbit configuration file.

    Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

    CodeRabbit Commands (invoked as PR comments)

    • @coderabbitai pause to pause the reviews on a PR.
    • @coderabbitai resume to resume the paused reviews.
    • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
    • @coderabbitai full review to do a full review from scratch and review all the files again.
    • @coderabbitai summary to regenerate the summary of the PR.
    • @coderabbitai resolve resolve all the CodeRabbit review comments.
    • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
    • @coderabbitai help to get help.

    Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

    CodeRabbit Configuration File (.coderabbit.yaml)

    • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
    • Please see the configuration documentation for more information.
    • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

    Documentation and Community

    • Visit our Documentation for detailed information on how to use CodeRabbit.
    • Join our Discord Community to get help, request features, and share feedback.
    • Follow us on X/Twitter for updates and announcements.

    Copy link
    Author

    @korbit-ai korbit-ai bot left a comment

    Choose a reason for hiding this comment

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

    I have reviewed your code and found 2 potential issues.

    Comment on lines 213 to 216
    return (
    prevNode.__type === nextNode.__type &&
    prevNode.__text === nextNode.__text &&
    prevNode.__mode === nextNode.__mode &&
    prevNode.__detail === nextNode.__detail &&
    prevNode.__style === nextNode.__style &&
    prevNode.__format === nextNode.__format &&
    prevNode.__parent === nextNode.__parent
    JSON.stringify(prevEditorState.read(() => prevNode.exportJSON())) ===
    JSON.stringify(nextEditorState.read(() => nextNode.exportJSON()))
    );
    Copy link
    Author

    Choose a reason for hiding this comment

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

    category Functionality

    The current implementation of isTextNodeUnchanged uses JSON.stringify and exportJSON to compare text nodes. This approach might miss some subtle changes or introduce unnecessary comparisons. Consider implementing a more direct comparison of the relevant properties of the text nodes, such as __text, __mode, __format, and __style. This will ensure more accurate change detection and potentially improve performance.

    Chat with Korbit by mentioning @korbit-ai, and give a 👍 or 👎 to help Korbit improve your reviews.

    Comment on lines 19 to 31
    export function HistoryPlugin({
    delay,
    externalHistoryState,
    }: {
    delay?: number;
    externalHistoryState?: HistoryState;
    }): null {
    const [editor] = useLexicalComposerContext();

    useHistory(editor, externalHistoryState);
    useHistory(editor, externalHistoryState, delay);

    return null;
    }
    Copy link
    Author

    Choose a reason for hiding this comment

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

    category Functionality

    The new delay parameter has been added to the HistoryPlugin function and passed to the useHistory hook. However, there's no documentation or explanation about what this parameter does or how it affects the plugin's behavior. Could you please add a comment or JSDoc explaining the purpose of the delay parameter and how it's used in the history functionality? This will help other developers understand how to use this new option correctly.

    Chat with Korbit by mentioning @korbit-ai, and give a 👍 or 👎 to help Korbit improve your reviews.

    @furwellness
    Copy link
    Owner

    /review

    Copy link

    codiumai-pr-agent-free bot commented Aug 20, 2024

    PR Reviewer Guide 🔍

    (Review updated until commit cc5b270)

    ⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
    🧪 PR contains tests
    🔒 No security concerns identified
    ⚡ Key issues to review

    Logic Change
    The isTextNodeUnchanged function has been significantly modified. The new implementation uses JSON.stringify to compare node states, which might have performance implications for large documents.

    New Test Case
    A new test case has been added to verify the fix for issue #6409. This test introduces a CustomTextNode class and checks if changes to TextNode leaf are detected properly.

    Function Refactoring
    The $cloneWithProperties function has been refactored. The changes might affect how node properties are copied during cloning operations.

    Copy link

    Persistent review updated to latest commit cc5b270

    @furwellness furwellness reopened this Aug 21, 2024
    @furwellness furwellness changed the base branch from cloned_main_40f6c to main August 21, 2024 12:40
    @furwellness
    Copy link
    Owner

    @coderabbitai full review

    Copy link

    coderabbitai bot commented Aug 21, 2024

    Actions performed

    Full review triggered.

    Copy link

    Persistent review updated to latest commit cc5b270

    Copy link

    PR Code Suggestions ✨

    CategorySuggestion                                                                                                                                    Score
    Performance
    Optimize text node comparison by checking specific properties instead of stringifying the entire JSON

    Consider using a more efficient comparison method for text nodes. Instead of
    stringifying and comparing the entire JSON, you could compare specific properties
    that are likely to change, such as __text, __format, and __style.

    packages/lexical-history/src/index.ts [213-216]

     return (
    -  JSON.stringify(prevEditorState.read(() => prevNode.exportJSON())) ===
    -  JSON.stringify(nextEditorState.read(() => nextNode.exportJSON()))
    +  prevNode.__text === nextNode.__text &&
    +  prevNode.__format === nextNode.__format &&
    +  prevNode.__style === nextNode.__style
     );
     
    • Apply this suggestion
    Suggestion importance[1-10]: 8

    Why: The suggestion improves performance by avoiding the overhead of JSON stringification and directly comparing relevant properties, which is more efficient and sufficient for detecting changes.

    8
    Enhancement
    Add a default value for the delay parameter in the HistoryPlugin function

    Add a default value for the delay parameter to maintain backwards compatibility and
    provide a sensible default.

    packages/lexical-react/src/LexicalHistoryPlugin.ts [19-25]

     export function HistoryPlugin({
    -  delay,
    +  delay = 1000,
       externalHistoryState,
     }: {
       delay?: number;
       externalHistoryState?: HistoryState;
     }): null {
     
    • Apply this suggestion
    Suggestion importance[1-10]: 7

    Why: Providing a default value for the delay parameter enhances usability and maintains backward compatibility, ensuring the function behaves predictably without requiring explicit delay input.

    7
    Best practice
    Improve type safety in node cloning by using separate type guards for the original node and its clone

    Consider using a type guard to ensure that clone is of the correct type before
    updating its properties. This will improve type safety and prevent potential runtime
    errors.

    packages/lexical-selection/src/lexical-node.ts [79-86]

    -if ($isElementNode(node) && $isElementNode(clone)) {
    -  $updateElementNodeProperties(clone, node);
    -  if ($isParagraphNode(node) && $isParagraphNode(clone)) {
    -    $updateParagraphNodeProperties(clone, node);
    +if ($isElementNode(node)) {
    +  if ($isElementNode(clone)) {
    +    $updateElementNodeProperties(clone, node);
    +    if ($isParagraphNode(node) && $isParagraphNode(clone)) {
    +      $updateParagraphNodeProperties(clone, node);
    +    }
       }
    -} else if ($isTextNode(node) && $isTextNode(clone)) {
    -  $updateTextNodeProperties(clone, node);
    +} else if ($isTextNode(node)) {
    +  if ($isTextNode(clone)) {
    +    $updateTextNodeProperties(clone, node);
    +  }
     }
     
    • Apply this suggestion
    Suggestion importance[1-10]: 6

    Why: The suggestion enhances type safety by ensuring that both the original node and its clone are of the expected types before updating properties, reducing the risk of runtime errors.

    6

    Copy link

    @coderabbitai coderabbitai bot left a comment

    Choose a reason for hiding this comment

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

    Actionable comments posted: 0

    Outside diff range, codebase verification and nitpick comments (2)
    packages/lexical-history/src/__tests__/unit/LexicalHistory.test.tsx (1)

    382-439: Consider adding comments to the test case.

    Adding comments to the test case for Changes to TextNode leaf are detected properly #6409 would improve readability and understanding of the test logic.

    + // Initialize the editor with CustomTextNode
    editor = createTestEditor({
      nodes: [CustomTextNode],
    });
    packages/lexical-clipboard/src/clipboard.ts (1)

    270-274: Acknowledgment: Grammatical correction in comments.

    The correction from "it's" to "its" improves the clarity of the documentation.

    Review details

    Configuration used: CodeRabbit UI
    Review profile: CHILL

    Commits

    Files that changed from the base of the PR and between 4cbb85f and cc5b270.

    Files selected for processing (8)
    • packages/lexical-clipboard/src/clipboard.ts (2 hunks)
    • packages/lexical-history/src/tests/unit/LexicalHistory.test.tsx (4 hunks)
    • packages/lexical-history/src/index.ts (1 hunks)
    • packages/lexical-html/src/index.ts (1 hunks)
    • packages/lexical-react/src/LexicalContentEditable.tsx (2 hunks)
    • packages/lexical-react/src/LexicalHistoryPlugin.ts (1 hunks)
    • packages/lexical-selection/src/lexical-node.ts (2 hunks)
    • packages/lexical-website/src/components/Gallery/utils.tsx (1 hunks)
    Additional comments not posted (11)
    packages/lexical-react/src/LexicalHistoryPlugin.ts (1)

    20-28: LGTM!

    The addition of the delay parameter to the HistoryPlugin function is well-integrated with the existing useHistory hook.

    packages/lexical-website/src/components/Gallery/utils.tsx (1)

    54-54: LGTM!

    The inclusion of tags in the dependency array of useFilteredExamples enhances the reactivity of the filtering logic.

    packages/lexical-react/src/LexicalContentEditable.tsx (1)

    Line range hint 16-41: LGTM!

    The removal of the deprecated editor__DEPRECATED property and ESLint disable comments enhances the maintainability and clarity of the code.

    packages/lexical-html/src/index.ts (1)

    106-106: LGTM!

    The removal of the explicit type parameter <LexicalNode> from the $cloneWithProperties call enhances type inference and flexibility.

    packages/lexical-history/src/__tests__/unit/LexicalHistory.test.tsx (1)

    46-104: LGTM!

    The introduction of the CustomTextNode class and related utility functions enhances the flexibility and usability of text node management.

    packages/lexical-history/src/index.ts (1)

    213-216: Consider direct property comparison for performance.

    The existing comment suggests using a more direct comparison of relevant properties for performance improvements.

    packages/lexical-clipboard/src/clipboard.ts (1)

    259-259: Verify the impact of removing the generic type parameter.

    The removal of the generic type parameter from $cloneWithProperties may increase flexibility but could affect type safety. Ensure this change aligns with the intended use and doesn't introduce issues.

    Run the following script to verify the usage of $cloneWithProperties and ensure type safety is maintained:

    packages/lexical-selection/src/lexical-node.ts (4)

    39-39: LGTM: Return type change to void.

    The change to a void return type emphasizes in-place updates, aligning with the procedural nature of the function.


    51-51: LGTM: Return type change to void.

    The change to a void return type emphasizes in-place updates, aligning with the procedural nature of the function.


    61-61: LGTM: Return type change to void.

    The change to a void return type emphasizes in-place updates, aligning with the procedural nature of the function.


    80-85: LGTM: Streamlined logic in $cloneWithProperties.

    The streamlined logic enhances clarity by directly applying updates and returning the clone, aligning with the intended procedural updates.

    @furwellness
    Copy link
    Owner

    Zero comments from coderabbit

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Projects
    None yet
    Development

    Successfully merging this pull request may close these issues.

    2 participants