   refcount: 0
   fileSubscriptions: null
 
+  @delegatesMethods 'undo', 'redo', 'transact', 'beginTransaction', 'commitTransaction',
+    'abortTransaction', 'isTransacting', 'clearUndoStack', toProperty: 'history'
+
+  ###
+  Section: Construction
+  ###
+
   # Public: Create a new buffer with the given params.
   #
-  # * `params` A {String} of text or an {Object} with the following keys:
+  # * `params` {Object} or {String} of text
   #   * `load` A {Boolean}, `true` to asynchronously load the buffer from disk
   #     after initialization.
   #   * `text` The initial {String} text of the buffer.

  # Returns a {Number}.
  getMarkerCount: -> @markers.getMarkerCount()

  markerCreated: (marker) ->
    @emit 'marker-created', marker
    @emitter.emit 'did-create-marker', marker

        return row unless @isRowBlank(row)
    null

  ###
  Section: Mutating Text
  ###

  # Public: Replace the entire contents of the buffer with the given text.
  #
  # * `text` A {String}
  #
  # Returns a {Range} spanning the new buffer contents.
  setText: (text) ->
    @setTextInRange(@getRange(), text, false)

  # Public: Replace the current buffer contents by applying a diff based on the
  # given text.
  #
  # * `text` A {String} containing the new buffer contents.
  setTextViaDiff: (text) ->
    currentText = @getText()
    return if currentText == text

    endsWithNewline = (str) ->
      /[\r\n]+$/g.test(str)

    computeBufferColumn = (str) ->
      newlineIndex = Math.max(str.lastIndexOf('\n'), str.lastIndexOf('\r'))
      if endsWithNewline(str)
        0
      else if newlineIndex == -1
        str.length
      else
        str.length - newlineIndex - 1

    @transact =>
      row = 0
      column = 0
      currentPosition = [0, 0]

      lineDiff = diff.diffLines(currentText, text)
      changeOptions = normalizeLineEndings: false

      for change in lineDiff
        lineCount = change.value.match(newlineRegex)?.length ? 0
        currentPosition[0] = row
        currentPosition[1] = column

        if change.added
          @setTextInRange([currentPosition, currentPosition], change.value, changeOptions)
          row += lineCount
          column = computeBufferColumn(change.value)

        else if change.removed
          endRow = row + lineCount
          endColumn = column + computeBufferColumn(change.value)
          @setTextInRange([currentPosition, [endRow, endColumn]], '', changeOptions)

        else
          row += lineCount
          column = computeBufferColumn(change.value)

  # Public: Set the text in the given range.
  #
  # * `range` A {Range}
  # * `text` A {String}
  # * `normalizeLineEndings` (optional) {Boolean} (default: true)
  #
  # Returns the {Range} of the inserted text.
  setTextInRange: (range, text, normalizeLineEndings=true) ->
    patch = @buildPatch(range, text, normalizeLineEndings)
    @history?.recordNewPatch(patch)
    @applyPatch(patch)
    patch.newRange

  # Public: Insert text at the given position.
  #
  # * `position` A {Point} representing the insertion location. The position is
  #   clipped before insertion.
  # * `text` A {String} representing the text to insert.
  # * `normalizeLineEndings` (optional) {Boolean} (default: true)\
  #
  # Returns the {Range} of the inserted text.
  insert: (position, text, normalizeLineEndings) ->
    @setTextInRange(new Range(position, position), text, normalizeLineEndings)

  # Public: Append text to the end of the buffer.
  #
  # * `text` A {String} representing the text text to append.
  #
  # Returns the {Range} of the inserted text
  append: (text, normalizeLineEndings) ->
    @insert(@getEndPosition(), text, normalizeLineEndings)

  # Builds a {BufferPatch}, which is used to modify the buffer and is also
  # pushed into the undo history so it can be undone.
  buildPatch: (oldRange, newText, normalizeLineEndings) ->
    oldRange = @clipRange(oldRange)
    oldText = @getTextInRange(oldRange)
    newRange = Range.fromText(oldRange.start, newText)
    patch = new BufferPatch(oldRange, newRange, oldText, newText, normalizeLineEndings)
    @markers?.handleBufferChange(patch)
    patch

  # Applies a {BufferPatch} to the buffer based on its old range and new text.
  # Also applies any {MarkerPatch}es associated with the {BufferPatch}.
  applyPatch: ({oldRange, newRange, oldText, newText, normalizeLineEndings, markerPatches}) ->
    @cachedText = null

    startRow = oldRange.start.row
    endRow = oldRange.end.row
    rowCount = endRow - startRow + 1

    # Determine how to normalize the line endings of inserted text if enabled
    if normalizeLineEndings
      normalizedEnding = @lineEndingForRow(startRow)
      if normalizedEnding is ''
        if startRow > 0
          normalizedEnding = @lineEndingForRow(startRow - 1)
        else
          normalizedEnding = null

    # Split inserted text into lines and line endings
    lines = []
    lineEndings = []
    lineStartIndex = 0
    while result = newlineRegex.exec(newText)
      lines.push(newText[lineStartIndex...result.index])
      lineEndings.push(normalizedEnding ? result[0])
      lineStartIndex = newlineRegex.lastIndex

    lastLine = newText[lineStartIndex..]
    lines.push(lastLine)
    lineEndings.push('')

    # Update first and last line so replacement preserves existing prefix and suffix of oldRange
    prefix = @lineForRow(startRow)[0...oldRange.start.column]
    lines[0] = prefix + lines[0]
    suffix = @lineForRow(endRow)[oldRange.end.column...]
    lastIndex = lines.length - 1
    lines[lastIndex] += suffix
    lastLineEnding = @lineEndingForRow(endRow)
    lastLineEnding = normalizedEnding if lastLineEnding isnt '' and normalizedEnding?
    lineEndings[lastIndex] = lastLineEnding

    # Replace lines in oldRange with new lines
    spliceArray(@lines, startRow, rowCount, lines)
    spliceArray(@lineEndings, startRow, rowCount, lineEndings)

    # Update the offset index for position <-> character offset translation
    offsets = lines.map (line, index) ->
      {rows: 1, characters: line.length + lineEndings[index].length}
    @offsetIndex.spliceArray('rows', startRow, rowCount, offsets)

    @markers?.pauseChangeEvents()
    @markers?.applyPatches(markerPatches, true)

    changeEvent = {oldRange, newRange, oldText, newText}

    @conflict = false if @conflict and !@isModified()
    @scheduleModifiedEvents()
    @emit 'changed', changeEvent
    @emitter.emit 'did-change', changeEvent
    @markers?.resumeChangeEvents()
    @emit 'markers-updated'
    @emitter.emit 'did-update-markers'

  ###
  Section: Removing Text
  ###

  # Public: Delete the text in the given range.
  #
  # * `range` A {Range} in which to delete. The range is clipped before deleting.
  #
  # Returns an empty {Range} starting at the start of deleted range.
  delete: (range) ->
    @setTextInRange(range, '')

  # Public: Delete the line associated with a specified row.
  #
  # * `row` A {Number} representing the 0-indexed row to delete.
  #
  # Returns the {Range} of the deleted text.
  deleteRow: (row) ->
    @deleteRows(row, row)

  # Public: Delete the lines associated with the specified row range.
  #
  # If the row range is out of bounds, it will be clipped. If the startRow is
  # greater than the end row, they will be reordered.
  #
  # * `startRow` A {Number} representing the first row to delete.
  # * `endRow` A {Number} representing the last row to delete, inclusive.
  #
  # Returns the {Range} of the deleted text.
  deleteRows: (startRow, endRow) ->
    lastRow = @getLastRow()

    [startRow, endRow] = [endRow, startRow] if startRow > endRow

    if endRow < 0
      return new Range(@getFirstPosition(), @getFirstPosition())

    if startRow > lastRow
      return new Range(@getEndPosition(), @getEndPosition())

    startRow = Math.max(0, startRow)
    endRow = Math.min(lastRow, endRow)

    if endRow < lastRow
      startPoint = new Point(startRow, 0)
      endPoint = new Point(endRow + 1, 0)
    else
      if startRow is 0
        startPoint = new Point(startRow, 0)
      else
        startPoint = new Point(startRow - 1, @lineLengthForRow(startRow - 1))
      endPoint = new Point(endRow, @lineLengthForRow(endRow))

    @delete(new Range(startPoint, endPoint))

  ###
  Section: Markers
  ###

  # Public: Create a marker with the given range. This marker will maintain
  # its logical location as the buffer is changed, so if you mark a particular
