<commit_before>Patch = require 'atom-patch'
DisplayIndex = require 'display-index'
{Emitter} = require 'event-kit'
Point = require './point'
Range = require './range'
DisplayMarkerLayer = require './display-marker-layer'
EmptyDecorationLayer = require './empty-decoration-layer'
{traverse, traversal, clipNegativePoint} = pointHelpers = require './point-helpers'
comparePoints = pointHelpers.compare
maxPoint = pointHelpers.max
{normalizePatchChanges} = require './helpers'

module.exports =
class DisplayLayer
  constructor: (@buffer, settings={}) ->
    @displayMarkerLayersById = {}
    @textDecorationLayer = null
    @foldsMarkerLayer = settings.foldsMarkerLayer ? @buffer.addMarkerLayer({maintainHistory: true})
    @foldIdCounter = 1
    @disposables = @buffer.onDidChange(@bufferDidChange.bind(this))
    @screenLineIndex = new DisplayIndex
    @spatialTokenIterator = @screenLineIndex.buildTokenIterator()
    @screenLineIterator = @screenLineIndex.buildScreenLineIterator()
    @textDecorationLayer = new EmptyDecorationLayer
    @emitter = new Emitter
    @invalidationCountsByScreenLineId = new Map
    @screenLinesBySpatialLineId = new Map
    @reset({
      invisibles: settings.invisibles ? {}
      tabLength: settings.tabLength ? 4
      softWrapColumn: settings.softWrapColumn ? Infinity
      softWrapHangingIndent: settings.softWrapHangingIndent ? 0
      showIndentGuides: settings.showIndentGuides ? false,
      ratioForCharacter: settings.ratioForCharacter ? -> 1.0,
    })

  destroy: ->
    @disposables.dispose()
    @foldsMarkerLayer.destroy()
    for id, displayMarkerLayer of @displayMarkerLayersById
      displayMarkerLayer.destroy()

  reset: ({tabLength, invisibles, showIndentGuides, softWrapColumn, softWrapHangingIndent, ratioForCharacter}) ->
    @tabLength = tabLength
    @showIndentGuides = showIndentGuides
    @softWrapColumn = softWrapColumn
    @softWrapHangingIndent = softWrapHangingIndent
    @ratioForCharacter = ratioForCharacter
    @invisibles = invisibles
    @eolInvisibles = {
      "\r": @invisibles.cr
      "\n": @invisibles.eol
      "\r\n": @invisibles.cr + @invisibles.eol
    }

    {startScreenRow, endScreenRow} = @expandBufferRangeToLineBoundaries(Range(Point.ZERO, Point(@buffer.getLineCount(), 0)))
    newLines = @buildSpatialTokenLines(0, @buffer.getLineCount())
    oldRowExtent = endScreenRow - startScreenRow + 1
    newRowExtent = newLines.length
    @spliceDisplayIndex(startScreenRow, oldRowExtent, newLines)
    @emitter.emit 'did-change-sync', Object.freeze([{
      start: Point(startScreenRow, 0),
      oldExtent: Point(oldRowExtent, 0),
      newExtent: Point(newRowExtent, 0)
    }])

  addMarkerLayer: (options) ->
    markerLayer = new DisplayMarkerLayer(this, @buffer.addMarkerLayer(options))
    @displayMarkerLayersById[markerLayer.id] = markerLayer

  getMarkerLayer: (id) ->
    if bufferMarkerLayer = @buffer.getMarkerLayer(id)
      @displayMarkerLayersById[id] ?= new DisplayMarkerLayer(this, bufferMarkerLayer)

  setTextDecorationLayer: (layer) ->
    @decorationLayerDisposable?.dispose()
    @textDecorationLayer = layer
    @decorationLayerDisposable = layer.onDidInvalidateRange?(@decorationLayerDidInvalidateRange.bind(this))

  bufferRangeForFold: (id) ->
    @foldsMarkerLayer.getMarkerRange(id)

  foldBufferRange: (bufferRange) ->
    bufferRange = @buffer.clipRange(bufferRange)
    foldId = @foldsMarkerLayer.markRange(bufferRange).id
    if @foldsMarkerLayer.findMarkers(containsRange: bufferRange).length is 1
      {startScreenRow, endScreenRow, startBufferRow, endBufferRow} = @expandBufferRangeToLineBoundaries(bufferRange)
      oldRowExtent = endScreenRow - startScreenRow + 1
      newScreenLines = @buildSpatialTokenLines(startBufferRow, endBufferRow)
      newRowExtent = newScreenLines.length
      @spliceDisplayIndex(startScreenRow, oldRowExtent, newScreenLines)
      @emitter.emit 'did-change-sync', Object.freeze([{
        start: Point(startScreenRow, 0),
        oldExtent: Point(oldRowExtent, 0),
        newExtent: Point(newRowExtent, 0)
      }])

    foldId

  foldsIntersectingBufferRange: (bufferRange) ->
    @foldsMarkerLayer.findMarkers(intersectsRange: bufferRange).map ({id}) -> id

  outermostFoldsInBufferRowRange: (startRow, endRow) ->
    folds = []
    lastFoldEndRow = -1

    for marker in @foldsMarkerLayer.findMarkers(intersectsRowRange: [startRow, endRow])
      range = marker.getRange()
      if range.start.row > lastFoldEndRow
        lastFoldEndRow = range.end.row
        if startRow <= range.start.row <= range.end.row < endRow
          folds.push(marker.id)

    folds

  destroyFold: (foldId) ->
    if foldMarker = @foldsMarkerLayer.getMarker(foldId)
      @destroyFoldMarkers([foldMarker])

  destroyFoldsIntersectingBufferRange: (bufferRange) ->
    bufferRange = @buffer.clipRange(bufferRange)
    @destroyFoldMarkers(@foldsMarkerLayer.findMarkers(intersectsRange: bufferRange))

  destroyAllFolds: ->
    @destroyFoldMarkers(@foldsMarkerLayer.getMarkers())

  destroyFoldMarkers: (foldMarkers) ->
    if foldMarkers.length > 0
      combinedRangeStart = combinedRangeEnd = foldMarkers[0].getStartPosition()<commit_msg>More fold APIs<commit_after>Patch = require 'atom-patch'
DisplayIndex = require 'display-index'
{Emitter} = require 'event-kit'
Point = require './point'
Range = require './range'
DisplayMarkerLayer = require './display-marker-layer'
EmptyDecorationLayer = require './empty-decoration-layer'
{traverse, traversal, clipNegativePoint} = pointHelpers = require './point-helpers'
comparePoints = pointHelpers.compare
maxPoint = pointHelpers.max
{normalizePatchChanges} = require './helpers'

module.exports =
class DisplayLayer
  constructor: (@buffer, settings={}) ->
    @displayMarkerLayersById = {}
    @textDecorationLayer = null
    @foldsMarkerLayer = settings.foldsMarkerLayer ? @buffer.addMarkerLayer({maintainHistory: true})
    @foldIdCounter = 1
    @disposables = @buffer.onDidChange(@bufferDidChange.bind(this))
    @screenLineIndex = new DisplayIndex
    @spatialTokenIterator = @screenLineIndex.buildTokenIterator()
    @screenLineIterator = @screenLineIndex.buildScreenLineIterator()
    @textDecorationLayer = new EmptyDecorationLayer
    @emitter = new Emitter
    @invalidationCountsByScreenLineId = new Map
    @screenLinesBySpatialLineId = new Map
    @reset({
      invisibles: settings.invisibles ? {}
      tabLength: settings.tabLength ? 4
      softWrapColumn: settings.softWrapColumn ? Infinity
      softWrapHangingIndent: settings.softWrapHangingIndent ? 0
      showIndentGuides: settings.showIndentGuides ? false,
      ratioForCharacter: settings.ratioForCharacter ? -> 1.0,
    })

  destroy: ->
    @disposables.dispose()
    @foldsMarkerLayer.destroy()
    for id, displayMarkerLayer of @displayMarkerLayersById
      displayMarkerLayer.destroy()

  reset: ({tabLength, invisibles, showIndentGuides, softWrapColumn, softWrapHangingIndent, ratioForCharacter}) ->
    @tabLength = tabLength
    @showIndentGuides = showIndentGuides
    @softWrapColumn = softWrapColumn
    @softWrapHangingIndent = softWrapHangingIndent
    @ratioForCharacter = ratioForCharacter
    @invisibles = invisibles
    @eolInvisibles = {
      "\r": @invisibles.cr
      "\n": @invisibles.eol
      "\r\n": @invisibles.cr + @invisibles.eol
    }

    {startScreenRow, endScreenRow} = @expandBufferRangeToLineBoundaries(Range(Point.ZERO, Point(@buffer.getLineCount(), 0)))
    newLines = @buildSpatialTokenLines(0, @buffer.getLineCount())
    oldRowExtent = endScreenRow - startScreenRow + 1
    newRowExtent = newLines.length
    @spliceDisplayIndex(startScreenRow, oldRowExtent, newLines)
    @emitter.emit 'did-change-sync', Object.freeze([{
      start: Point(startScreenRow, 0),
      oldExtent: Point(oldRowExtent, 0),
      newExtent: Point(newRowExtent, 0)
    }])

  addMarkerLayer: (options) ->
    markerLayer = new DisplayMarkerLayer(this, @buffer.addMarkerLayer(options))
    @displayMarkerLayersById[markerLayer.id] = markerLayer

  getMarkerLayer: (id) ->
    if bufferMarkerLayer = @buffer.getMarkerLayer(id)
      @displayMarkerLayersById[id] ?= new DisplayMarkerLayer(this, bufferMarkerLayer)

  setTextDecorationLayer: (layer) ->
    @decorationLayerDisposable?.dispose()
    @textDecorationLayer = layer
    @decorationLayerDisposable = layer.onDidInvalidateRange?(@decorationLayerDidInvalidateRange.bind(this))

  bufferRangeForFold: (id) ->
    @foldsMarkerLayer.getMarkerRange(id)

  foldBufferRange: (bufferRange) ->
    bufferRange = @buffer.clipRange(bufferRange)
    foldId = @foldsMarkerLayer.markRange(bufferRange).id
    if @foldsMarkerLayer.findMarkers(containsRange: bufferRange).length is 1
      {startScreenRow, endScreenRow, startBufferRow, endBufferRow} = @expandBufferRangeToLineBoundaries(bufferRange)
      oldRowExtent = endScreenRow - startScreenRow + 1
      newScreenLines = @buildSpatialTokenLines(startBufferRow, endBufferRow)
      newRowExtent = newScreenLines.length
      @spliceDisplayIndex(startScreenRow, oldRowExtent, newScreenLines)
      @emitter.emit 'did-change-sync', Object.freeze([{
        start: Point(startScreenRow, 0),
        oldExtent: Point(oldRowExtent, 0),
        newExtent: Point(newRowExtent, 0)
      }])

    foldId

  foldsIntersectingBufferRange: (bufferRange) ->
    @foldsMarkerLayer.findMarkers(intersectsRange: bufferRange).map ({id}) -> id

  foldsStartingAtBufferRow: (bufferRow) ->
    @foldsMarkerLayer.findMarkers(startRow: bufferRow).map ({id}) -> id

  outermostFoldsInBufferRowRange: (startBufferRow, endBufferRow) ->
    folds = []
    lastFoldEndRow = -1

    for marker in @foldsMarkerLayer.findMarkers(intersectsRowRange: [startBufferRow, endBufferRow])
      range = marker.getRange()
      if range.start.row > lastFoldEndRow
        lastFoldEndRow = range.end.row
        if startBufferRow <= range.start.row <= range.end.row < endBufferRow
          folds.push(marker.id)

    folds

  largestFoldStartingAtBufferRow: (row) ->
    @foldsStartingAtBufferRow(row)[0]

  largestFoldContainingBufferRow: (row) ->
    @foldsIntersectingBufferRange([[row, 0], [row, Infinity]])[0]

  destroyFold: (foldId) ->
    if foldMarker = @foldsMarkerLayer.getMarker(foldId)
      @destroyFoldMarkers([foldMarker])

  destroyFoldsIntersectingBufferRange: (bufferRange) ->
    bufferRange = @buffer.clipRange(bufferRange)
    @destroyFoldMarkers(@foldsMarkerLayer.findMarkers(intersectsRange: bufferRange))

  destroyAllFolds: ->
    @destroyFoldMarkers(@foldsMarkerLayer.getMarkers())

  destroyFoldMarkers: (foldMarkers) ->
    if foldMarkers.length > 0
      combinedRangeStart = combinedRangeEnd = foldMarkers[0].getStartPosition()
{'fraction_non_alphanumeric': 0.10599121413216188, 'fraction_numerical': 0.0038321338442845127, 'mean_word_length': 4.546915500259201, 'pattern_counts': {'":': 6, '<': 9, '<?xml version=': 0, '>': 43, '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': '85711', 'n_tokens_mistral': 3342, 'n_tokens_neox': 3242, 'n_words': 566}