(function() {
  var BufferOffsetIndex, CompositeDisposable, DisplayLayer, Disposable, Emitter, File, History, MarkerLayer, MatchIterator, Patch, Point, Range, TextBuffer, TransactionAbortedError, _, crypto, diff, fs, newlineRegex, normalizePatchChanges, path, ref, ref1, ref2, regexIsSingleLine, spliceArray,
    extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
    hasProp = {}.hasOwnProperty;

  ref = require('event-kit'), Emitter = ref.Emitter, CompositeDisposable = ref.CompositeDisposable, Disposable = ref.Disposable;

  File = require('pathwatcher').File;

  diff = require('diff');

  _ = require('underscore-plus');

  fs = require('fs-plus');

  path = require('path');

  crypto = require('crypto');

  ref1 = require('superstring'), BufferOffsetIndex = ref1.BufferOffsetIndex, Patch = ref1.Patch;

  Point = require('./point');

  Range = require('./range');

  History = require('./history');

  MarkerLayer = require('./marker-layer');

  MatchIterator = require('./match-iterator');

  DisplayLayer = require('./display-layer');

  ref2 = require('./helpers'), spliceArray = ref2.spliceArray, newlineRegex = ref2.newlineRegex, normalizePatchChanges = ref2.normalizePatchChanges, regexIsSingleLine = ref2.regexIsSingleLine;

  TransactionAbortedError = (function(superClass) {
    extend(TransactionAbortedError, superClass);

    function TransactionAbortedError() {
      TransactionAbortedError.__super__.constructor.apply(this, arguments);
    }

    return TransactionAbortedError;

  })(Error);

  module.exports = TextBuffer = (function() {
    TextBuffer.version = 5;

    TextBuffer.Point = Point;

    TextBuffer.Range = Range;

    TextBuffer.newlineRegex = newlineRegex;

    TextBuffer.prototype.cachedText = null;

    TextBuffer.prototype.encoding = null;

    TextBuffer.prototype.stoppedChangingDelay = 300;

    TextBuffer.prototype.stoppedChangingTimeout = null;

    TextBuffer.prototype.cachedDiskContents = null;

    TextBuffer.prototype.conflict = false;

    TextBuffer.prototype.file = null;

    TextBuffer.prototype.refcount = 0;

    TextBuffer.prototype.fileSubscriptions = null;

    TextBuffer.prototype.backwardsScanChunkSize = 8000;

    TextBuffer.prototype.defaultMaxUndoEntries = 10000;

    TextBuffer.prototype.changeCount = 0;

    TextBuffer.prototype.nextMarkerLayerId = 0;


    /*
    Section: Construction
     */

    function TextBuffer(params) {
      var maxUndoEntries, ref10, ref11, ref12, ref3, ref4, ref5, ref6, ref7, ref8, ref9, text;
      if (typeof params === 'string') {
        text = params;
      }
      this.emitter = new Emitter;
      this.patchesSinceLastStoppedChangingEvent = [];
      this.id = (ref3 = params != null ? params.id : void 0) != null ? ref3 : crypto.randomBytes(16).toString('hex');
      this.lines = [''];
      this.lineEndings = [''];
      this.offsetIndex = new BufferOffsetIndex();
      this.textDecorationLayers = new Set();
      this.setTextInRange([[0, 0], [0, 0]], (ref4 = text != null ? text : params != null ? params.text : void 0) != null ? ref4 : '', {
        normalizeLineEndings: false
      });
      maxUndoEntries = (ref5 = params != null ? params.maxUndoEntries : void 0) != null ? ref5 : this.defaultMaxUndoEntries;
      this.history = (ref6 = params != null ? params.history : void 0) != null ? ref6 : new History(this, maxUndoEntries);
      this.nextMarkerLayerId = (ref7 = params != null ? params.nextMarkerLayerId : void 0) != null ? ref7 : 0;
      this.nextDisplayLayerId = (ref8 = params != null ? params.nextDisplayLayerId : void 0) != null ? ref8 : 0;
      this.defaultMarkerLayer = (ref9 = params != null ? params.defaultMarkerLayer : void 0) != null ? ref9 : new MarkerLayer(this, String(this.nextMarkerLayerId++));
      this.displayLayers = {};
      this.markerLayers = (ref10 = params != null ? params.markerLayers : void 0) != null ? ref10 : {};
      this.markerLayers[this.defaultMarkerLayer.id] = this.defaultMarkerLayer;
      this.nextMarkerId = (ref11 = params != null ? params.nextMarkerId : void 0) != null ? ref11 : 1;
      this.setEncoding(params != null ? params.encoding : void 0);
      this.setPreferredLineEnding(params != null ? params.preferredLineEnding : void 0);
      this.loaded = false;
      this.destroyed = false;
      this.transactCallDepth = 0;
      this.digestWhenLastPersisted = (ref12 = params != null ? params.digestWhenLastPersisted : void 0) != null ? ref12 : false;
      if (params != null ? params.filePath : void 0) {
        this.setPath(params.filePath);
      }
      if (params != null ? params.load : void 0) {
        this.load();
      }
    }

    TextBuffer.deserialize = function(params) {
      var buffer, displayLayers, layerId, layerState, markerLayers, ref3, ref4;
      if (params.version !== TextBuffer.prototype.version) {
        return;
      }
      buffer = Object.create(TextBuffer.prototype);
      markerLayers = {};
      ref3 = params.markerLayers;
      for (layerId in ref3) {
        layerState = ref3[layerId];
        markerLayers[layerId] = MarkerLayer.deserialize(buffer, layerState);
      }
      params.markerLayers = markerLayers;
      params.defaultMarkerLayer = params.markerLayers[params.defaultMarkerLayerId];
      params.history = History.deserialize(params.history, buffer);
      if (params.filePath) {
        params.load = true;
      }
      TextBuffer.call(buffer, params);
      displayLayers = {};
      ref4 = params.displayLayers;
      for (layerId in ref4) {
        layerState = ref4[layerId];
        displayLayers[layerId] = DisplayLayer.deserialize(buffer, layerState);
      }
      buffer.setDisplayLayers(displayLayers);
      return buffer;
    };

    TextBuffer.prototype.getId = function() {
      return this.id;
    };

    TextBuffer.prototype.serialize = function(options) {
      var displayLayers, id, layer, markerLayers, ref3, ref4, ref5;
      if (options == null) {
        options = {};
      }
      if (options.markerLayers == null) {
        options.markerLayers = true;
      }
      markerLayers = {};
      if (options.markerLayers) {
        ref3 = this.markerLayers;
        for (id in ref3) {
          layer = ref3[id];
          if (layer.persistent) {
            markerLayers[id] = layer.serialize();
          }
        }
      }
      displayLayers = {};
      ref4 = this.displayLayers;
      for (id in ref4) {
        layer = ref4[id];
        displayLayers[id] = layer.serialize();
      }
      return {
        id: this.getId(),
        text: this.getText(),
        defaultMarkerLayerId: this.defaultMarkerLayer.id,
        markerLayers: markerLayers,
        displayLayers: displayLayers,
        nextMarkerLayerId: this.nextMarkerLayerId,
        nextDisplayLayerId: this.nextDisplayLayerId,
        history: this.history.serialize(options),
        encoding: this.getEncoding(),
        filePath: this.getPath(),
        digestWhenLastPersisted: (ref5 = this.file) != null ? ref5.getDigestSync() : void 0,
        preferredLineEnding: this.preferredLineEnding,
        nextMarkerId: this.nextMarkerId
      };
    };


    /*
    Section: Event Subscription
     */

    TextBuffer.prototype.onWillChange = function(callback) {
      return this.emitter.on('will-change', callback);
    };

    TextBuffer.prototype.onDidChange = function(callback) {
      return this.emitter.on('did-change', callback);
    };

    TextBuffer.prototype.onDidChangeText = function(callback) {
      return this.emitter.on('did-change-text', callback);
    };

    TextBuffer.prototype.preemptDidChange = function(callback) {
      return this.emitter.preempt('did-change', callback);
    };

    TextBuffer.prototype.onDidStopChanging = function(callback) {
      return this.emitter.on('did-stop-changing', callback);
    };

    TextBuffer.prototype.onDidConflict = function(callback) {
      return this.emitter.on('did-conflict', callback);
    };

    TextBuffer.prototype.onDidChangeModified = function(callback) {
      return this.emitter.on('did-change-modified', callback);
    };

    TextBuffer.prototype.onDidUpdateMarkers = function(callback) {
      return this.emitter.on('did-update-markers', callback);
    };

    TextBuffer.prototype.onDidCreateMarker = function(callback) {
      return this.emitter.on('did-create-marker', callback);
    };

    TextBuffer.prototype.onDidChangePath = function(callback) {
      return this.emitter.on('did-change-path', callback);
    };

    TextBuffer.prototype.onDidChangeEncoding = function(callback) {
      return this.emitter.on('did-change-encoding', callback);
    };

    TextBuffer.prototype.onWillSave = function(callback) {
      return this.emitter.on('will-save', callback);
    };

    TextBuffer.prototype.onDidSave = function(callback) {
      return this.emitter.on('did-save', callback);
    };

    TextBuffer.prototype.onDidDelete = function(callback) {
      return this.emitter.on('did-delete', callback);
    };

    TextBuffer.prototype.onWillReload = function(callback) {
      return this.emitter.on('will-reload', callback);
    };

    TextBuffer.prototype.onDidReload = function(callback) {
      return this.emitter.on('did-reload', callback);
    };

    TextBuffer.prototype.onDidDestroy = function(callback) {
      return this.emitter.on('did-destroy', callback);
    };

    TextBuffer.prototype.onWillThrowWatchError = function(callback) {
      return this.emitter.on('will-throw-watch-error', callback);
    };

    TextBuffer.prototype.getStoppedChangingDelay = function() {
      return this.stoppedChangingDelay;
    };


    /*
    Section: File Details
     */

    TextBuffer.prototype.isModified = function() {
      var ref3;
      if (this.file) {
        if (!this.loaded) {
          return false;
        }
        if (this.file.existsSync()) {
          return this.getText() !== this.cachedDiskContents;
        } else {
          return (ref3 = this.wasModifiedBeforeRemove) != null ? ref3 : !this.isEmpty();
        }
      } else {
        return !this.isEmpty();
      }
    };

    TextBuffer.prototype.isInConflict = function() {
      return this.conflict;
    };

    TextBuffer.prototype.getPath = function() {
      var ref3;
      return (ref3 = this.file) != null ? ref3.getPath() : void 0;
    };

    TextBuffer.prototype.setPath = function(filePath) {
      if (filePath === this.getPath()) {
        return;
      }
      if (filePath) {
        this.file = new File(filePath);
        this.file.setEncoding(this.getEncoding());
        this.subscribeToFile();
      } else {
        this.file = null;
      }
      return this.emitter.emit('did-change-path', this.getPath());
    };

    TextBuffer.prototype.setEncoding = function(encoding) {
      if (encoding == null) {
        encoding = 'utf8';
      }
      if (encoding === this.getEncoding()) {
        return;
      }
      this.encoding = encoding;
      if (this.file != null) {
        this.file.setEncoding(encoding);
        this.emitter.emit('did-change-encoding', encoding);
        if (!this.isModified()) {
          this.updateCachedDiskContents(true, (function(_this) {
            return function() {
              return _this.reload(true);
            };
          })(this));
        }
      } else {
        this.emitter.emit('did-change-encoding', encoding);
      }
    };

    TextBuffer.prototype.getEncoding = function() {
      var ref3, ref4;
      return (ref3 = this.encoding) != null ? ref3 : (ref4 = this.file) != null ? ref4.getEncoding() : void 0;
    };

    TextBuffer.prototype.setPreferredLineEnding = function(preferredLineEnding) {
      if (preferredLineEnding == null) {
        preferredLineEnding = null;
      }
      return this.preferredLineEnding = preferredLineEnding;
    };

    TextBuffer.prototype.getPreferredLineEnding = function() {
      return this.preferredLineEnding;
    };

    TextBuffer.prototype.getUri = function() {
      return this.getPath();
    };

    TextBuffer.prototype.getBaseName = function() {
      var ref3;
      return (ref3 = this.file) != null ? ref3.getBaseName() : void 0;
    };


    /*
    Section: Reading Text
     */

    TextBuffer.prototype.isEmpty = function() {
      return this.getLastRow() === 0 && this.lineLengthForRow(0) === 0;
    };

    TextBuffer.prototype.getText = function() {
      var j, ref3, row, text;
      if (this.cachedText != null) {
        return this.cachedText;
      } else {
        text = '';
        for (row = j = 0, ref3 = this.getLastRow(); 0 <= ref3 ? j <= ref3 : j >= ref3; row = 0 <= ref3 ? ++j : --j) {
          text += this.lineForRow(row) + this.lineEndingForRow(row);
        }
        return this.cachedText = text;
      }
    };

    TextBuffer.prototype.getTextInRange = function(range) {
      var endRow, j, line, ref3, ref4, row, startRow, text;
      range = this.clipRange(Range.fromObject(range));
      startRow = range.start.row;
      endRow = range.end.row;
      if (startRow === endRow) {
        return this.lineForRow(startRow).slice(range.start.column, range.end.column);
      } else {
        text = '';
        for (row = j = ref3 = startRow, ref4 = endRow; ref3 <= ref4 ? j <= ref4 : j >= ref4; row = ref3 <= ref4 ? ++j : --j) {
          line = this.lineForRow(row);
          if (row === startRow) {
            text += line.slice(range.start.column);
          } else if (row === endRow) {
            text += line.slice(0, range.end.column);
            continue;
          } else {
            text += line;
          }
          text += this.lineEndingForRow(row);
        }
        return text;
      }
    };

    TextBuffer.prototype.getLines = function() {
      return this.lines.slice();
    };

    TextBuffer.prototype.getLastLine = function() {
      return this.lineForRow(this.getLastRow());
    };

    TextBuffer.prototype.lineForRow = function(row) {
      return this.lines[row];
    };

    TextBuffer.prototype.lineEndingForRow = function(row) {
      return this.lineEndings[row];
    };

    TextBuffer.prototype.lineLengthForRow = function(row) {
      return this.lines[row].length;
    };

    TextBuffer.prototype.isRowBlank = function(row) {
      return !/\S/.test(this.lineForRow(row));
    };

    TextBuffer.prototype.previousNonBlankRow = function(startRow) {
      var j, ref3, row;
      if (startRow === 0) {
        return null;
      }
      startRow = Math.min(startRow, this.getLastRow());
      for (row = j = ref3 = startRow - 1; ref3 <= 0 ? j <= 0 : j >= 0; row = ref3 <= 0 ? ++j : --j) {
        if (!this.isRowBlank(row)) {
          return row;
        }
      }
      return null;
    };

    TextBuffer.prototype.nextNonBlankRow = function(startRow) {
      var j, lastRow, ref3, ref4, row;
      lastRow = this.getLastRow();
      if (startRow < lastRow) {
        for (row = j = ref3 = startRow + 1, ref4 = lastRow; ref3 <= ref4 ? j <= ref4 : j >= ref4; row = ref3 <= ref4 ? ++j : --j) {
          if (!this.isRowBlank(row)) {
            return row;
          }
        }
      }
      return null;
    };


    /*
    Section: Mutating Text
     */

    TextBuffer.prototype.setText = function(text) {
      return this.setTextInRange(this.getRange(), text, {
        normalizeLineEndings: false
      });
    };

    TextBuffer.prototype.setTextViaDiff = function(text) {
      var computeBufferColumn, currentText, endsWithNewline;
      currentText = this.getText();
      if (currentText === text) {
        return;
      }
      endsWithNewline = function(str) {
        return /[\r\n]+$/g.test(str);
      };
      computeBufferColumn = function(str) {
        var newlineIndex;
        newlineIndex = Math.max(str.lastIndexOf('\n'), str.lastIndexOf('\r'));
        if (endsWithNewline(str)) {
          return 0;
        } else if (newlineIndex === -1) {
          return str.length;
        } else {
          return str.length - newlineIndex - 1;
        }
      };
      return this.transact((function(_this) {
        return function() {
          var change, changeOptions, column, currentPosition, endColumn, endRow, j, len, lineCount, lineDiff, ref3, ref4, row;
          row = 0;
          column = 0;
          currentPosition = [0, 0];
          lineDiff = diff.diffLines(currentText, text);
          changeOptions = {
            normalizeLineEndings: false
          };
          for (j = 0, len = lineDiff.length; j < len; j++) {
            change = lineDiff[j];
            lineCount = (ref3 = (ref4 = change.value.match(newlineRegex)) != null ? ref4.length : void 0) != null ? ref3 : 0;
            currentPosition[0] = row;
            currentPosition[1] = column;
            if (change.added) {
              row += lineCount;
              column = computeBufferColumn(change.value);
              _this.setTextInRange([currentPosition, currentPosition], change.value, changeOptions);
            } else if (change.removed) {
              endRow = row + lineCount;
              endColumn = column + computeBufferColumn(change.value);
              _this.setTextInRange([currentPosition, [endRow, endColumn]], '', changeOptions);
            } else {
              row += lineCount;
              column = computeBufferColumn(change.value);
            }
          }
        };
      })(this));
    };

    TextBuffer.prototype.setTextInRange = function(range, newText, options) {
      var change, normalizeLineEndings, oldRange, oldText, undo;
      if (this.transactCallDepth === 0) {
        return this.transact((function(_this) {
          return function() {
            return _this.setTextInRange(range, newText, options);
          };
        })(this));
      }
      if (options != null) {
        normalizeLineEndings = options.normalizeLineEndings, undo = options.undo;
      }
      if (normalizeLineEndings == null) {
        normalizeLineEndings = true;
      }
      oldRange = this.clipRange(range);
      oldText = this.getTextInRange(oldRange);
      change = {
        newStart: oldRange.start,
        oldExtent: oldRange.getExtent(),
        oldText: oldText,
        newText: newText,
        normalizeLineEndings: normalizeLineEndings
      };
      return this.applyChange(change, undo !== 'skip');
    };

    TextBuffer.prototype.insert = function(position, text, options) {
      return this.setTextInRange(new Range(position, position), text, options);
    };

    TextBuffer.prototype.append = function(text, options) {
      return this.insert(this.getEndPosition(), text, options);
    };

    TextBuffer.prototype.applyChange = function(change, pushToHistory) {
      var changeEvent, displayLayer, endRow, ending, id, lastIndex, lastLine, lastLineEnding, line, lineEndings, lineStartIndex, lines, markerLayer, newExtent, newRange, newStart, newText, normalizeLineEndings, normalizedEnding, normalizedNewText, oldExtent, oldRange, oldText, prefix, ref3, ref4, ref5, ref6, result, rowCount, start, startRow, suffix;
      if (pushToHistory == null) {
        pushToHistory = false;
      }
      newStart = change.newStart, oldExtent = change.oldExtent, oldText = change.oldText, newText = change.newText, normalizeLineEndings = change.normalizeLineEndings;
      this.cachedText = null;
      start = Point.fromObject(newStart);
      oldRange = Range(start, start.traverse(oldExtent));
      oldRange.freeze();
      startRow = oldRange.start.row;
      endRow = oldRange.end.row;
      rowCount = endRow - startRow + 1;
      if (normalizeLineEndings) {
        normalizedEnding = (ref3 = this.preferredLineEnding) != null ? ref3 : this.lineEndingForRow(startRow);
        if (!normalizedEnding) {
          if (startRow > 0) {
            normalizedEnding = this.lineEndingForRow(startRow - 1);
          } else {
            normalizedEnding = null;
          }
        }
      }
      lines = [];
      lineEndings = [];
      lineStartIndex = 0;
      normalizedNewText = "";
      while (result = newlineRegex.exec(newText)) {
        line = newText.slice(lineStartIndex, result.index);
        ending = normalizedEnding != null ? normalizedEnding : result[0];
        lines.push(line);
        lineEndings.push(ending);
        normalizedNewText += line + ending;
        lineStartIndex = newlineRegex.lastIndex;
      }
      lastLine = newText.slice(lineStartIndex);
      lines.push(lastLine);
      lineEndings.push('');
      normalizedNewText += lastLine;
      newExtent = Point(lines.length - 1, lastLine.length);
      newRange = Range(start, start.traverse(newExtent));
      newRange.freeze();
      if (pushToHistory) {
        if (change.newExtent == null) {
          change.newExtent = newExtent;
        }
        if ((ref4 = this.history) != null) {
          ref4.pushChange(change);
        }
      }
      newText = normalizedNewText;
      changeEvent = Object.freeze({
        oldRange: oldRange,
        newRange: newRange,
        oldText: oldText,
        newText: newText
      });
      ref5 = this.displayLayers;
      for (id in ref5) {
        displayLayer = ref5[id];
        displayLayer.bufferWillChange(changeEvent);
      }
      this.emitter.emit('will-change', changeEvent);
      prefix = this.lineForRow(startRow).slice(0, oldRange.start.column);
      lines[0] = prefix + lines[0];
      suffix = this.lineForRow(endRow).slice(oldRange.end.column);
      lastIndex = lines.length - 1;
      lines[lastIndex] += suffix;
      lastLineEnding = this.lineEndingForRow(endRow);
      if (lastLineEnding !== '' && (normalizedEnding != null)) {
        lastLineEnding = normalizedEnding;
      }
      lineEndings[lastIndex] = lastLineEnding;
      if (this.lines.length > 1 || this.lines[0].length > 0) {
        spliceArray(this.lines, startRow, rowCount, lines);
        spliceArray(this.lineEndings, startRow, rowCount, lineEndings);
      } else {
        this.lines = lines;
        this.lineEndings = lineEndings;
      }
      this.offsetIndex.splice(startRow, rowCount, lines.map(function(line, i) {
        return line.length + lineEndings[i].length;
      }));
      if (this.markerLayers != null) {
        oldExtent = oldRange.getExtent();
        newExtent = newRange.getExtent();
        ref6 = this.markerLayers;
        for (id in ref6) {
          markerLayer = ref6[id];
          markerLayer.splice(oldRange.start, oldExtent, newExtent);
        }
      }
      if (this.conflict && !this.isModified()) {
        this.conflict = false;
      }
      this.changeCount++;
      this.emitDidChangeEvent(changeEvent);
      return newRange;
    };

    TextBuffer.prototype.emitDidChangeEvent = function(changeEvent) {
      var changeEventsByDisplayLayer, displayLayer, event, id, ref3;
      this.textDecorationLayers.forEach(function(textDecorationLayer) {
        return textDecorationLayer.bufferDidChange(changeEvent);
      });
      changeEventsByDisplayLayer = new Map();
      ref3 = this.displayLayers;
      for (id in ref3) {
        displayLayer = ref3[id];
        event = displayLayer.bufferDidChange(changeEvent);
        changeEventsByDisplayLayer.set(displayLayer, event);
      }
      this.emitter.emit('did-change', changeEvent);
      return changeEventsByDisplayLayer.forEach(function(event, displayLayer) {
        return displayLayer.emitDidChangeSyncEvent(event);
      });
    };

    TextBuffer.prototype["delete"] = function(range) {
      return this.setTextInRange(range, '');
    };

    TextBuffer.prototype.deleteRow = function(row) {
      return this.deleteRows(row, row);
    };

    TextBuffer.prototype.deleteRows = function(startRow, endRow) {
      var endPoint, lastRow, ref3, startPoint;
      lastRow = this.getLastRow();
      if (startRow > endRow) {
        ref3 = [endRow, startRow], startRow = ref3[0], endRow = ref3[1];
      }
      if (endRow < 0) {
        return new Range(this.getFirstPosition(), this.getFirstPosition());
      }
      if (startRow > lastRow) {
        return new Range(this.getEndPosition(), this.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 === 0) {
          startPoint = new Point(startRow, 0);
        } else {
          startPoint = new Point(startRow - 1, this.lineLengthForRow(startRow - 1));
        }
        endPoint = new Point(endRow, this.lineLengthForRow(endRow));
      }
      return this["delete"](new Range(startPoint, endPoint));
    };


    /*
    Section: Markers
     */

    TextBuffer.prototype.addMarkerLayer = function(options) {
      var layer;
      layer = new MarkerLayer(this, String(this.nextMarkerLayerId++), options);
      this.markerLayers[layer.id] = layer;
      return layer;
    };

    TextBuffer.prototype.getMarkerLayer = function(id) {
      return this.markerLayers[id];
    };

    TextBuffer.prototype.getDefaultMarkerLayer = function() {
      return this.defaultMarkerLayer;
    };

    TextBuffer.prototype.markRange = function(range, properties) {
      return this.defaultMarkerLayer.markRange(range, properties);
    };

    TextBuffer.prototype.markPosition = function(position, options) {
      return this.defaultMarkerLayer.markPosition(position, options);
    };

    TextBuffer.prototype.getMarkers = function() {
      return this.defaultMarkerLayer.getMarkers();
    };

    TextBuffer.prototype.getMarker = function(id) {
      return this.defaultMarkerLayer.getMarker(id);
    };

    TextBuffer.prototype.findMarkers = function(params) {
      return this.defaultMarkerLayer.findMarkers(params);
    };

    TextBuffer.prototype.getMarkerCount = function() {
      return this.defaultMarkerLayer.getMarkerCount();
    };

    TextBuffer.prototype.destroyMarker = function(id) {
      var ref3;
      return (ref3 = this.getMarker(id)) != null ? ref3.destroy() : void 0;
    };


    /*
    Section: History
     */

    TextBuffer.prototype.undo = function() {
      var change, j, len, pop, ref3;
      if (pop = this.history.popUndoStack()) {
        ref3 = pop.patch.getHunks();
        for (j = 0, len = ref3.length; j < len; j++) {
          change = ref3[j];
          this.applyChange(change);
        }
        this.restoreFromMarkerSnapshot(pop.snapshot);
        this.emitMarkerChangeEvents(pop.snapshot);
        this.emitDidChangeTextEvent(pop.patch);
        return true;
      } else {
        return false;
      }
    };

    TextBuffer.prototype.redo = function() {
      var change, j, len, pop, ref3;
      if (pop = this.history.popRedoStack()) {
        ref3 = pop.patch.getHunks();
        for (j = 0, len = ref3.length; j < len; j++) {
          change = ref3[j];
          this.applyChange(change);
        }
        this.restoreFromMarkerSnapshot(pop.snapshot);
        this.emitMarkerChangeEvents(pop.snapshot);
        this.emitDidChangeTextEvent(pop.patch);
        return true;
      } else {
        return false;
      }
    };

    TextBuffer.prototype.transact = function(groupingInterval, fn) {
      var checkpointBefore, compactedChanges, endMarkerSnapshot, exception, ref3, result;
      if (typeof groupingInterval === 'function') {
        fn = groupingInterval;
        groupingInterval = 0;
      }
      checkpointBefore = this.history.createCheckpoint(this.createMarkerSnapshot(), true);
      try {
        this.transactCallDepth++;
        result = fn();
      } catch (error1) {
        exception = error1;
        this.revertToCheckpoint(checkpointBefore, true);
        if (!(exception instanceof TransactionAbortedError)) {
          throw exception;
        }
        return;
      } finally {
        this.transactCallDepth--;
      }
      endMarkerSnapshot = this.createMarkerSnapshot();
      compactedChanges = this.history.groupChangesSinceCheckpoint(checkpointBefore, endMarkerSnapshot, true);
      if ((ref3 = global.atom) != null) {
        ref3.assert(compactedChanges, "groupChangesSinceCheckpoint() returned false.", (function(_this) {
          return function(error) {
            return error.metadata = {
              history: _this.history.toString()
            };
          };
        })(this));
      }
      this.history.applyGroupingInterval(groupingInterval);
      this.history.enforceUndoStackSizeLimit();
      this.emitMarkerChangeEvents(endMarkerSnapshot);
      if (compactedChanges) {
        this.emitDidChangeTextEvent(compactedChanges);
      }
      return result;
    };

    TextBuffer.prototype.abortTransaction = function() {
      throw new TransactionAbortedError("Transaction aborted.");
    };

    TextBuffer.prototype.clearUndoStack = function() {
      return this.history.clearUndoStack();
    };

    TextBuffer.prototype.createCheckpoint = function() {
      return this.history.createCheckpoint(this.createMarkerSnapshot(), false);
    };

    TextBuffer.prototype.revertToCheckpoint = function(checkpoint) {
      var change, j, len, ref3, truncated;
      if (truncated = this.history.truncateUndoStack(checkpoint)) {
        ref3 = truncated.patch.getHunks();
        for (j = 0, len = ref3.length; j < len; j++) {
          change = ref3[j];
          this.applyChange(change);
        }
        this.restoreFromMarkerSnapshot(truncated.snapshot);
        this.emitter.emit('did-update-markers');
        this.emitDidChangeTextEvent(truncated.patch);
        return true;
      } else {
        return false;
      }
    };

    TextBuffer.prototype.groupChangesSinceCheckpoint = function(checkpoint) {
      return this.history.groupChangesSinceCheckpoint(checkpoint, this.createMarkerSnapshot(), false);
    };

    TextBuffer.prototype.getChangesSinceCheckpoint = function(checkpoint) {
      var patch;
      if (patch = this.history.getChangesSinceCheckpoint(checkpoint)) {
        return normalizePatchChanges(patch.getHunks());
      } else {
        return [];
      }
    };


    /*
    Section: Search And Replace
     */

    TextBuffer.prototype.scan = function(regex, iterator) {
      return this.scanInRange(regex, this.getRange(), iterator);
    };

    TextBuffer.prototype.backwardsScan = function(regex, iterator) {
      return this.backwardsScanInRange(regex, this.getRange(), iterator);
    };

    TextBuffer.prototype.scanInRange = function(regex, range, callback, reverse) {
      var flags, global, iterator;
      if (reverse == null) {
        reverse = false;
      }
      range = this.clipRange(range);
      global = regex.global;
      flags = "gm";
      if (regex.ignoreCase) {
        flags += "i";
      }
      regex = new RegExp(regex.source, flags);
      if (regexIsSingleLine(regex)) {
        if (reverse) {
          iterator = new MatchIterator.BackwardsSingleLine(this, regex, range);
        } else {
          iterator = new MatchIterator.ForwardsSingleLine(this, regex, range);
        }
      } else {
        if (reverse) {
          iterator = new MatchIterator.BackwardsMultiLine(this, regex, range, this.backwardsScanChunkSize);
        } else {
          iterator = new MatchIterator.ForwardsMultiLine(this, regex, range);
        }
      }
      return iterator.iterate(callback, global);
    };

    TextBuffer.prototype.backwardsScanInRange = function(regex, range, iterator) {
      return this.scanInRange(regex, range, iterator, true);
    };

    TextBuffer.prototype.replace = function(regex, replacementText) {
      var doSave, replacements;
      doSave = !this.isModified();
      replacements = 0;
      this.transact((function(_this) {
        return function() {
          return _this.scan(regex, function(arg) {
            var matchText, replace;
            matchText = arg.matchText, replace = arg.replace;
            replace(matchText.replace(regex, replacementText));
            return replacements++;
          });
        };
      })(this));
      if (doSave) {
        this.save();
      }
      return replacements;
    };


    /*
    Section: Buffer Range Details
     */

    TextBuffer.prototype.getRange = function() {
      return new Range(this.getFirstPosition(), this.getEndPosition());
    };

    TextBuffer.prototype.getLineCount = function() {
      return this.lines.length;
    };

    TextBuffer.prototype.getLastRow = function() {
      return this.getLineCount() - 1;
    };

    TextBuffer.prototype.getFirstPosition = function() {
      return new Point(0, 0);
    };

    TextBuffer.prototype.getEndPosition = function() {
      var lastRow;
      lastRow = this.getLastRow();
      return new Point(lastRow, this.lineLengthForRow(lastRow));
    };

    TextBuffer.prototype.getMaxCharacterIndex = function() {
      return this.characterIndexForPosition(Point.INFINITY);
    };

    TextBuffer.prototype.rangeForRow = function(row, includeNewline) {
      row = Math.max(row, 0);
      row = Math.min(row, this.getLastRow());
      if (includeNewline && row < this.getLastRow()) {
        return new Range(new Point(row, 0), new Point(row + 1, 0));
      } else {
        return new Range(new Point(row, 0), new Point(row, this.lineLengthForRow(row)));
      }
    };

    TextBuffer.prototype.characterIndexForPosition = function(position) {
      position = this.clipPosition(Point.fromObject(position));
      return this.offsetIndex.characterIndexForPosition(position);
    };

    TextBuffer.prototype.positionForCharacterIndex = function(offset) {
      var position;
      position = this.offsetIndex.positionForCharacterIndex(Math.max(0, offset));
      return new Point(position.row, position.column);
    };

    TextBuffer.prototype.clipRange = function(range) {
      var end, start;
      range = Range.fromObject(range);
      start = this.clipPosition(range.start);
      end = this.clipPosition(range.end);
      if (range.start.isEqual(start) && range.end.isEqual(end)) {
        return range;
      } else {
        return new Range(start, end);
      }
    };

    TextBuffer.prototype.clipPosition = function(position, options) {
      var column, row;
      position = Point.fromObject(position);
      Point.assertValid(position);
      row = position.row, column = position.column;
      if (row < 0) {
        return this.getFirstPosition();
      } else if (row > this.getLastRow()) {
        return this.getEndPosition();
      } else if (column < 0) {
        return Point(row, 0);
      } else if (column >= this.lineLengthForRow(row)) {
        if ((options != null ? options.clipDirection : void 0) === 'forward' && row < this.getLastRow()) {
          return Point(row + 1, 0);
        } else {
          return Point(row, this.lineLengthForRow(row));
        }
      } else {
        return position;
      }
    };


    /*
    Section: Buffer Operations
     */

    TextBuffer.prototype.save = function(options) {
      return this.saveAs(this.getPath(), options);
    };

    TextBuffer.prototype.saveAs = function(filePath, options) {
      var backupFile, error;
      if (this.destroyed) {
        throw new Error("Can't save destroyed buffer");
      }
      if (!filePath) {
        throw new Error("Can't save buffer with no file path");
      }
      this.emitter.emit('will-save', {
        path: filePath
      });
      this.setPath(filePath);
      if (options != null ? options.backup : void 0) {
        backupFile = this.backUpFileContentsBeforeWriting();
      }
      try {
        this.file.writeSync(this.getText());
        if (backupFile != null) {
          backupFile.safeRemoveSync();
        }
      } catch (error1) {
        error = error1;
        if (backupFile != null) {
          this.file.writeSync(backupFile.readSync());
        }
        throw error;
      }
      this.cachedDiskContents = this.getText();
      this.conflict = false;
      this.emitModifiedStatusChanged(false);
      return this.emitter.emit('did-save', {
        path: filePath
      });
    };

    TextBuffer.prototype.reload = function(clearHistory) {
      var ref3, ref4;
      if (clearHistory == null) {
        clearHistory = false;
      }
      if (this.destroyed) {
        return;
      }
      this.emitter.emit('will-reload');
      if (clearHistory) {
        this.clearUndoStack();
        this.setTextInRange(this.getRange(), (ref3 = this.cachedDiskContents) != null ? ref3 : "", {
          normalizeLineEndings: false,
          undo: 'skip'
        });
      } else {
        this.setTextViaDiff((ref4 = this.cachedDiskContents) != null ? ref4 : "");
      }
      this.emitModifiedStatusChanged(false);
      return this.emitter.emit('did-reload');
    };

    TextBuffer.prototype.updateCachedDiskContentsSync = function() {
      var ref3, ref4;
      return this.cachedDiskContents = (ref3 = (ref4 = this.file) != null ? ref4.readSync() : void 0) != null ? ref3 : "";
    };

    TextBuffer.prototype.updateCachedDiskContents = function(flushCache, callback) {
      var promise;
      if (flushCache == null) {
        flushCache = false;
      }
      if (this.file != null) {
        promise = this.file.read(flushCache);
      } else {
        promise = Promise.resolve("");
      }
      return promise.then((function(_this) {
        return function(contents) {
          _this.cachedDiskContents = contents;
          return typeof callback === "function" ? callback() : void 0;
        };
      })(this));
    };

    TextBuffer.prototype.backUpFileContentsBeforeWriting = function() {
      var backupFilePath, file, maxTildes;
      if (!this.file.existsSync()) {
        return;
      }
      backupFilePath = this.getPath() + '~';
      maxTildes = 10;
      while (fs.existsSync(backupFilePath)) {
        if (--maxTildes === 0) {
          throw new Error("Can't create a backup file for " + (this.getPath()) + " because files already exist at every candidate path.");
        }
        backupFilePath += '~';
      }
      file = new File(backupFilePath, false, false);
      file.safeWriteSync(this.file.readSync());
      return file;
    };


    /*
    Section: Display Layers
     */

    TextBuffer.prototype.addDisplayLayer = function(params) {
      var id;
      id = this.nextDisplayLayerId++;
      return this.displayLayers[id] = new DisplayLayer(id, this, params);
    };

    TextBuffer.prototype.getDisplayLayer = function(id) {
      return this.displayLayers[id];
    };

    TextBuffer.prototype.setDisplayLayers = function(displayLayers1) {
      this.displayLayers = displayLayers1;
    };

    TextBuffer.prototype.registerTextDecorationLayer = function(textDecorationLayer) {
      this.textDecorationLayers.add(textDecorationLayer);
      return new Disposable((function(_this) {
        return function() {
          return _this.textDecorationLayers["delete"](textDecorationLayer);
        };
      })(this));
    };


    /*
    Section: Private Utility Methods
     */

    TextBuffer.prototype.loadSync = function() {
      this.updateCachedDiskContentsSync();
      return this.finishLoading();
    };

    TextBuffer.prototype.load = function() {
      return this.updateCachedDiskContents().then((function(_this) {
        return function() {
          return _this.finishLoading();
        };
      })(this));
    };

    TextBuffer.prototype.finishLoading = function() {
      var ref3;
      if (this.isAlive()) {
        this.loaded = true;
        if (this.digestWhenLastPersisted === ((ref3 = this.file) != null ? ref3.getDigestSync() : void 0)) {
          this.emitModifiedStatusChanged(this.isModified());
        } else {
          this.reload(true);
        }
      }
      return this;
    };

    TextBuffer.prototype.destroy = function() {
      var id, markerLayer, ref3, ref4;
      if (!this.destroyed) {
        this.destroyed = true;
        this.emitter.emit('did-destroy');
        this.emitter.clear();
        this.cancelStoppedChangingTimeout();
        if ((ref3 = this.fileSubscriptions) != null) {
          ref3.dispose();
        }
        ref4 = this.markerLayers;
        for (id in ref4) {
          markerLayer = ref4[id];
          markerLayer.destroy();
        }
        this.setText('', {
          undo: 'skip'
        });
        return this.history.clear();
      }
    };

    TextBuffer.prototype.isAlive = function() {
      return !this.destroyed;
    };

    TextBuffer.prototype.isDestroyed = function() {
      return this.destroyed;
    };

    TextBuffer.prototype.isRetained = function() {
      return this.refcount > 0;
    };

    TextBuffer.prototype.retain = function() {
      this.refcount++;
      return this;
    };

    TextBuffer.prototype.release = function() {
      this.refcount--;
      if (!this.isRetained()) {
        this.destroy();
      }
      return this;
    };

    TextBuffer.prototype.subscribeToFile = function() {
      var ref3;
      if ((ref3 = this.fileSubscriptions) != null) {
        ref3.dispose();
      }
      this.fileSubscriptions = new CompositeDisposable;
      this.fileSubscriptions.add(this.file.onDidChange((function(_this) {
        return function() {
          var previousContents;
          if (_this.isModified()) {
            _this.conflict = true;
          }
          previousContents = _this.cachedDiskContents;
          _this.updateCachedDiskContentsSync();
          if (previousContents === _this.cachedDiskContents) {
            return;
          }
          if (_this.conflict) {
            return _this.emitter.emit('did-conflict');
          } else {
            return _this.reload();
          }
        };
      })(this)));
      this.fileSubscriptions.add(this.file.onDidDelete((function(_this) {
        return function() {
          var modified;
          modified = _this.getText() !== _this.cachedDiskContents;
          _this.wasModifiedBeforeRemove = modified;
          _this.emitter.emit('did-delete');
          if (modified) {
            return _this.updateCachedDiskContents();
          } else {
            return _this.destroy();
          }
        };
      })(this)));
      this.fileSubscriptions.add(this.file.onDidRename((function(_this) {
        return function() {
          return _this.emitter.emit('did-change-path', _this.getPath());
        };
      })(this)));
      return this.fileSubscriptions.add(this.file.onWillThrowWatchError((function(_this) {
        return function(errorObject) {
          return _this.emitter.emit('will-throw-watch-error', errorObject);
        };
      })(this)));
    };

    TextBuffer.prototype.createMarkerSnapshot = function() {
      var markerLayer, markerLayerId, ref3, snapshot;
      snapshot = {};
      ref3 = this.markerLayers;
      for (markerLayerId in ref3) {
        markerLayer = ref3[markerLayerId];
        if (markerLayer.maintainHistory) {
          snapshot[markerLayerId] = markerLayer.createSnapshot();
        }
      }
      return snapshot;
    };

    TextBuffer.prototype.restoreFromMarkerSnapshot = function(snapshot) {
      var layerSnapshot, markerLayerId, ref3, results;
      results = [];
      for (markerLayerId in snapshot) {
        layerSnapshot = snapshot[markerLayerId];
        results.push((ref3 = this.markerLayers[markerLayerId]) != null ? ref3.restoreFromSnapshot(layerSnapshot) : void 0);
      }
      return results;
    };

    TextBuffer.prototype.emitMarkerChangeEvents = function(snapshot) {
      var markerLayer, markerLayerId, ref3, results;
      ref3 = this.markerLayers;
      results = [];
      for (markerLayerId in ref3) {
        markerLayer = ref3[markerLayerId];
        results.push(markerLayer.emitChangeEvents(snapshot != null ? snapshot[markerLayerId] : void 0));
      }
      return results;
    };

    TextBuffer.prototype.emitDidChangeTextEvent = function(patch) {
      if (this.transactCallDepth !== 0) {
        return;
      }
      this.emitter.emit('did-change-text', {
        changes: Object.freeze(normalizePatchChanges(patch.getHunks()))
      });
      this.patchesSinceLastStoppedChangingEvent.push(patch);
      return this.scheduleDidStopChangingEvent();
    };

    TextBuffer.prototype.hasMultipleEditors = function() {
      return this.refcount > 1;
    };

    TextBuffer.prototype.cancelStoppedChangingTimeout = function() {
      if (this.stoppedChangingTimeout) {
        return clearTimeout(this.stoppedChangingTimeout);
      }
    };

    TextBuffer.prototype.scheduleDidStopChangingEvent = function() {
      var stoppedChangingCallback;
      this.cancelStoppedChangingTimeout();
      stoppedChangingCallback = (function(_this) {
        return function() {
          var modifiedStatus;
          _this.stoppedChangingTimeout = null;
          modifiedStatus = _this.isModified();
          _this.emitter.emit('did-stop-changing', {
            changes: Object.freeze(normalizePatchChanges(Patch.compose(_this.patchesSinceLastStoppedChangingEvent).getHunks()))
          });
          _this.patchesSinceLastStoppedChangingEvent = [];
          return _this.emitModifiedStatusChanged(modifiedStatus);
        };
      })(this);
      return this.stoppedChangingTimeout = setTimeout(stoppedChangingCallback, this.stoppedChangingDelay);
    };

    TextBuffer.prototype.emitModifiedStatusChanged = function(modifiedStatus) {
      if (modifiedStatus === this.previousModifiedStatus) {
        return;
      }
      this.previousModifiedStatus = modifiedStatus;
      return this.emitter.emit('did-change-modified', modifiedStatus);
    };

    TextBuffer.prototype.logLines = function(start, end) {
      var j, line, ref3, ref4, row;
      if (start == null) {
        start = 0;
      }
      if (end == null) {
        end = this.getLastRow();
      }
      for (row = j = ref3 = start, ref4 = end; ref3 <= ref4 ? j <= ref4 : j >= ref4; row = ref3 <= ref4 ? ++j : --j) {
        line = this.lineForRow(row);
        console.log(row, line, line.length);
      }
    };


    /*
    Section: Private History Delegate Methods
     */

    TextBuffer.prototype.invertChange = function(change) {
      return Object.freeze({
        oldRange: change.newRange,
        newRange: change.oldRange,
        oldText: change.newText,
        newText: change.oldText
      });
    };

    TextBuffer.prototype.serializeChange = function(change) {
      return {
        oldRange: change.oldRange.serialize(),
        newRange: change.newRange.serialize(),
        oldText: change.oldText,
        newText: change.newText
      };
    };

    TextBuffer.prototype.deserializeChange = function(change) {
      return {
        oldRange: Range.deserialize(change.oldRange),
        newRange: Range.deserialize(change.newRange),
        oldText: change.oldText,
        newText: change.newText
      };
    };

    TextBuffer.prototype.serializeSnapshot = function(snapshot, options) {
      if (!options.markerLayers) {
        return;
      }
      return MarkerLayer.serializeSnapshot(snapshot);
    };

    TextBuffer.prototype.deserializeSnapshot = function(snapshot) {
      return MarkerLayer.deserializeSnapshot(snapshot);
    };


    /*
    Section: Private MarkerLayer Delegate Methods
     */

    TextBuffer.prototype.markerLayerDestroyed = function(markerLayer) {
      return delete this.markerLayers[markerLayer.id];
    };

    TextBuffer.prototype.markerCreated = function(layer, marker) {
      if (layer === this.defaultMarkerLayer) {
        return this.emitter.emit('did-create-marker', marker);
      }
    };

    TextBuffer.prototype.markersUpdated = function(layer) {
      if (layer === this.defaultMarkerLayer) {
        return this.emitter.emit('did-update-markers');
      }
    };

    TextBuffer.prototype.getNextMarkerId = function() {
      return this.nextMarkerId++;
    };

    return TextBuffer;

  })();

}).call(this);
