have visual refs working and better error models

This commit is contained in:
Peter Cottle 2012-09-12 21:38:00 -07:00
parent 9221088853
commit af76c03ad1
10 changed files with 212 additions and 74 deletions

5
src/collections.js Normal file
View file

@ -0,0 +1,5 @@
var CommitCollection = Backbone.Collection.extend({
model: Commit
});
var commitCollection = new CommitCollection();

View file

@ -37,22 +37,24 @@ Command.prototype.getRegexMap = function() {
Command.prototype.getSandboxCommands = function() {
return [
[/^ls/, function() {
throw new CommandResult("\
DontWorryAboutFilesInThisDemo.txt\
");
throw new CommandResult({
msg: "DontWorryAboutFilesInThisDemo.txt"
});
}],
[/^cd/, function() {
throw new CommandResult("\
Directory Changed to '/directories/dont/matter/in/this/demo' \
");
throw new CommandResult({
msg: "Directory Changed to '/directories/dont/matter/in/this/demo'"
});
}],
[/^git$/, function() {
throw new CommandResult(_.escape("\
throw new CommandResult({
msg: _.escape("\
Git Version \n \
PCOTTLE.1.0 \
Usage: \n \
git <command> [<args>] \
"));
")
});
}]
];
};
@ -60,7 +62,7 @@ Command.prototype.getSandboxCommands = function() {
Command.prototype.parse = function(str) {
// first if the string is empty, they just want a blank line
if (!str.length) {
throw new CommandResult("");
throw new CommandResult({msg: ""});
}
// then check if it's one of our sandbox commands
@ -82,7 +84,9 @@ Command.prototype.parse = function(str) {
// see if begins with git
if (str.slice(0,3) !== 'git') {
throw new CommandProcessError('Git commands only, sorry!');
throw new CommandProcessError({
msg: 'Git commands only, sorry!'
});
}
// ok, we have a (probably) valid command. actually parse it
@ -106,9 +110,9 @@ Command.prototype.gitParse = function(str) {
}, this);
if (!matched) {
throw new CommandProcessError(
"Sorry, this demo does not support that git command: " + this.fullCommand
);
throw new CommandProcessError({
msg: "Sorry, this demo does not support that git command: " + this.fullCommand
});
}
this.optionParser = new OptionParser(this.method, this.options);
@ -170,7 +174,9 @@ OptionParser.prototype.explodeAndSet = function() {
if (part.slice(0,1) == '-') {
// it's an option, check supportedMap
if (this.supportedMap[part] === undefined) {
throw new CommandProcessError('The option "' + part + '" is not supported');
throw new CommandProcessError({
msg: 'The option "' + part + '" is not supported'
});
}
// go through and include all the next args until we hit another option or the end

View file

@ -20,10 +20,21 @@ var graphics = {
edgeStroke: 'rgba(94%, 96%, 98%, 0.5)', // '#EFF5FB',
nodeEdge: 'rgba(94%, 96%, 98%, 0.9)', // '#EFF5FB',
nodeFill: '#0066cc',
nodeRadius: 10,
// widths
nodeStrokeWidth: 15,
edgeWidth: 2,
// ref names
refFont: '14pt Courier New',
refFontFill: '#FFF',
// ref arrows
arrowFill: '#FFF',
arrowStroke: '#000',
arrowWidth: 4,
arrowHeadWidth: 5
};
function randomString(string_length) {

View file

@ -1,31 +1,35 @@
function CommandProcessError(msg) {
this.msg = msg;
var MyError = Backbone.Model.extend({
defaults: {
type: 'MyError',
msg: 'Unknown Error'
},
toString: function() {
return this.get('type') + ': ' + this.get('msg');
},
getMsg: function() {
return this.get('msg') || 'Unknown Error';
},
toResult: function() {
return this.get('msg').replace('\n', '</br>');
}
});
CommandProcessError.prototype.toString = function() {
return 'Command Process Error: ' + this.msg;
};
CommandProcessError.prototype.toResult = function() {
return this.msg.replace('\n', '</br>');
};
function CommandResult(msg) {
this.msg = msg;
var CommandProcessError = MyError.extend({
defaults: {
type: 'Command Process Error'
}
});
CommandResult.prototype.toString = function() {
return 'Command Result: ' + this.msg;
};
CommandResult.prototype.toResult = function() {
return this.msg.replace('\n', '</br>');
};
function GitError(msg) {
this.msg = msg;
var CommandResult = MyError.extend({
defaults: {
type: 'Command Result'
}
});
GitError.prototype.toString = function() {
return 'Git Error: ' + this.msg;
};
var GitError = MyError.extend({
defaults: {
type: 'Git Error'
}
});

View file

@ -13,6 +13,7 @@ function GitEngine() {
this.HEAD = null;
this.id_gen = 0;
this.branches = [];
this.collection = commitCollection;
// global variable to keep track of the options given
// along with the command call.
@ -27,6 +28,8 @@ function GitEngine() {
GitEngine.prototype.init = function() {
// make an initial commit and a master branch
this.rootCommit = new Commit({rootCommit: true});
commitCollection.add(this.rootCommit);
this.refs[this.rootCommit.get('id')] = this.rootCommit;
var master = this.makeBranch('master', this.rootCommit);
@ -50,10 +53,14 @@ GitEngine.prototype.getDetachedHead = function() {
GitEngine.prototype.validateBranchName = function(name) {
name = name.replace(/\s/g, '');
if (!/^[a-zA-Z0-9]+$/.test(name)) {
throw new Error('woah bad branch name!! This is not ok: ' + name);
throw new GitError({
msg: 'woah bad branch name!! This is not ok: ' + name
});
}
if (/[hH][eE][aA][dD]/.test(name)) {
throw new Error('branch name of "head" is ambiguous, dont name it that');
throw new GitError({
msg: 'branch name of "head" is ambiguous, dont name it that'
});
}
return name;
};
@ -61,7 +68,9 @@ GitEngine.prototype.validateBranchName = function(name) {
GitEngine.prototype.makeBranch = function(id, target) {
id = this.validateBranchName(id);
if (this.refs[id]) {
throw new Error('that branch id already exists!');
throw new GitError({
msg: 'that branch id already exists!'
});
}
var branch = new Branch({
@ -73,18 +82,34 @@ GitEngine.prototype.makeBranch = function(id, target) {
return branch;
};
GitEngine.prototype.getHead = function() {
return _.clone(this.HEAD);
};
GitEngine.prototype.getBranches = function() {
var toReturn = [];
_.each(this.branches, function(branch) {
toReturn.push({
id: branch.get('id'),
selected: this.HEAD.get('target') === branch
selected: this.HEAD.get('target') === branch,
target: branch.get('target')
});
}, this);
return toReturn;
};
GitEngine.prototype.printBranches = function() {
var branches = this.getBranches();
var result = '';
_.each(branches, function(branch) {
result += (branch.selected ? '* ' : '') + branch.id + '\n';
});
throw new CommandResult({
msg: result
});
};
GitEngine.prototype.logBranches = function() {
var branches = this.getBranches();
_.each(branches, function(branch) {
console.log((branch.selected ? '* ' : '') + branch.id);
@ -96,12 +121,15 @@ GitEngine.prototype.makeCommit = function(parent) {
parents: [parent]
});
this.refs[commit.get('id')] = commit;
this.collection.add(commit);
return commit;
};
GitEngine.prototype.acceptNoGeneralArgs = function() {
if (this.generalArgs.length) {
throw new GitError("That command accepts no general arguments");
throw new GitError({
msg: "That command accepts no general arguments"
});
}
};
@ -167,10 +195,14 @@ GitEngine.prototype.resolveStringRef = function(ref) {
}, this);
if (!startRef) {
throw new Error('unknown ref ' + ref);
throw new GitError({
msg: 'unknown ref ' + ref
});
}
if (!this.refs[startRef]) {
throw new Error('the ref ' + startRef +' does not exist.');
throw new GitError({
msg: 'the ref ' + startRef +' does not exist.'
});
}
var commit = this.getCommitFromRef(startRef);
@ -224,14 +256,18 @@ GitEngine.prototype.numBackFrom = function(commit, numBack) {
}
if (numBack !== 0 || pQueue.length == 0) {
throw new Error('exhausted search, sorry');
throw new GitError({
msg: "Sorry, I can't go that many commits back"
});
}
return pQueue.shift(0);
};
GitEngine.prototype.checkoutStarter = function() {
if (this.generalArgs.length != 1) {
throw new GitError('I expect one argument along with git checkout (dont reference files)');
throw new GitError({
msg: 'I expect one argument along with git checkout (dont reference files)'
});
}
this.checkout(this.generalArgs[0]);
@ -249,7 +285,9 @@ GitEngine.prototype.checkout = function(idOrTarget) {
var type = target.get('type');
if (type !== 'branch' && type !== 'commit') {
throw new Error('can only checkout branches and commits!');
throw new GitError({
msg: 'can only checkout branches and commits!'
});
}
this.HEAD.set('target', target);
@ -259,7 +297,9 @@ GitEngine.prototype.branchStarter = function() {
// handle deletion first
if (this.commandOptions['-d'] || this.commandOptions['-D']) {
if (!this.generalArgs.length) {
throw new GitError('I expect branch names when deleting');
throw new GitError({
msg: 'I expect branch names when deleting'
});
}
_.each(this.generalArgs, function(name) {
this.deleteBranch(name);
@ -269,11 +309,14 @@ GitEngine.prototype.branchStarter = function() {
var len = this.generalArgs.length;
if (len > 2) {
throw new GitError('git branch with more than two general args does not make sense!');
throw new GitError({
msg: 'git branch with more than two general args does not make sense!'
});
}
if (len == 0) {
throw new Error('going to implement eventually');
//TODO: print branches, etc
this.printBranches();
return;
}
@ -293,13 +336,19 @@ GitEngine.prototype.deleteBranch = function(name) {
// trying to delete, lets check our refs
var target = this.resolveId(name);
if (target.get('type') !== 'branch') {
throw new GitError("You can't delete things that arent branches with branch command");
throw new GitError({
msg: "You can't delete things that arent branches with branch command"
});
}
if (target.get('id') == 'master') {
throw new GitError("You can't delete the master branch!");
throw new GitError({
msg: "You can't delete the master branch!"
});
}
if (this.HEAD.get('target') === target) {
throw new GitError("Cannot delete the branch you are currently on");
throw new GitError({
msg: "Cannot delete the branch you are currently on"
});
}
var id = target.get('id');
@ -315,10 +364,10 @@ GitEngine.prototype.dispatch = function(commandObj) {
};
GitEngine.prototype.add = function() {
throw new Error(
"This demo is meant to demonstrate git branching, so don't worry about " +
throw new CommandResult({
msg: "This demo is meant to demonstrate git branching, so don't worry about " +
"adding / staging files. Just go ahead and commit away!"
);
});
};
GitEngine.prototype.execute = function(command, callback) {
@ -401,7 +450,6 @@ var Commit = Backbone.Model.extend({
initialize: function() {
this.validateAtInit();
this.addNodeToVisuals();
console.log('MAKING NEW COMMIT', this.get('id'));
_.each(this.get('parents'), function(parent) {
parent.get('children').push(this);

View file

@ -51,5 +51,7 @@
<script src="commandline.js"></script>
<script src="views.js"></script>
<script src="errors.js"></script>
<script src="collections.js"></script>
<script src="visuals.js"></script>
</body>
</html>

View file

@ -39,6 +39,7 @@ Renderer = function(canvas) {
ctx.clearRect(0,0, canvas.width, canvas.height);
particleSystem.eachEdge(this.drawEdge);
particleSystem.eachNode(this.drawNode);
events.trigger('drawGitVisuals', particleSystem, ctx, canvas);
},
resize: function(){

View file

@ -5,6 +5,7 @@ var events = _.clone(Backbone.Events);
var sys = null;
var graphicsEffects = {};
var gitEngine = null;
var gitVisuals = null;
$(document).ready(function(){
sys = arbor.ParticleSystem(4000, 500, 0.5, false, 55, 0.005, 'verlet');
@ -18,6 +19,7 @@ $(document).ready(function(){
});
gitEngine = new GitEngine();
gitVisuals = new GitVisuals();
var repulsionBreathe = function(r) {
sys.parameters({repulsion: r});
@ -40,9 +42,10 @@ Node.prototype.drawCircleNode = function(ctx, pt) {
ctx.strokeStyle = graphics.nodeEdge;
ctx.lineWidth = graphics.nodeStrokeWidth;
ctx.fillStyle = graphics.nodeFill;
var radius = graphics.nodeRadius;
ctx.beginPath();
ctx.arc(pt.x, pt.y, 10, 0, Math.PI*2, true);
ctx.arc(pt.x, pt.y, radius, 0, Math.PI*2, true);
ctx.closePath();
ctx.stroke();
ctx.fill();

View file

@ -7,7 +7,7 @@ var CommandLineView = Backbone.View.extend({
$.proxy(this.keyUp, this)
);
events.on('commandConsumed', _.bind(
events.on('commandReadyForProcess', _.bind(
this.parseOrCatch, this
));
},
@ -53,8 +53,8 @@ var CommandLineView = Backbone.View.extend({
processError: function(err) {
// in this demo, every command that's not a git command will
// throw an exception. Some of these errors might be just to
// short-circuit the normal programatic flow, so we handle them
// here
// short-circuit the normal programatic flow and print stuff,
// so we handle them here
if (err instanceof CommandProcessError) {
events.trigger('commandProcessError', err);
} else if (err instanceof CommandResult) {
@ -81,13 +81,13 @@ var CommandLineView = Backbone.View.extend({
}
this.index = -1;
events.trigger('commandConsumed', value);
events.trigger('commandSubmitted', value);
events.trigger('commandReadyForProcess', value);
},
parseOrCatch: function(value) {
try {
var command = new Command(value);
console.log(command);
events.trigger('gitCommandReady', command);
} catch (err) {
this.processError(err);
@ -97,7 +97,7 @@ var CommandLineView = Backbone.View.extend({
var CommandLineHistoryView = Backbone.View.extend({
initialize: function(options) {
events.on('commandConsumed', _.bind(
events.on('commandSubmitted', _.bind(
this.addCommand, this
));
@ -165,7 +165,8 @@ var CommandLineHistoryView = Backbone.View.extend({
},
commandResultPrint: function(err) {
if (!err.msg.length) {
if (!err.get('msg') || !err.get('msg').length) {
console.log(err);
// blank lines
return;
}

57
src/visuals.js Normal file
View file

@ -0,0 +1,57 @@
function GitVisuals() {
this.collection = commitCollection;
this.collection.on('change', _.bind(this.collectionChanged, this));
events.on('drawGitVisuals', _.bind(this.drawVisuals, this));
}
GitVisuals.prototype.drawVisuals = function(sys, ctx, canvas) {
// we need to draw refs here
var branches = gitEngine.getBranches();
var detachedHead = gitEngine.getDetachedHead();
var HEAD = gitEngine.getHead();
_.forEach(branches, _.bind(function(branch) {
// get the location of the arbor node and then somehow draw the ref to the side?
var node = branch.target.get('arborNode');
var nodePoint = sys.toScreen(node._p);
// text position
// TODO: better positioning of text here
var screenPoint = _.clone(nodePoint);
screenPoint.x += 100;
ctx.font = graphics.refFont;
ctx.fillStyle = graphics.refFontFill;
ctx.fillText(branch.id, screenPoint.x, screenPoint.y);
// also draw an arrow
var offset = Math.round(graphics.nodeRadius * 2.5);
this.drawArrow(ctx, screenPoint, nodePoint, graphics.arrowHeadWidth, offset);
}, this));
};
GitVisuals.prototype.drawArrow = function(ctx, start, end, headWidth, offset) {
// TODO only horizontal arrows for now, fix this later
var end = _.clone(end);
end.x += offset;
ctx.lineWidth = graphics.arrowWidth;
ctx.fillStyle = graphics.arrowFill;
ctx.strokeStyle = graphics.arrowStroke;
ctx.beginPath();
ctx.moveTo(start.x, start.y);
ctx.lineTo(end.x, end.y);
// now do the little arrow head
ctx.lineTo(end.x + headWidth, end.y + headWidth);
ctx.lineTo(end.x + headWidth, end.y - headWidth);
ctx.lineTo(end.x, end.y);
ctx.stroke();
};
GitVisuals.prototype.collectionChanged = function() {
// redo the algorithms
};