Skip to content

Instantly share code, notes, and snippets.

@senocular
Created April 18, 2016 14:50
Show Gist options
  • Save senocular/b5f7bffc1769620b21eb57dd6eb2667f to your computer and use it in GitHub Desktop.
Save senocular/b5f7bffc1769620b21eb57dd6eb2667f to your computer and use it in GitHub Desktop.
Simple way to step through a code block with execution for demonstration purposes
class FunctionSequencer {
constructor (genFunc) {
if (Object.prototype.toString.call(genFunc) !== '[object GeneratorFunction]') {
throw new TypeError('Failed to create FunctionSequencer instance: parameter 1 is not of type \'GeneratorFunction\'.');
}
this._genFunc = genFunc;
this._codeBlocks = this._parseBlocks(this._genFunc);
// when attached to a DOM element
this._container = null;
this._codeEls = [];
this._currentClass = 'highlight';
}
_parseBlocks (genFunc) {
var reNotWhite = /[^\s]+/;
var reNewlineCaps = /^[\r\n]+|[\r\n]+$/g;
// get function body text
var str = genFunc.toString();
var starti = str.indexOf('{') + 1;
var endi = str.indexOf('}');
var span = str.slice(starti, endi);
// capture individual blocks of code between yields
return span
.split(/(^\s*(?:yield|return).*$)/m)
.filter((code, index) => reNotWhite.test(code) && index % 2 === 0)
.map(code => code.replace(reNewlineCaps, ''));
}
attach (container) {
this._container = container;
this._codeEls = [];
// one element added for each block of code
for (var code of this._codeBlocks) {
var el = document.createElement('pre');
this._container.appendChild(el);
el.textContent = code;
this._codeEls.push(el);
}
}
call () {
var iter = this._genFunc();
for (var annotation of iter);
}
* [Symbol.iterator] () {
var index = 0;
var el;
var code;
var stepThroughCode = () => {
code = this._codeBlocks[index];
if (el) el.classList.remove(this._currentClass);
el = this._codeEls[index];
if (el) el.classList.add(this._currentClass);
index++;
}
var iter = this._genFunc();
do {
var next = iter.next();
var annotation = next.value;
stepThroughCode();
yield { annotation, code };
} while (!next.done);
// clears on done
stepThroughCode();
}
entries () {
return this[Symbol.iterator]();
}
}
function * foo () {
console.log(1);
yield 'first';
console.log(2.1);
console.log(2.2);
// comment
yield 'second';
console.log(3);
return 'last'
}
var container = document.createElement('div');
document.body.appendChild(container);
var seq = new FunctionSequencer(foo);
seq.attach(container);
//seq.call(); // to call the function without stepping through it
var iter = seq.entries();
document.addEventListener('click', function() {
var results = iter.next();
if (results.done) {
console.log('DONE');
iter = seq.entries();
} else {
console.log(results.value.annotation);
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment