<commit_before>"use babel"

const {Range} = require("atom")
const Base = require("./base")

class MiscCommand extends Base {
  static operationKind = "misc-command"
}
MiscCommand.register(false)

class Mark extends MiscCommand {
  requireInput = true
  initialize() {
    this.readChar()
    return super.initialize()
  }

  execute() {
    this.vimState.mark.set(this.input, this.editor.getCursorBufferPosition())
    this.activateMode("normal")
  }
}
Mark.register()

class ReverseSelections extends MiscCommand {
  execute() {
    this.swrap.setReversedState(this.editor, !this.editor.getLastSelection().isReversed())
    if (this.isMode("visual", "blockwise")) {
      this.getLastBlockwiseSelection().autoscroll()
    }
  }
}
ReverseSelections.register()

class BlockwiseOtherEnd extends ReverseSelections {
  execute() {
    for (const blockwiseSelection of this.getBlockwiseSelections()) {
      blockwiseSelection.reverse()
    }
    super.execute()
  }
}
BlockwiseOtherEnd.register()

class Undo extends MiscCommand {
  setCursorPosition({newRanges, oldRanges, strategy}) {
    const lastCursor = this.editor.getLastCursor() // This is restored cursor

    const changedRange =
      strategy === "smart"
        ? this.utils.findRangeContainsPoint(newRanges, lastCursor.getBufferPosition())
        : this.utils.sortRanges(newRanges.concat(oldRanges))[0]

    if (changedRange) {
      if (this.utils.isLinewiseRange(changedRange)) this.utils.setBufferRow(lastCursor, changedRange.start.row)
      else lastCursor.setBufferPosition(changedRange.start)
    }
  }

  mutateWithTrackChanges() {
    const newRanges = []
    const oldRanges = []

    // Collect changed range while mutating text-state by fn callback.
    const disposable = this.editor.getBuffer().onDidChange(({newRange, oldRange}) => {
      if (newRange.isEmpty()) {
        oldRanges.push(oldRange) // Remove only
      } else {
        newRanges.push(newRange)
      }
    })

    this.mutate()
    disposable.dispose()
    return {newRanges, oldRanges}
  }

  flashChanges({newRanges, oldRanges}) {
    const isMultipleSingleLineRanges = ranges => ranges.length > 1 && ranges.every(this.utils.isSingleLineRange)

    if (newRanges.length > 0) {
      if (this.isMultipleAndAllRangeHaveSameColumnAndConsecutiveRows(newRanges)) return

      newRanges = newRanges.map(range => this.utils.humanizeBufferRange(this.editor, range))
      newRanges = this.filterNonLeadingWhiteSpaceRange(newRanges)

      const type = isMultipleSingleLineRanges(newRanges) ? "undo-redo-multiple-changes" : "undo-redo"
      this.flash(newRanges, {type})
    } else {
      if (this.isMultipleAndAllRangeHaveSameColumnAndConsecutiveRows(oldRanges)) return

      if (isMultipleSingleLineRanges(oldRanges)) {
        oldRanges = this.filterNonLeadingWhiteSpaceRange(oldRanges)
        this.flash(oldRanges, {type: "undo-redo-multiple-delete"})
      }
    }
  }

  filterNonLeadingWhiteSpaceRange(ranges) {
    return ranges.filter(range => !this.utils.isLeadingWhiteSpaceRange(this.editor, range))
  }

  // [TODO] Improve further by checking oldText, newText?
  // [Purpose of this function]
  // Suppress flash when undo/redoing toggle-comment while flashing undo/redo of occurrence operation.
  // This huristic approach never be perfect.
  // Ultimately cannnot distinguish occurrence operation.
  isMultipleAndAllRangeHaveSameColumnAndConsecutiveRows(ranges) {
    if (ranges.length <= 1) {
      return false
    }

    const {start: {column: startColumn}, end: {column: endColumn}} = ranges[0]
    let previousRow

    for (const range of ranges) {
      const {start, end} = range
      if (start.column !== startColumn || end.column !== endColumn) return false
      if (previousRow != null && previousRow + 1 !== start.row) return false
      previousRow = start.row
    }
    return true
  }

  flash(ranges, options) {
    if (options.timeout == null) options.timeout = 500
    this.onDidFinishOperation(() => this.vimState.flash(ranges, options))
  }

  execute() {
    const {newRanges, oldRanges} = this.mutateWithTrackChanges()

    for (const selection of this.editor.getSelections()) {
      selection.clear()
    }

    if (this.getConfig("setCursorToStartOfChangeOnUndoRedo")) {
      const strategy = this.getConfig("setCursorToStartOfChangeOnUndoRedoStrategy")
      this.setCursorPosition({newRanges, oldRanges, strategy})
      this.vimState.clearSelections()
    }

    if (this.getConfig("flashOnUndoRedo")) this.flashChanges({newRanges, oldRanges})
    this.activateMode("normal")
  }

  mutate() {
    this.editor.undo()
  }
}
Undo.register()

class Redo extends Undo {
  mutate() {
    this.editor.redo()
  }
}
Redo.register()

// zc
class FoldCurrentRow extends MiscCommand {
  execute() {
    for (const selection of this.editor.getSelections()) {
      this.editor.foldBufferRow(this.getCursorPositionForSelection(selection).row)
    }
  }
}
FoldCurrentRow.register()

// zo
class UnfoldCurrentRow extends MiscCommand {
  execute() {
    for (const selection of this.editor.getSelections()) {
      this.editor.unfoldBufferRow(this.getCursorPositionForSelection(selection).row)
    }
  }
}
UnfoldCurrentRow.register()

// za
class ToggleFold extends MiscCommand {
  execute() {
    this.editor.toggleFoldAtBufferRow(this.editor.getCursorBufferPosition().row)
  }
}
ToggleFold.register()

// Base of zC, zO, zA
class FoldCurrentRowRecursivelyBase extends MiscCommand {
  eachFoldStartRow(fn) {
    for (const selection of this.editor.getSelectionsOrderedByBufferPosition().reverse()) {
      const {row} = this.getCursorPositionForSelection(selection)
      if (!this.editor.isFoldableAtBufferRow(row)) continue

      const foldStartRows = this.utils.getFoldRowRangesContainedByFoldStartsAtRow(this.editor, row).map(rowRange => rowRange[0])
      for (const row of foldStartRows.reverse()) {
        fn(row)
      }
    }
  }

  foldRecursively() {
    this.eachFoldStartRow(row => {
      if (!this.editor.isFoldedAtBufferRow(row)) this.editor.foldBufferRow(row)
    })
  }<commit_msg>prettier<commit_after>"use babel"

const {Range} = require("atom")
const Base = require("./base")

class MiscCommand extends Base {
  static operationKind = "misc-command"
}
MiscCommand.register(false)

class Mark extends MiscCommand {
  requireInput = true
  initialize() {
    this.readChar()
    return super.initialize()
  }

  execute() {
    this.vimState.mark.set(this.input, this.editor.getCursorBufferPosition())
    this.activateMode("normal")
  }
}
Mark.register()

class ReverseSelections extends MiscCommand {
  execute() {
    this.swrap.setReversedState(this.editor, !this.editor.getLastSelection().isReversed())
    if (this.isMode("visual", "blockwise")) {
      this.getLastBlockwiseSelection().autoscroll()
    }
  }
}
ReverseSelections.register()

class BlockwiseOtherEnd extends ReverseSelections {
  execute() {
    for (const blockwiseSelection of this.getBlockwiseSelections()) {
      blockwiseSelection.reverse()
    }
    super.execute()
  }
}
BlockwiseOtherEnd.register()

class Undo extends MiscCommand {
  setCursorPosition({newRanges, oldRanges, strategy}) {
    const lastCursor = this.editor.getLastCursor() // This is restored cursor

    const changedRange =
      strategy === "smart"
        ? this.utils.findRangeContainsPoint(newRanges, lastCursor.getBufferPosition())
        : this.utils.sortRanges(newRanges.concat(oldRanges))[0]

    if (changedRange) {
      if (this.utils.isLinewiseRange(changedRange)) this.utils.setBufferRow(lastCursor, changedRange.start.row)
      else lastCursor.setBufferPosition(changedRange.start)
    }
  }

  mutateWithTrackChanges() {
    const newRanges = []
    const oldRanges = []

    // Collect changed range while mutating text-state by fn callback.
    const disposable = this.editor.getBuffer().onDidChange(({newRange, oldRange}) => {
      if (newRange.isEmpty()) {
        oldRanges.push(oldRange) // Remove only
      } else {
        newRanges.push(newRange)
      }
    })

    this.mutate()
    disposable.dispose()
    return {newRanges, oldRanges}
  }

  flashChanges({newRanges, oldRanges}) {
    const isMultipleSingleLineRanges = ranges => ranges.length > 1 && ranges.every(this.utils.isSingleLineRange)

    if (newRanges.length > 0) {
      if (this.isMultipleAndAllRangeHaveSameColumnAndConsecutiveRows(newRanges)) return

      newRanges = newRanges.map(range => this.utils.humanizeBufferRange(this.editor, range))
      newRanges = this.filterNonLeadingWhiteSpaceRange(newRanges)

      const type = isMultipleSingleLineRanges(newRanges) ? "undo-redo-multiple-changes" : "undo-redo"
      this.flash(newRanges, {type})
    } else {
      if (this.isMultipleAndAllRangeHaveSameColumnAndConsecutiveRows(oldRanges)) return

      if (isMultipleSingleLineRanges(oldRanges)) {
        oldRanges = this.filterNonLeadingWhiteSpaceRange(oldRanges)
        this.flash(oldRanges, {type: "undo-redo-multiple-delete"})
      }
    }
  }

  filterNonLeadingWhiteSpaceRange(ranges) {
    return ranges.filter(range => !this.utils.isLeadingWhiteSpaceRange(this.editor, range))
  }

  // [TODO] Improve further by checking oldText, newText?
  // [Purpose of this function]
  // Suppress flash when undo/redoing toggle-comment while flashing undo/redo of occurrence operation.
  // This huristic approach never be perfect.
  // Ultimately cannnot distinguish occurrence operation.
  isMultipleAndAllRangeHaveSameColumnAndConsecutiveRows(ranges) {
    if (ranges.length <= 1) {
      return false
    }

    const {start: {column: startColumn}, end: {column: endColumn}} = ranges[0]
    let previousRow

    for (const range of ranges) {
      const {start, end} = range
      if (start.column !== startColumn || end.column !== endColumn) return false
      if (previousRow != null && previousRow + 1 !== start.row) return false
      previousRow = start.row
    }
    return true
  }

  flash(ranges, options) {
    if (options.timeout == null) options.timeout = 500
    this.onDidFinishOperation(() => this.vimState.flash(ranges, options))
  }

  execute() {
    const {newRanges, oldRanges} = this.mutateWithTrackChanges()

    for (const selection of this.editor.getSelections()) {
      selection.clear()
    }

    if (this.getConfig("setCursorToStartOfChangeOnUndoRedo")) {
      const strategy = this.getConfig("setCursorToStartOfChangeOnUndoRedoStrategy")
      this.setCursorPosition({newRanges, oldRanges, strategy})
      this.vimState.clearSelections()
    }

    if (this.getConfig("flashOnUndoRedo")) this.flashChanges({newRanges, oldRanges})
    this.activateMode("normal")
  }

  mutate() {
    this.editor.undo()
  }
}
Undo.register()

class Redo extends Undo {
  mutate() {
    this.editor.redo()
  }
}
Redo.register()

// zc
class FoldCurrentRow extends MiscCommand {
  execute() {
    for (const selection of this.editor.getSelections()) {
      this.editor.foldBufferRow(this.getCursorPositionForSelection(selection).row)
    }
  }
}
FoldCurrentRow.register()

// zo
class UnfoldCurrentRow extends MiscCommand {
  execute() {
    for (const selection of this.editor.getSelections()) {
      this.editor.unfoldBufferRow(this.getCursorPositionForSelection(selection).row)
    }
  }
}
UnfoldCurrentRow.register()

// za
class ToggleFold extends MiscCommand {
  execute() {
    this.editor.toggleFoldAtBufferRow(this.editor.getCursorBufferPosition().row)
  }
}
ToggleFold.register()

// Base of zC, zO, zA
class FoldCurrentRowRecursivelyBase extends MiscCommand {
  eachFoldStartRow(fn) {
    for (const selection of this.editor.getSelectionsOrderedByBufferPosition().reverse()) {
      const {row} = this.getCursorPositionForSelection(selection)
      if (!this.editor.isFoldableAtBufferRow(row)) continue

      const foldStartRows = this.utils
        .getFoldRowRangesContainedByFoldStartsAtRow(this.editor, row)
        .map(rowRange => rowRange[0])
      for (const row of foldStartRows.reverse()) {
        fn(row)
      }
    }
  }

  foldRecursively() {
    this.eachFoldStartRow(row => {
      if (!this.editor.isFoldedAtBufferRow(row)) this.editor.foldBufferRow(row)
    })
  }
{'fraction_non_alphanumeric': 0.11561549214702738, 'fraction_numerical': 0.0016446015952635473, 'mean_word_length': 3.7712828560219696, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 21, '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': '5068398', 'n_tokens_mistral': 3862, 'n_tokens_neox': 3733, 'n_words': 704}