<commit_before>import * as symbols from './symbols.js';
import Input from './Input.js';


/**
 * A text input box that completes text as the user types
 * 
 * @inherits Input
 */
class AutoCompleteInput extends Input {

  componentDidMount() {
    if (super.componentDidMount) { super.componentDidMount(); }

    // In many ways it would be cleaner to do AutoComplete work in a keydown
    // listener. Unfortunately, Chrome for Android sets the keyCode on *all*
    // keydown events to a mysterious 229 value, making it impossible for us to
    // look at the keyCode and determine whether the user is typing a key that
    // should trigger AutoComplete.
    //
    // Instead, we listen to input events. That comes with its own
    // set of headaches, noted below.
    this.$.inner.addEventListener('input', () => {
      // Gboard will generate multiple input events for a single keypress. In
      // particular, if we do AutoComplete and leave the text selected, then
      // when the user types the next key, we'll get *three* input events: one
      // for the actual change, and two other events (probably related to
      // Gboard's own AutoComplete behavior). We give the input value a chance
      // to stabilize by waiting a tick.
      setTimeout(() => {
        this[symbols.raiseChangeEvents] = true;
          /** @type {any} */
        const inner = this.inner;
        const text = this.value.toLowerCase();
        // We only AutoComplete if the user's typing at the end of the input.
        // Read the selection start and end directly off the inner element to
        // ensure they're up to date.
        const typingAtEnd = inner.selectionStart === text.length &&
          inner.selectionEnd === text.length;
        // Moreover, we only AutoComplete if we're sure the user's added a
        // single character to the value seen on the previous input event. Among
        // other things, we want to ensure the user can delete text from the end
        // without having AutoComplete kick in.
        const originalText = this.state.originalText;
        const userAddedText = text.startsWith(originalText) &&
          text.length === originalText.length + 1;
        if (typingAtEnd && userAddedText) {
          autoComplete(this);
        }
        // Remember what the user typed for next time.
        this.setState({
          originalText: text
        });
        this[symbols.raiseChangeEvents] = false;
      });
    });
  }

  componentDidUpdate(previousState) {
    if (super.componentDidUpdate) { super.componentDidUpdate(previousState); }

    const { autoCompleteSelect, originalText } = this.state;
    if (autoCompleteSelect) {
      // We've finished rendering new auto-completed text.
      // Leave the auto-completed portion (after the part the user originally
      // typed) selected.
      this.setState({
        autoCompleteSelect: false
      });
      this.setInnerProperty('selectionStart', originalText.length);
      this.setInnerProperty('selectionEnd', this.value.length);

      // Dispatch an input event so that listeners can process the
      // auto-completed text.
      // @ts-ignore
      const InputEvent = window.InputEvent || Event;
      const event = new InputEvent('input', {
        // @ts-ignore
        detail: {
          originalText
        }
      });
      this.dispatchEvent(event);
    }
  }

  get defaultState() {
    return Object.assign(super.defaultState, {
      autoCompleteSelect: false,
      originalText: '',
      texts: []
    });
  }

  get texts() {
    return this.state.texts;
  }
  set texts(texts) {
    this.setState({ texts });
  }

  // Setting the value from the outside is treated as if the user had typed the
  // value. This way the component's value can be prepopulated, and the user can
  // start typing at the end of it to get AutoComplete.
  get value() {
    return super.value;
  }
  set value(value) {
    super.value = value;
    // If the input has focus, we assume the user is typing, and rely on
    // the `input` event to update the originalText state.
    if (!this.inner.matches(':focus')) {
      this.setState({
        originalText: value
      });
    }
  }

}
<commit_msg>Don't bother looking up inner element if we haven't rendered.<commit_after>import * as symbols from './symbols.js';
import Input from './Input.js';


/**
 * A text input box that completes text as the user types
 * 
 * @inherits Input
 */
class AutoCompleteInput extends Input {

  componentDidMount() {
    if (super.componentDidMount) { super.componentDidMount(); }

    // In many ways it would be cleaner to do AutoComplete work in a keydown
    // listener. Unfortunately, Chrome for Android sets the keyCode on *all*
    // keydown events to a mysterious 229 value, making it impossible for us to
    // look at the keyCode and determine whether the user is typing a key that
    // should trigger AutoComplete.
    //
    // Instead, we listen to input events. That comes with its own
    // set of headaches, noted below.
    this.$.inner.addEventListener('input', () => {
      // Gboard will generate multiple input events for a single keypress. In
      // particular, if we do AutoComplete and leave the text selected, then
      // when the user types the next key, we'll get *three* input events: one
      // for the actual change, and two other events (probably related to
      // Gboard's own AutoComplete behavior). We give the input value a chance
      // to stabilize by waiting a tick.
      setTimeout(() => {
        this[symbols.raiseChangeEvents] = true;
          /** @type {any} */
        const inner = this.inner;
        const text = this.value.toLowerCase();
        // We only AutoComplete if the user's typing at the end of the input.
        // Read the selection start and end directly off the inner element to
        // ensure they're up to date.
        const typingAtEnd = inner.selectionStart === text.length &&
          inner.selectionEnd === text.length;
        // Moreover, we only AutoComplete if we're sure the user's added a
        // single character to the value seen on the previous input event. Among
        // other things, we want to ensure the user can delete text from the end
        // without having AutoComplete kick in.
        const originalText = this.state.originalText;
        const userAddedText = text.startsWith(originalText) &&
          text.length === originalText.length + 1;
        if (typingAtEnd && userAddedText) {
          autoComplete(this);
        }
        // Remember what the user typed for next time.
        this.setState({
          originalText: text
        });
        this[symbols.raiseChangeEvents] = false;
      });
    });
  }

  componentDidUpdate(previousState) {
    if (super.componentDidUpdate) { super.componentDidUpdate(previousState); }

    const { autoCompleteSelect, originalText } = this.state;
    if (autoCompleteSelect) {
      // We've finished rendering new auto-completed text.
      // Leave the auto-completed portion (after the part the user originally
      // typed) selected.
      this.setState({
        autoCompleteSelect: false
      });
      this.setInnerProperty('selectionStart', originalText.length);
      this.setInnerProperty('selectionEnd', this.value.length);

      // Dispatch an input event so that listeners can process the
      // auto-completed text.
      // @ts-ignore
      const InputEvent = window.InputEvent || Event;
      const event = new InputEvent('input', {
        // @ts-ignore
        detail: {
          originalText
        }
      });
      this.dispatchEvent(event);
    }
  }

  get defaultState() {
    return Object.assign(super.defaultState, {
      autoCompleteSelect: false,
      originalText: '',
      texts: []
    });
  }

  get texts() {
    return this.state.texts;
  }
  set texts(texts) {
    this.setState({ texts });
  }

  // Setting the value from the outside is treated as if the user had typed the
  // value. This way the component's value can be prepopulated, and the user can
  // start typing at the end of it to get AutoComplete.
  get value() {
    return super.value;
  }
  set value(value) {
    super.value = value;
    // If the input has focus, we assume the user is typing, and rely on
    // the `input` event to update the originalText state.
    if (this.shadowRoot && !this.inner.matches(':focus')) {
      this.setState({
        originalText: value
      });
    }
  }

}

{'fraction_non_alphanumeric': 0.09546258102533883, 'fraction_numerical': 0.0009428403064230996, 'mean_word_length': 2.397117694155324, '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': '7357786', 'n_tokens_mistral': 2246, 'n_tokens_neox': 2201, 'n_words': 866}