Skip to main content
shorter
Source Link
mathheadinclouds
  • 3.6k
  • 2
  • 28
  • 40

I made a fiddlefiddle implementing (essentially) above ideas outlined by iman. Here is how it looks when you mouse over the second ipsum in return ipsum*ipsum - ...

Here is the main portion of the code from the fiddle:

function initCodeContainer(sourceString){
    codeSpans = {};
    activeSpan = null;
    while (codeContainer.lastChild) { codeContainer.removeChild(codeContainer.lastChild); }
    ast = esprima.parse(sourceString, {range: true, sourceType: 'script'});
    analysis = escope.analyze(ast);
    var positionsObj = {};
    positionsObj[0] = null;
    positionsObj[sourceString.length] = null;
    estraverse.traverse(ast, {
        enter: function(node, parent){
            positionsObj[node.range[0]] = null;
            positionsObj[node.range[1]] = null;
        }
    });
    var positions = Object.keys(positionsObj).map(function(p){ return +p; });
    var i;
    for (i=0; i<positions.length-1; i++){
        var startPos = positions[i];
        var endPos = positions[i+1];
        var codePortion = sourceString.slice(startPos, endPos);
        var span = document.createElement('span');
        span.textContent = codePortion;
        codeContainer.appendChild(span);
        span.dataset.sourceFrom = startPos;
        span.dataset.sourceTo   = endPos;
        span.addEventListener('click', spanClick);
        span.addEventListener('mouseenter', spanMouseEnter);
        codeSpans[startPos] = span;
    }
  while (swatchesParent.lastChild) { swatchesParent.removeChild(swatchesParent.lastChild); }
  for (i=0; i<analysis.scopes.length; i++){
    if (i >= colors.length){
      colors[i] = generateColor(i);
    }
    var div = document.createElement('div');
    div.classList.add('swatch');
    div.style.backgroundColor = colors[i];
    div.textContent = i;
    swatchesParent.appendChild(div);
  }

}

howHow it works

I made a fiddle implementing (essentially) above ideas outlined by iman. Here is how it looks when you mouse over the second ipsum in return ipsum*ipsum - ...

Here is the main portion of the code from the fiddle:

function initCodeContainer(sourceString){
    codeSpans = {};
    activeSpan = null;
    while (codeContainer.lastChild) { codeContainer.removeChild(codeContainer.lastChild); }
    ast = esprima.parse(sourceString, {range: true, sourceType: 'script'});
    analysis = escope.analyze(ast);
    var positionsObj = {};
    positionsObj[0] = null;
    positionsObj[sourceString.length] = null;
    estraverse.traverse(ast, {
        enter: function(node, parent){
            positionsObj[node.range[0]] = null;
            positionsObj[node.range[1]] = null;
        }
    });
    var positions = Object.keys(positionsObj).map(function(p){ return +p; });
    var i;
    for (i=0; i<positions.length-1; i++){
        var startPos = positions[i];
        var endPos = positions[i+1];
        var codePortion = sourceString.slice(startPos, endPos);
        var span = document.createElement('span');
        span.textContent = codePortion;
        codeContainer.appendChild(span);
        span.dataset.sourceFrom = startPos;
        span.dataset.sourceTo   = endPos;
        span.addEventListener('click', spanClick);
        span.addEventListener('mouseenter', spanMouseEnter);
        codeSpans[startPos] = span;
    }
  while (swatchesParent.lastChild) { swatchesParent.removeChild(swatchesParent.lastChild); }
  for (i=0; i<analysis.scopes.length; i++){
    if (i >= colors.length){
      colors[i] = generateColor(i);
    }
    var div = document.createElement('div');
    div.classList.add('swatch');
    div.style.backgroundColor = colors[i];
    div.textContent = i;
    swatchesParent.appendChild(div);
  }

}

how it works

I made a fiddle implementing (essentially) above ideas outlined by iman. Here is how it looks when you mouse over the second ipsum in return ipsum*ipsum - ...

How it works

Source Link
mathheadinclouds
  • 3.6k
  • 2
  • 28
  • 40

I made a fiddle implementing (essentially) above ideas outlined by iman. Here is how it looks when you mouse over the second ipsum in return ipsum*ipsum - ...

enter image description here

The variables which are in scope are highlighted where they are declared (with different colors for different scopes). The lorem with red border is a shadowed variable (not in scope, but be in scope if the other lorem further down the tree wouldn't be there.)

I'm using esprima library to parse the JavaScript, and estraverse, escodegen, escope (utility libraries on top of esprima.) The 'heavy lifting' is done all by those libraries (the most complex being esprima itself, of course.)

Here is the main portion of the code from the fiddle:

function initCodeContainer(sourceString){
    codeSpans = {};
    activeSpan = null;
    while (codeContainer.lastChild) { codeContainer.removeChild(codeContainer.lastChild); }
    ast = esprima.parse(sourceString, {range: true, sourceType: 'script'});
    analysis = escope.analyze(ast);
    var positionsObj = {};
    positionsObj[0] = null;
    positionsObj[sourceString.length] = null;
    estraverse.traverse(ast, {
        enter: function(node, parent){
            positionsObj[node.range[0]] = null;
            positionsObj[node.range[1]] = null;
        }
    });
    var positions = Object.keys(positionsObj).map(function(p){ return +p; });
    var i;
    for (i=0; i<positions.length-1; i++){
        var startPos = positions[i];
        var endPos = positions[i+1];
        var codePortion = sourceString.slice(startPos, endPos);
        var span = document.createElement('span');
        span.textContent = codePortion;
        codeContainer.appendChild(span);
        span.dataset.sourceFrom = startPos;
        span.dataset.sourceTo   = endPos;
        span.addEventListener('click', spanClick);
        span.addEventListener('mouseenter', spanMouseEnter);
        codeSpans[startPos] = span;
    }
  while (swatchesParent.lastChild) { swatchesParent.removeChild(swatchesParent.lastChild); }
  for (i=0; i<analysis.scopes.length; i++){
    if (i >= colors.length){
      colors[i] = generateColor(i);
    }
    var div = document.createElement('div');
    div.classList.add('swatch');
    div.style.backgroundColor = colors[i];
    div.textContent = i;
    swatchesParent.appendChild(div);
  }

}

how it works

ast = esprima.parse(sourceString, {range: true, sourceType: 'script'});

makes the abstract syntax tree. Then,

analysis = escope.analyze(ast);

generates a complex data structure encapsulating information about all the scopes in the program. The rest is gathering together the information encoded in that analysis object (and the abstract syntax tree itself), and making an interactive coloring scheme out of it.

So the correct answer is actually not "no", but "yes, but". The "but" being a big one: you basically have to rewrite significant parts of the chrome browser (and it's devtools) in JavaScript. JavaScript is a Turing complete language, so of course that is possible, in principle. What is impossible is doing the whole thing without using the entirety of your source code (as a string) and then doing highly complex stuff with that.