diff | | Cell 1 | Search

This code snippet compares two code snippets, calculates their differences, and presents the results as a visually highlighted HTML diff using Prism.js for syntax highlighting.

Run example

npm run import -- "diff"

diff

// npm install diff --save
var fs = require('fs');
var {JSDOM} = require('jsdom');
var JsDiff = require('diff');
var importer = require('../Core');
var prismPlugin = '../node_modules/prismjs/plugins/keep-markup/prism-keep-markup.js';
//TODO: fix diff fs.readFileSync(prismPlugin).toString()
function htmlEntities(str) { return str.replace(/[\u00A0-\u9999<>\&]/gim, i => '&#' + i.charCodeAt(0) + ';'); }

function prismHighlightHtml(code) {
    var Prism = require('prismjs');
    var dom = new JSDOM('<body>' + code + '</body>');
    dom.window.document.createRange = () => ({
        createContextualFragment: str => JSDOM.fragment(str)
    });
    const ctx = {
        __filename: prismPlugin,
        Prism: Prism,
        document: dom.window.document,
    };
    ctx.self = ctx;
    return Prism.highlightElement(dom.window.document.body, Prism.languages.javascript);
}

function diffTwoTexts(left, right) {
    var diff = JsDiff.diffWords(left, right);
    var code = '';
    for (var i = 0; i < diff.length; i++) {
        if (diff[i].added && diff[i + 1] && diff[i + 1].removed) {
            var swap = diff[i];
            diff[i] = diff[i + 1];
            diff[i + 1] = swap;
        }

        if (diff[i].removed) {
            code += '<span class="del">' + htmlEntities(diff[i].value) + '</span>';
        } else if (diff[i].added) {
            code += '<span class="ins">' + htmlEntities(diff[i].value) + '</span>';
        } else {
            code += '<span>' + htmlEntities(diff[i].value) + '</span>';
        }
    }
    return `
<style>

.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
    color: slategray;
}

.token.punctuation {
    color: #999;
}

.namespace {
    opacity: .7;
}

.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
    color: #905;
}

.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
    color: #690;
}

.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
    color: #a67f59;
    background: hsla(0, 0%, 100%, .5);
}

.token.atrule,
.token.attr-value,
.token.keyword {
    color: #07a;
}

.token.function {
    color: #DD4A68;
}

.token.regex,
.token.important,
.token.variable {
    color: #e90;
}

.token.important,
.token.bold {
    font-weight: bold;
}

.token.italic {
    font-style: italic;
}

.token.entity {
    cursor: help;
}
.del {
    text-decoration: none;
    background: #fadad7;
}
.ins {
    background: #eaf2c2;
    text-decoration: none;
}
</style>
<pre>` + prismHighlightHtml(code) + `</pre>`;
}
module.exports = diffTwoTexts;
diffTwoTexts;

What the code could have been:

const fs = require('fs');
const { JSDOM } = require('jsdom');
const JsDiff = require('diff');
const Prism = require('prismjs');
const prismPlugin = require('../node_modules/prismjs/plugins/keep-markup/prism-keep-markup.js');
const importer = require('../Core');

// Function to replace special characters with HTML entities
function htmlEntities(str) {
  return str.replace(/[\u00A0-\u9999<>\&]/gim, i => '&#' + i.charCodeAt(0) + ';');
}

// Function to highlight PrismJS code
function prismHighlightHtml(code) {
  const dom = new JSDOM('<body>' + code + '</body>');
  dom.window.document.createRange = () => ({
    createContextualFragment: str => JSDOM.fragment(str)
  });
  const ctx = {
    __filename: prismPlugin,
    Prism,
    document: dom.window.document,
  };
  ctx.self = ctx;
  return Prism.highlightElement(dom.window.document.body, Prism.languages.javascript);
}

// Function to diff two texts
function diffTwoTexts(left, right) {
  const diff = JsDiff.diffWords(left, right);
  let code = '';

  // Swap removed and added lines to ensure correct order
  diff.forEach((part, index, array) => {
    if (part.added && array[index + 1] && array[index + 1].removed) {
      [part, array[index + 1]] = [array[index + 1], part];
    }
  });

  diff.forEach((part) => {
    if (part.removed) {
      code += `<span class="del">${htmlEntities(part.value)}</span>`;
    } else if (part.added) {
      code += `<span class="ins">${htmlEntities(part.value)}</span>`;
    } else {
      code += `<span>${htmlEntities(part.value)}</span>`;
    }
  });

  const css = `
  <style>
    /* PrismJS styles */
   .token.comment,
   .token.prolog,
   .token.doctype,
   .token.cdata {
      color: slategray;
    }
   .token.punctuation {
      color: #999;
    }
   .namespace {
      opacity:.7;
    }
   .token.property,
   .token.tag,
   .token.boolean,
   .token.number,
   .token.constant,
   .token.symbol,
   .token.deleted {
      color: #905;
    }
   .token.selector,
   .token.attr-name,
   .token.string,
   .token.char,
   .token.builtin,
   .token.inserted {
      color: #690;
    }
   .token.operator,
   .token.entity,
   .token.url,
   .language-css.token.string,
   .style.token.string {
      color: #a67f59;
      background: hsla(0, 0%, 100%,.5);
    }
   .token.atrule,
   .token.attr-value,
   .token.keyword {
      color: #07a;
    }
   .token.function {
      color: #DD4A68;
    }
   .token.regex,
   .token.important,
   .token.variable {
      color: #e90;
    }
   .token.important,
   .token.bold {
      font-weight: bold;
    }
   .token.italic {
      font-style: italic;
    }
   .token.entity {
      cursor: help;
    }
   .del {
      text-decoration: none;
      background: #fadad7;
    }
   .ins {
      background: #eaf2c2;
      text-decoration: none;
    }
  </style>
  `;

  return `${css}<pre>${prismHighlightHtml(code)}</pre>`;
}

module.exports = diffTwoTexts;

This code snippet focuses on comparing and highlighting code diffs using D3.js and Prism.js.

Here's a breakdown:

  1. Dependencies:

  2. Helper Functions:

  3. Diff Calculation:

In essence, this code snippet provides a way to compare two code snippets, highlight the differences, and present them in a visually understandable format using HTML and CSS.