<commit_before>import Debug from 'debug'
import React from 'react'
import SlateTypes from 'slate-prop-types'
import Types from 'prop-types'
import invariant from 'tiny-invariant'
import memoizeOne from 'memoize-one'
import warning from 'tiny-warning'
import { Editor as Controller } from 'slate'

import EVENT_HANDLERS from '../constants/event-handlers'
import ReactPlugin from '../plugins/react'

/**
 * Debug.
 *
 * @type {Function}
 */

const debug = Debug('slate:editor')

/**
 * Editor.
 *
 * @type {Component}
 */

class Editor extends React.Component {
  /**
   * Property types.
   *
   * @type {Object}
   */

  static propTypes = {
    autoCorrect: Types.bool,
    autoFocus: Types.bool,
    className: Types.string,
    onChange: Types.func,
    options: Types.object,
    placeholder: Types.any,
    plugins: Types.array,
    readOnly: Types.bool,
    role: Types.string,
    schema: Types.object,
    spellCheck: Types.bool,
    style: Types.object,
    tabIndex: Types.number,
    value: SlateTypes.value.isRequired,
    ...EVENT_HANDLERS.reduce((obj, handler) => {
      obj[handler] = Types.func
      return obj
    }, {}),
  }

  /**
   * Default properties.
   *
   * @type {Object}
   */

  static defaultProps = {
    autoFocus: false,
    autoCorrect: true,
    onChange: () => {},
    options: {},
    placeholder: '',
    plugins: [],
    readOnly: false,
    schema: {},
    spellCheck: true,
  }

  /**
   * Initial state.
   *
   * @type {Object}
   */

  state = {}

  /**
   * Temporary values.
   *
   * @type {Object}
   */

  tmp = {
    mounted: false,
    change: null,
    resolves: 0,
    updates: 0,
  }

  /**
   * When the component first mounts, flush a queued change if one exists.
   */

  componentDidMount() {
    this.tmp.mounted = true
    this.tmp.updates++

    if (this.props.autoFocus) {
      this.focus()
    }

    if (this.tmp.change) {
      this.props.onChange(this.tmp.change)
      this.tmp.change = null
    }
  }

  /**
   * When the component updates, flush a queued change if one exists.
   */

  componentDidUpdate() {
    this.tmp.updates++

    if (this.tmp.change) {
      this.props.onChange(this.tmp.change)
      this.tmp.change = null
    }
  }

  /**
   * Render the editor.
   *
   * @return {Element}
   */

  render() {
    debug('render', this)
    const props = { ...this.props, editor: this }

    // Re-resolve the controller if needed based on memoized props.
    const { commands, placeholder, plugins, queries, schema } = props<commit_msg>Avoid async commands causing rerenders after unmount. (#2395)<commit_after>import Debug from 'debug'
import React from 'react'
import SlateTypes from 'slate-prop-types'
import Types from 'prop-types'
import invariant from 'tiny-invariant'
import memoizeOne from 'memoize-one'
import warning from 'tiny-warning'
import { Editor as Controller } from 'slate'

import EVENT_HANDLERS from '../constants/event-handlers'
import ReactPlugin from '../plugins/react'

/**
 * Debug.
 *
 * @type {Function}
 */

const debug = Debug('slate:editor')

/**
 * Editor.
 *
 * @type {Component}
 */

class Editor extends React.Component {
  /**
   * Property types.
   *
   * @type {Object}
   */

  static propTypes = {
    autoCorrect: Types.bool,
    autoFocus: Types.bool,
    className: Types.string,
    onChange: Types.func,
    options: Types.object,
    placeholder: Types.any,
    plugins: Types.array,
    readOnly: Types.bool,
    role: Types.string,
    schema: Types.object,
    spellCheck: Types.bool,
    style: Types.object,
    tabIndex: Types.number,
    value: SlateTypes.value.isRequired,
    ...EVENT_HANDLERS.reduce((obj, handler) => {
      obj[handler] = Types.func
      return obj
    }, {}),
  }

  /**
   * Default properties.
   *
   * @type {Object}
   */

  static defaultProps = {
    autoFocus: false,
    autoCorrect: true,
    onChange: () => {},
    options: {},
    placeholder: '',
    plugins: [],
    readOnly: false,
    schema: {},
    spellCheck: true,
  }

  /**
   * Initial state.
   *
   * @type {Object}
   */

  state = {}

  /**
   * Temporary values.
   *
   * @type {Object}
   */

  tmp = {
    mounted: false,
    change: null,
    resolves: 0,
    updates: 0,
  }

  /**
   * When the component first mounts, flush a queued change if one exists.
   */

  componentDidMount() {
    this.tmp.mounted = true
    this.tmp.updates++

    if (this.props.autoFocus) {
      this.focus()
    }

    if (this.tmp.change) {
      this.props.onChange(this.tmp.change)
      this.tmp.change = null
    }
  }

  /**
   * When the component updates, flush a queued change if one exists.
   */

  componentDidUpdate() {
    this.tmp.updates++

    if (this.tmp.change) {
      this.props.onChange(this.tmp.change)
      this.tmp.change = null
    }
  }

  /**
   * When the component unmounts, make sure async commands don't trigger react updates.
   */

  componentWillUnmount() {
    this.tmp.mounted = false
  }

  /**
   * Render the editor.
   *
   * @return {Element}
   */

  render() {
    debug('render', this)
    const props = { ...this.props, editor: this }

    // Re-resolve the controller if needed based on memoized props.
    const { commands, placeholder, plugins, queries, schema } = props
{'fraction_non_alphanumeric': 0.13714285714285715, 'fraction_numerical': 0.0015238095238095239, 'mean_word_length': 2.438768827766863, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6336293', 'n_tokens_mistral': 1737, 'n_tokens_neox': 1694, 'n_words': 427}