simple-text-buffer
Version:
A version of Atom's text-buffer with marker layers and file support removed
410 lines (375 loc) • 12.3 kB
JavaScript
(function() {
var Checkpoint, History, Patch, SerializationVersion, Transaction;
Patch = require('atom-patch');
SerializationVersion = 5;
Checkpoint = (function() {
function Checkpoint(id, isBoundary1) {
this.id = id;
this.isBoundary = isBoundary1;
}
return Checkpoint;
})();
Transaction = (function() {
function Transaction(patch1, groupingInterval1) {
this.patch = patch1;
this.groupingInterval = groupingInterval1 != null ? groupingInterval1 : 0;
this.timestamp = Date.now();
}
Transaction.prototype.shouldGroupWith = function(previousTransaction) {
var timeBetweenTransactions;
timeBetweenTransactions = this.timestamp - previousTransaction.timestamp;
return timeBetweenTransactions < Math.min(this.groupingInterval, previousTransaction.groupingInterval);
};
Transaction.prototype.groupWith = function(previousTransaction) {
return new Transaction(Patch.compose([previousTransaction.patch, this.patch]), this.groupingInterval);
};
return Transaction;
})();
module.exports = History = (function() {
History.deserialize = function(state, buffer) {
var history;
history = new History(buffer);
history.deserialize(state);
return history;
};
function History(buffer1, maxUndoEntries) {
this.buffer = buffer1;
this.maxUndoEntries = maxUndoEntries;
this.nextCheckpointId = 0;
this.undoStack = [];
this.redoStack = [];
}
History.prototype.createCheckpoint = function(isBoundary) {
var checkpoint;
checkpoint = new Checkpoint(this.nextCheckpointId++, isBoundary);
this.undoStack.push(checkpoint);
return checkpoint.id;
};
History.prototype.groupChangesSinceCheckpoint = function(checkpointId, deleteCheckpoint) {
var checkpointIndex, composedPatches, entry, i, j, patchesSinceCheckpoint, ref;
if (deleteCheckpoint == null) {
deleteCheckpoint = false;
}
checkpointIndex = null;
patchesSinceCheckpoint = [];
ref = this.undoStack;
for (i = j = ref.length - 1; j >= 0; i = j += -1) {
entry = ref[i];
if (checkpointIndex != null) {
break;
}
switch (entry.constructor) {
case Checkpoint:
if (entry.id === checkpointId) {
checkpointIndex = i;
} else if (entry.isBoundary) {
return false;
}
break;
case Transaction:
patchesSinceCheckpoint.unshift(entry.patch);
break;
case Patch:
patchesSinceCheckpoint.unshift(entry);
break;
default:
throw new Error("Unexpected undo stack entry type: " + entry.constructor.name);
}
}
if (checkpointIndex != null) {
composedPatches = Patch.compose(patchesSinceCheckpoint);
if (patchesSinceCheckpoint.length > 0) {
this.undoStack.splice(checkpointIndex + 1);
this.undoStack.push(new Transaction(composedPatches));
}
if (deleteCheckpoint) {
this.undoStack.splice(checkpointIndex, 1);
}
return composedPatches;
} else {
return false;
}
};
History.prototype.getChangesSinceCheckpoint = function(checkpointId) {
var checkpointIndex, entry, i, j, patchesSinceCheckpoint, ref;
checkpointIndex = null;
patchesSinceCheckpoint = [];
ref = this.undoStack;
for (i = j = ref.length - 1; j >= 0; i = j += -1) {
entry = ref[i];
if (checkpointIndex != null) {
break;
}
switch (entry.constructor) {
case Checkpoint:
if (entry.id === checkpointId) {
checkpointIndex = i;
}
break;
case Transaction:
patchesSinceCheckpoint.unshift(entry.patch);
break;
case Patch:
patchesSinceCheckpoint.unshift(entry);
break;
default:
throw new Error("Unexpected undo stack entry type: " + entry.constructor.name);
}
}
if (checkpointIndex != null) {
return Patch.compose(patchesSinceCheckpoint);
} else {
return null;
}
};
History.prototype.enforceUndoStackSizeLimit = function() {
if (this.undoStack.length > this.maxUndoEntries) {
return this.undoStack.splice(0, this.undoStack.length - this.maxUndoEntries);
}
};
History.prototype.applyGroupingInterval = function(groupingInterval) {
var previousEntry, topEntry;
topEntry = this.undoStack[this.undoStack.length - 1];
previousEntry = this.undoStack[this.undoStack.length - 2];
if (topEntry instanceof Transaction) {
topEntry.groupingInterval = groupingInterval;
} else {
return;
}
if (groupingInterval === 0) {
return;
}
if (previousEntry instanceof Transaction && topEntry.shouldGroupWith(previousEntry)) {
return this.undoStack.splice(this.undoStack.length - 2, 2, topEntry.groupWith(previousEntry));
}
};
History.prototype.pushChange = function(change) {
this.undoStack.push(Patch.hunk(change));
return this.clearRedoStack();
};
History.prototype.popUndoStack = function() {
var entry, i, j, patch, ref, ref1, spliceIndex;
patch = null;
spliceIndex = null;
ref = this.undoStack;
for (i = j = ref.length - 1; j >= 0; i = j += -1) {
entry = ref[i];
if (spliceIndex != null) {
break;
}
switch (entry.constructor) {
case Checkpoint:
if (entry.isBoundary) {
return false;
}
break;
case Transaction:
patch = Patch.invert(entry.patch);
spliceIndex = i;
break;
case Patch:
patch = Patch.invert(entry);
spliceIndex = i;
break;
default:
throw new Error("Unexpected entry type when popping undoStack: " + entry.constructor.name);
}
}
if (spliceIndex != null) {
(ref1 = this.redoStack).push.apply(ref1, this.undoStack.splice(spliceIndex).reverse());
return {
patch: patch
};
} else {
return false;
}
};
History.prototype.popRedoStack = function() {
var entry, i, j, patch, ref, ref1, spliceIndex;
patch = null;
spliceIndex = null;
ref = this.redoStack;
for (i = j = ref.length - 1; j >= 0; i = j += -1) {
entry = ref[i];
if (spliceIndex != null) {
break;
}
switch (entry.constructor) {
case Checkpoint:
if (entry.isBoundary) {
throw new Error("Invalid redo stack state");
}
break;
case Transaction:
patch = entry.patch;
spliceIndex = i;
break;
case Patch:
patch = entry;
spliceIndex = i;
break;
default:
throw new Error("Unexpected entry type when popping redoStack: " + entry.constructor.name);
}
}
while (this.redoStack[spliceIndex - 1] instanceof Checkpoint) {
spliceIndex--;
}
if (spliceIndex != null) {
(ref1 = this.undoStack).push.apply(ref1, this.redoStack.splice(spliceIndex).reverse());
return {
patch: patch
};
} else {
return false;
}
};
History.prototype.truncateUndoStack = function(checkpointId) {
var entry, i, j, patchesSinceCheckpoint, ref, spliceIndex;
spliceIndex = null;
patchesSinceCheckpoint = [];
ref = this.undoStack;
for (i = j = ref.length - 1; j >= 0; i = j += -1) {
entry = ref[i];
if (spliceIndex != null) {
break;
}
switch (entry.constructor) {
case Checkpoint:
if (entry.id === checkpointId) {
spliceIndex = i;
} else if (entry.isBoundary) {
return false;
}
break;
case Transaction:
patchesSinceCheckpoint.push(Patch.invert(entry.patch));
break;
default:
patchesSinceCheckpoint.push(Patch.invert(entry));
}
}
if (spliceIndex != null) {
this.undoStack.splice(spliceIndex);
return {
patch: Patch.compose(patchesSinceCheckpoint)
};
} else {
return false;
}
};
History.prototype.clearUndoStack = function() {
return this.undoStack.length = 0;
};
History.prototype.clearRedoStack = function() {
return this.redoStack.length = 0;
};
History.prototype.toString = function() {
var entry, j, len, output, ref;
output = '';
ref = this.undoStack;
for (j = 0, len = ref.length; j < len; j++) {
entry = ref[j];
switch (entry.constructor) {
case Checkpoint:
output += "Checkpoint, ";
break;
case Transaction:
output += "Transaction, ";
break;
case Patch:
output += "Patch, ";
break;
default:
output += "Unknown {" + (JSON.stringify(entry)) + "}, ";
}
}
return '[' + output.slice(0, -2) + ']';
};
History.prototype.serialize = function(options) {
return {
version: SerializationVersion,
nextCheckpointId: this.nextCheckpointId,
undoStack: this.serializeStack(this.undoStack, options),
redoStack: this.serializeStack(this.redoStack, options),
maxUndoEntries: this.maxUndoEntries
};
};
History.prototype.deserialize = function(state) {
if (state.version !== SerializationVersion) {
return;
}
this.nextCheckpointId = state.nextCheckpointId;
this.maxUndoEntries = state.maxUndoEntries;
this.undoStack = this.deserializeStack(state.undoStack);
return this.redoStack = this.deserializeStack(state.redoStack);
};
/*
Section: Private
*/
History.prototype.getCheckpointIndex = function(checkpointId) {
var entry, i, j, ref;
ref = this.undoStack;
for (i = j = ref.length - 1; j >= 0; i = j += -1) {
entry = ref[i];
if (entry instanceof Checkpoint && entry.id === checkpointId) {
return i;
}
}
return null;
};
History.prototype.serializeStack = function(stack, options) {
var entry, j, len, results;
results = [];
for (j = 0, len = stack.length; j < len; j++) {
entry = stack[j];
switch (entry.constructor) {
case Checkpoint:
results.push({
type: 'checkpoint',
id: entry.id,
isBoundary: entry.isBoundary
});
break;
case Transaction:
results.push({
type: 'transaction',
patch: entry.patch.serialize()
});
break;
case Patch:
results.push({
type: 'patch',
content: entry.serialize()
});
break;
default:
throw new Error("Unexpected undoStack entry type during serialization: " + entry.constructor.name);
}
}
return results;
};
History.prototype.deserializeStack = function(stack) {
var entry, j, len, results;
results = [];
for (j = 0, len = stack.length; j < len; j++) {
entry = stack[j];
switch (entry.type) {
case 'checkpoint':
results.push(new Checkpoint(entry.id, entry.isBoundary));
break;
case 'transaction':
results.push(new Transaction(Patch.deserialize(entry.patch)));
break;
case 'patch':
results.push(Patch.deserialize(entry.content));
break;
default:
throw new Error("Unexpected undoStack entry type during deserialization: " + entry.type);
}
}
return results;
};
return History;
})();
}).call(this);