diff --git a/src/async.js b/src/async.js index 62ab3d88..025e75df 100644 --- a/src/async.js +++ b/src/async.js @@ -2,9 +2,69 @@ * Util classes */ +function CommandQueue() { + this.commands = []; + this.consumeTimeout = null; -/* - var e = sys.addEdge(node1, node2); + this.initialDelay = 400; +} + +CommandQueue.prototype.add = function(command) { + this.commands.push(command); + this.touchTimer(); +}; + +CommandQueue.prototype.touchTimer = function() { + if (this.consumeTimeout) { + return; + } + this.consumeTimeout = setTimeout(_.bind(function() { + this.next(); + }, this), this.initialDelay); +}; + +CommandQueue.prototype.reset = function() { + this.consumeTimeout = null; +}; + +CommandQueue.prototype.next = function() { + if (this.commands.length == 0) { + this.reset(); + return; + } + + // execute the top command by passing it into the engine + var toExecute = this.commands.shift(0); + var callback = _.bind(function() { + this.next(); + }, this); + gitEngine.execute(toExecute, callback); +}; + + + +/****************** + * Planning: + + here is the major flow: + + someone types in a command -> + make a new command object. if error, give immediate feedback, dont append to queue + if not error -> + append command object to queue + + + Command Queue -> + consume commands at a certain rate (either instantly if just added, or with an interval + Execute command -> (usually a git engine thing) + Wait for git engine command to finish + when done, execute next command (if more) + + so two levels of Async-ness: + command queue slowly consumes commands + + GitEngine executes commands, which will have async bits to them (such as popping off commits for a + rebase) */ function Scheduler(closures, options) { diff --git a/src/git.js b/src/git.js index 189257c1..c78eb428 100644 --- a/src/git.js +++ b/src/git.js @@ -1,9 +1,98 @@ + +// backbone or something uses _.uniqueId, so we make our own here +var uniqueId = (function() { + var n = 0; + return function(prepend) { + return prepend? prepend + n++ : n++; + }; +})(); + function GitEngine() { - this.detachedHead = false; + this.rootCommit = null; + this.refs = {}; + this.HEAD = null; + this.id_gen = 0; + + this.init(); } -GitEngine.prototype.commit = function() { +GitEngine.prototype.init = function() { + // make an initial commit and a master branch + this.rootCommit = new Commit({rootCommit: true}); + this.refs[this.rootCommit.get('id')] = this.rootCommit; + var master = this.makeBranch('master', this.rootCommit); + this.HEAD = new Ref({ + id: 'HEAD', + target: master + }); + this.refs[this.HEAD.get('id')] = this.HEAD; + + // commit once to get things going + this.commit(); +}; + +GitEngine.prototype.getDetachedHead = function() { + // detached head is if HEAD points to a commit instead of a branch... + var target = this.HEAD.get('target'); + var targetType = target.get('type'); + return targetType !== 'branch'; +}; + +GitEngine.prototype.makeBranch = function(id, target) { + var branch = new Branch({ + target: target, + id: id + }); + this.refs[branch.get('id')] = branch; + return branch; +}; + +GitEngine.prototype.makeCommit = function(parent) { + var commit = new Commit({ + parents: [parent] + }); + this.refs[commit.get('id')] = commit; + return commit; +}; + +GitEngine.prototype.commit = function() { + if (this.getDetachedHead()) { + throw new Error('you are in detached head state!'); + } + + // commits are always done on head + var targetBranch = this.HEAD.get('target'); + var targetCommit = targetBranch.get('target'); + + var newCommit = this.makeCommit(targetCommit); + targetBranch.set('target', newCommit); +}; + +GitEngine.prototype.resolveId = function(idOrTarget) { + if (typeof idOrTarget !== 'string') { + return idOrTarget; + } + return this.resolveStringRef(idOrTarget); +}; + +GitEngine.prototype.resolveStringRef = function(ref) { + var target = this.refs[ref]; + if (!target) { + throw new Error('that ref ' + idOrTarget + ' does not exist'); + } + return target; +}; + +GitEngine.prototype.checkout = function(idOrTarget) { + var target = this.resolveId(idOrTarget); + var type = target.get('type'); + + if (type !== 'branch' && type !== 'commit') { + throw new Error('can only checkout branches and commits!'); + } + + this.HEAD.set('target', target); }; GitEngine.prototype.execute = function(command, callback) { @@ -30,91 +119,68 @@ GitEngine.prototype.getClosuresForCommand = function(command) { return closures; }; -var Commit = Backbone.Model.extend({ +var Ref = Backbone.Model.extend({ initialize: function() { - // validation / defaults + if (!this.get('target')) { + throw new Error('must be initialized with target'); + } if (!this.get('id')) { - this.set('id', _.uniqueId('C')); - } - if (!this.get('parentCommit') && !this.get('rootCommit')) { - throw new Error('needs parent commit'); + throw new Error('must be given an id'); } + this.set('type', 'general ref'); + }, - this.set('node', sys.addNode(this.get('id'))); - - if (this.get('rootCommit')) { - // TODO -- fix this node in place - // this.get('node').fixed = true; - // this.get('node').p = {x: 0, y: 0}; - } else { - var parentNode = this.get('parentCommit').get('node'); - sys.addEdge(parentNode, this.get('node')); - } + toString: function() { + return 'a ' + this.get('type') + 'pointing to ' + String(this.get('target')); } }); - -function CommandQueue() { - this.commands = []; - this.consumeTimeout = null; - - this.initialDelay = 400; -} - -CommandQueue.prototype.add = function(command) { - this.commands.push(command); - this.touchTimer(); -}; - -CommandQueue.prototype.touchTimer = function() { - if (this.consumeTimeout) { - return; +var Branch = Ref.extend({ + initialize: function() { + Ref.prototype.initialize.call(this); + this.set('type', 'branch'); } - this.consumeTimeout = setTimeout(_.bind(function() { - this.next(); - }, this), this.initialDelay); -}; +}); -CommandQueue.prototype.reset = function() { - this.consumeTimeout = null; -}; +var Commit = Backbone.Model.extend({ + validateAtInit: function() { + if (!this.get('id')) { + this.set('id', uniqueId('C')); + } + this.set('type', 'commit'); -CommandQueue.prototype.next = function() { - if (this.commands.length == 0) { - this.reset(); - return; + // root commits have no parents + if (this.get('rootCommit')) { + this.set('parents', []); + } else { + if (!this.get('parents') || !this.get('parents').length) { + throw new Error('needs parents'); + } + } + }, + + addNodeToVisuals: function() { + // TODO: arbor stuff + // this.set('node', sys.addNode(this.get('id'))); + }, + + addEdgeToVisuals: function(parent) { + + }, + + initialize: function() { + this.validateAtInit(); + this.addNodeToVisuals(); + console.log('MAKING NEW COMMIT', this.get('id')); + + + _.each(this.get('parents'), function(parent) { + if (parent.get('child')) { + console.warn('overwriting child for ', parent, ' to this', this); + } + parent.set('child', this); + this.addEdgeToVisuals(parent); + }, this); } +}); - // execute the top command by passing it into the engine - var toExecute = this.commands.shift(0); - var callback = _.bind(function() { - this.next(); - }, this); - gitEngine.execute(toExecute, callback); -}; - - - -/****************** - * Planning: - - here is the major flow: - - someone types in a command -> - make a new command object. if error, give immediate feedback, dont append to queue - if not error -> - append command object to queue - - - Command Queue -> - consume commands at a certain rate (either instantly if just added, or with an interval - Execute command -> (usually a git engine thing) - Wait for git engine command to finish - when done, execute next command (if more) - - so two levels of Async-ness: - command queue slowly consumes commands - - GitEngine executes commands, which will have async bits to them (such as popping off commits for a - rebase) -*/ diff --git a/src/legacy.js b/src/legacy.js index ca0b1f36..85c6de8d 100644 --- a/src/legacy.js +++ b/src/legacy.js @@ -58,7 +58,7 @@ Renderer = function(canvas) { var p = {x:e.pageX-pos.left, y:e.pageY-pos.top}; selected = nearest = dragged = particleSystem.nearest(p); - if (selected.node !== null){ + if (selected && selected.node !== null){ dragged.node.tempMass = constants.clickDragMass; dragged.node.fixed = true; } diff --git a/src/mine.js b/src/mine.js index b4b59cf0..808f9e1e 100644 --- a/src/mine.js +++ b/src/mine.js @@ -7,12 +7,12 @@ var graphicsEffects = {}; var gitEngine = null; $(document).ready(function(){ - gitEngine = new GitEngine(); ee = new EventEmitter(); - sys = arbor.ParticleSystem(4000, 500, 0.5, false, 55, 0.005, 'verlet'); sys.renderer = Renderer('#viewport'); + gitEngine = new GitEngine(); + var repulsionBreathe = function(r) { sys.parameters({repulsion: r}); };