<commit_before>{Emitter} = require 'event-kit'
{View, $, callRemoveHooks} = require 'space-pen'
Path = require 'path'
{defaults} = require 'underscore-plus'
TextBuffer = require 'text-buffer'
Grim = require 'grim'
TextEditor = require './text-editor'
TextEditorComponent = require './text-editor-component'
TextEditorView = null

ShadowStyleSheet = null

class TextEditorElement extends HTMLElement
  model: null
  componentDescriptor: null
  component: null
  attached: false
  tileSize: null
  focusOnAttach: false
  hasTiledRendering: true

  createdCallback: ->
    @emitter = new Emitter
    @initializeContent()
    @createSpacePenShim() if Grim.includeDeprecatedAPIs
    @addEventListener 'focus', @focused.bind(this)
    @addEventListener 'blur', @blurred.bind(this)

  initializeContent: (attributes) ->
    @classList.add('editor')
    @setAttribute('tabindex', -1)

    if atom.config.get('editor.useShadowDOM')
      @useShadowDOM = true

      unless ShadowStyleSheet?
        ShadowStyleSheet = document.createElement('style')
        ShadowStyleSheet.textContent = atom.themes.loadLessStylesheet(require.resolve('../static/text-editor-shadow.less'))

      @createShadowRoot()

      @shadowRoot.appendChild(ShadowStyleSheet.cloneNode(true))
      @stylesElement = document.createElement('atom-styles')
      @stylesElement.setAttribute('context', 'atom-text-editor')
      @stylesElement.initialize()

      @rootElement = document.createElement('div')
      @rootElement.classList.add('editor--private')

      @shadowRoot.appendChild(@stylesElement)
      @shadowRoot.appendChild(@rootElement)
    else
      @useShadowDOM = false

      @classList.add('editor', 'editor-colors')
      @stylesElement = document.head.querySelector('atom-styles')
      @rootElement = this

  createSpacePenShim: ->
    TextEditorView ?= require './text-editor-view'
    @__spacePenView = new TextEditorView(this)

  attachedCallback: ->
    @buildModel() unless @getModel()?
    atom.assert(@model.isAlive(), "Attaching a view for a destroyed editor")
    @mountComponent() unless @component?
    @component.checkForVisibilityChange()
    if this is document.activeElement
      @focused()
    @emitter.emit("did-attach")

  detachedCallback: ->
    @unmountComponent()
    @emitter.emit("did-detach")

  initialize: (model) ->
    @setModel(model)
    this

  setModel: (model) ->
    throw new Error("Model already assigned on TextEditorElement") if @model?
    return if model.isDestroyed()

    @model = model
    @mountComponent()
    @addGrammarScopeAttribute()
    @addMiniAttribute() if @model.isMini()
    @addEncodingAttribute()
    @model.onDidChangeGrammar => @addGrammarScopeAttribute()
    @model.onDidChangeEncoding => @addEncodingAttribute()
    @model.onDidDestroy => @unmountComponent()
    @model.onDidChangeMini (mini) => if mini then @addMiniAttribute() else @removeMiniAttribute()
    @__spacePenView.setModel(@model) if Grim.includeDeprecatedAPIs
    @model

  getModel: ->
    @model ? @buildModel()

  buildModel: ->
    @setModel(new TextEditor(
      buffer: new TextBuffer(@textContent)
      softWrapped: false
      tabLength: 2
      softTabs: true
      mini: @hasAttribute('mini')
      lineNumberGutterVisible: not @hasAttribute('gutter-hidden')
      placeholderText: @getAttribute('placeholder-text')
    ))

  mountComponent: ->
    @component = new TextEditorComponent(
      hostElement: this
      rootElement: @rootElement
      stylesElement: @stylesElement
      editor: @model
      tileSize: @tileSize
      useShadowDOM: @useShadowDOM
    )
    @rootElement.appendChild(@component.getDomNode())

    if @useShadowDOM
      @shadowRoot.addEventListener('blur', @shadowRootBlurred.bind(this), true)
    else
      inputNode = @component.hiddenInputComponent.getDomNode()
      inputNode.addEventListener 'focus', @focused.bind(this)
      inputNode.addEventListener 'blur', => @dispatchEvent(new FocusEvent('blur', bubbles: false))

  unmountComponent: ->
    callRemoveHooks(this)
    if @component?
      @component.destroy()
      @component.getDomNode().remove()
      @component = null

  focused: ->
    @component?.focused()

  blurred: (event) ->
    unless @useShadowDOM
      if event.relatedTarget is @component.hiddenInputComponent.getDomNode()
        event.stopImmediatePropagation()
        return

    @component?.blurred()

  # Work around what seems to be a bug in Chromium. Focus can be stolen from the
  # hidden input when clicking on the gutter and transferred to the
  # already-focused host element. The host element never gets a 'focus' event
  # however, which leaves us in a limbo state where the text editor element is
  # focused but the hidden input isn't focused. This always refocuses the hidden
  # input if a blur event occurs in the shadow DOM that is transferring focus
  # back to the host element.
  shadowRootBlurred: (event) ->
    @component.focused() if event.relatedTarget is this

  addGrammarScopeAttribute: ->
    @dataset.grammar = @model.getGrammar()?.scopeName?.replace(/\./g, ' ')

  addMiniAttribute: ->
    @setAttributeNode(document.createAttribute("mini"))

  removeMiniAttribute: ->
    @removeAttribute("mini")

  addEncodingAttribute: ->
    @dataset.encoding = @model.getEncoding()

  hasFocus: ->
    this is document.activeElement or @contains(document.activeElement)

  setUpdatedSynchronously: (@updatedSynchronously) -> @updatedSynchronously

  isUpdatedSynchronously: -> @updatedSynchronously

  # Extended: Continuously reflows lines and line numbers. (Has performance overhead)
  #
  # `continousReflow` A {Boolean} indicating whether to keep reflowing or not.
  setContinuousReflow: (continuousReflow) ->
    @component?.setContinuousReflow(continuousReflow)

  # Extended: get the width of a character of text displayed in this element.
  #
  # Returns a {Number} of pixels.
  getDefaultCharacterWidth: ->
    @getModel().getDefaultCharWidth()

  # Extended: Converts a buffer position to a pixel position.
  #
  # * `bufferPosition` An object that represents a buffer position. It can be either
  #   an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
  #
  # Returns an {Object} with two values: `top` and `left`, representing the pixel position.
  pixelPositionForBufferPosition: (bufferPosition) ->
    @getModel().pixelPositionForBufferPosition(bufferPosition, true)

  # Extended: Converts a screen position to a pixel position.
  #
  # * `screenPosition` An object that represents a screen position. It can be either
  #   an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
  #
  # Returns an {Object} with two values: `top` and `left`, representing the pixel positions.
  pixelPositionForScreenPosition: (screenPosition) ->
    @getModel().pixelPositionForScreenPosition(screenPosition, true)
<commit_msg>:memo: Fix typo<commit_after>{Emitter} = require 'event-kit'
{View, $, callRemoveHooks} = require 'space-pen'
Path = require 'path'
{defaults} = require 'underscore-plus'
TextBuffer = require 'text-buffer'
Grim = require 'grim'
TextEditor = require './text-editor'
TextEditorComponent = require './text-editor-component'
TextEditorView = null

ShadowStyleSheet = null

class TextEditorElement extends HTMLElement
  model: null
  componentDescriptor: null
  component: null
  attached: false
  tileSize: null
  focusOnAttach: false
  hasTiledRendering: true

  createdCallback: ->
    @emitter = new Emitter
    @initializeContent()
    @createSpacePenShim() if Grim.includeDeprecatedAPIs
    @addEventListener 'focus', @focused.bind(this)
    @addEventListener 'blur', @blurred.bind(this)

  initializeContent: (attributes) ->
    @classList.add('editor')
    @setAttribute('tabindex', -1)

    if atom.config.get('editor.useShadowDOM')
      @useShadowDOM = true

      unless ShadowStyleSheet?
        ShadowStyleSheet = document.createElement('style')
        ShadowStyleSheet.textContent = atom.themes.loadLessStylesheet(require.resolve('../static/text-editor-shadow.less'))

      @createShadowRoot()

      @shadowRoot.appendChild(ShadowStyleSheet.cloneNode(true))
      @stylesElement = document.createElement('atom-styles')
      @stylesElement.setAttribute('context', 'atom-text-editor')
      @stylesElement.initialize()

      @rootElement = document.createElement('div')
      @rootElement.classList.add('editor--private')

      @shadowRoot.appendChild(@stylesElement)
      @shadowRoot.appendChild(@rootElement)
    else
      @useShadowDOM = false

      @classList.add('editor', 'editor-colors')
      @stylesElement = document.head.querySelector('atom-styles')
      @rootElement = this

  createSpacePenShim: ->
    TextEditorView ?= require './text-editor-view'
    @__spacePenView = new TextEditorView(this)

  attachedCallback: ->
    @buildModel() unless @getModel()?
    atom.assert(@model.isAlive(), "Attaching a view for a destroyed editor")
    @mountComponent() unless @component?
    @component.checkForVisibilityChange()
    if this is document.activeElement
      @focused()
    @emitter.emit("did-attach")

  detachedCallback: ->
    @unmountComponent()
    @emitter.emit("did-detach")

  initialize: (model) ->
    @setModel(model)
    this

  setModel: (model) ->
    throw new Error("Model already assigned on TextEditorElement") if @model?
    return if model.isDestroyed()

    @model = model
    @mountComponent()
    @addGrammarScopeAttribute()
    @addMiniAttribute() if @model.isMini()
    @addEncodingAttribute()
    @model.onDidChangeGrammar => @addGrammarScopeAttribute()
    @model.onDidChangeEncoding => @addEncodingAttribute()
    @model.onDidDestroy => @unmountComponent()
    @model.onDidChangeMini (mini) => if mini then @addMiniAttribute() else @removeMiniAttribute()
    @__spacePenView.setModel(@model) if Grim.includeDeprecatedAPIs
    @model

  getModel: ->
    @model ? @buildModel()

  buildModel: ->
    @setModel(new TextEditor(
      buffer: new TextBuffer(@textContent)
      softWrapped: false
      tabLength: 2
      softTabs: true
      mini: @hasAttribute('mini')
      lineNumberGutterVisible: not @hasAttribute('gutter-hidden')
      placeholderText: @getAttribute('placeholder-text')
    ))

  mountComponent: ->
    @component = new TextEditorComponent(
      hostElement: this
      rootElement: @rootElement
      stylesElement: @stylesElement
      editor: @model
      tileSize: @tileSize
      useShadowDOM: @useShadowDOM
    )
    @rootElement.appendChild(@component.getDomNode())

    if @useShadowDOM
      @shadowRoot.addEventListener('blur', @shadowRootBlurred.bind(this), true)
    else
      inputNode = @component.hiddenInputComponent.getDomNode()
      inputNode.addEventListener 'focus', @focused.bind(this)
      inputNode.addEventListener 'blur', => @dispatchEvent(new FocusEvent('blur', bubbles: false))

  unmountComponent: ->
    callRemoveHooks(this)
    if @component?
      @component.destroy()
      @component.getDomNode().remove()
      @component = null

  focused: ->
    @component?.focused()

  blurred: (event) ->
    unless @useShadowDOM
      if event.relatedTarget is @component.hiddenInputComponent.getDomNode()
        event.stopImmediatePropagation()
        return

    @component?.blurred()

  # Work around what seems to be a bug in Chromium. Focus can be stolen from the
  # hidden input when clicking on the gutter and transferred to the
  # already-focused host element. The host element never gets a 'focus' event
  # however, which leaves us in a limbo state where the text editor element is
  # focused but the hidden input isn't focused. This always refocuses the hidden
  # input if a blur event occurs in the shadow DOM that is transferring focus
  # back to the host element.
  shadowRootBlurred: (event) ->
    @component.focused() if event.relatedTarget is this

  addGrammarScopeAttribute: ->
    @dataset.grammar = @model.getGrammar()?.scopeName?.replace(/\./g, ' ')

  addMiniAttribute: ->
    @setAttributeNode(document.createAttribute("mini"))

  removeMiniAttribute: ->
    @removeAttribute("mini")

  addEncodingAttribute: ->
    @dataset.encoding = @model.getEncoding()

  hasFocus: ->
    this is document.activeElement or @contains(document.activeElement)

  setUpdatedSynchronously: (@updatedSynchronously) -> @updatedSynchronously

  isUpdatedSynchronously: -> @updatedSynchronously

  # Extended: Continuously reflows lines and line numbers. (Has performance overhead)
  #
  # `continuousReflow` A {Boolean} indicating whether to keep reflowing or not.
  setContinuousReflow: (continuousReflow) ->
    @component?.setContinuousReflow(continuousReflow)

  # Extended: get the width of a character of text displayed in this element.
  #
  # Returns a {Number} of pixels.
  getDefaultCharacterWidth: ->
    @getModel().getDefaultCharWidth()

  # Extended: Converts a buffer position to a pixel position.
  #
  # * `bufferPosition` An object that represents a buffer position. It can be either
  #   an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
  #
  # Returns an {Object} with two values: `top` and `left`, representing the pixel position.
  pixelPositionForBufferPosition: (bufferPosition) ->
    @getModel().pixelPositionForBufferPosition(bufferPosition, true)

  # Extended: Converts a screen position to a pixel position.
  #
  # * `screenPosition` An object that represents a screen position. It can be either
  #   an {Object} (`{row, column}`), {Array} (`[row, column]`), or {Point}
  #
  # Returns an {Object} with two values: `top` and `left`, representing the pixel positions.
  pixelPositionForScreenPosition: (screenPosition) ->
    @getModel().pixelPositionForScreenPosition(screenPosition, true)

{'fraction_non_alphanumeric': 0.11247905587528229, 'fraction_numerical': 0.00029139651781161214, 'mean_word_length': 3.9865601162368325, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 63, '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': '4015916', 'n_tokens_mistral': 3966, 'n_tokens_neox': 3846, 'n_words': 998}