Skip to content

Instantly share code, notes, and snippets.

@heyimalex
Created June 12, 2015 21:23
Show Gist options
  • Save heyimalex/346049c6d946193e0ccd to your computer and use it in GitHub Desktop.
Save heyimalex/346049c6d946193e0ccd to your computer and use it in GitHub Desktop.
Undo/Redo helper for immutable state changes.
export default function History(initialState) {
this.reset(initialState);
}
History.prototype = {
reset: function(initialState) {
this.history = [initialState];
this.pointer = 0;
this.tail = 0;
},
canUndo: function() {
return this.pointer > 0;
},
canRedo: function() {
return this.pointer < this.tail;
},
undo: function() {
if (!this.canUndo()) { return null; }
this.pointer--;
return this.getState();
},
redo: function() {
if (!this.canRedo()) { return null; }
this.pointer++;
return this.getState();
},
getState: function() {
return this.history[this.pointer];
},
pushState: function(state) {
const tail = this.tail;
const pointer = this.pointer;
const nextPointer = pointer + 1;
if (pointer === tail) {
this.history[nextPointer] = state;
} else {
const removeCount = tail - pointer;
this.history.splice(nextPointer, removeCount, state);
}
this.pointer = nextPointer;
this.tail = nextPointer;
}
};
export function RingBufferHistory(capacity, initialState) {
this.capacity = capacity;
this.reset(initialState);
}
RingBufferHistory.prototype = {
reset: function(initialState) {
this.buffer = [initialState];
this.buffer.length = this.capacity;
this.head = 0;
this.tail = 0;
this.pointer = 0;
},
canUndo: function() {
return this.pointer !== this.head;
},
canRedo: function() {
return this.pointer !== this.tail;
},
undo: function() {
if (!this.canUndo()) { return null; }
this.pointer = (this.pointer - 1 + this.capacity) % this.capacity;
return this.getState();
},
redo: function() {
if (!this.canRedo()) { return null; }
this.pointer = (this.pointer + 1) % this.capacity;
return this.getState();
},
getState: function() {
return this.buffer[this.pointer];
},
pushState: function(state) {
const pointer = this.pointer;
const capacity = this.capacity;
const nextPointer = (pointer + 1) % capacity;
const head = this.head;
if (nextPointer === head) {
this.head = ( head + 1 ) % capacity;
}
this.pointer = nextPointer;
this.tail = nextPointer;
this.buffer[nextPointer] = state;
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment