or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

array-diffing.mdcharacter-diffing.mdcss-diffing.mdcustom-diffing.mdformat-conversion.mdindex.mdjson-diffing.mdline-diffing.mdpatch-application.mdpatch-creation.mdpatch-utilities.mdsentence-diffing.mdword-diffing.md
tile.json

format-conversion.mddocs/

Format Conversion

Convert diff results to XML and Google diff-match-patch formats. Enables integration with other diff tools and systems that require specific output formats.

Capabilities

convertChangesToXML Function

Converts change objects to XML format with <ins> and <del> tags for added and removed content.

/**
 * Convert change objects to XML format
 * @param changes - Array of change objects from any diff function
 * @returns String containing XML with <ins> and <del> tags
 */
function convertChangesToXML(changes);

convertChangesToDMP Function

Converts change objects to Google diff-match-patch format using operation codes.

/**
 * Convert change objects to diff-match-patch format
 * @param changes - Array of change objects from any diff function
 * @returns Array of [operation, text] tuples where operation is -1 (delete), 0 (equal), or 1 (insert)
 */
function convertChangesToDMP(changes);

Usage Examples

XML Format Conversion

import { diffWords, convertChangesToXML } from "diff";

// Create a diff
const changes = diffWords("Hello world", "Hello universe");

// Convert to XML
const xmlOutput = convertChangesToXML(changes);
console.log(xmlOutput);
// Output: "Hello <del>world</del><ins>universe</ins>"

// Example with line diff
const lineChanges = diffLines(
  "line 1\nline 2\nline 3",
  "line 1\nmodified line 2\nline 3"
);

const xmlLines = convertChangesToXML(lineChanges);
console.log(xmlLines);
// Output: "line 1\n<del>line 2\n</del><ins>modified line 2\n</ins>line 3"

HTML Rendering

import { diffChars, convertChangesToXML } from "diff";

function renderDiffAsHTML(oldText, newText) {
  const changes = diffChars(oldText, newText);
  const xmlDiff = convertChangesToXML(changes);
  
  // Style the diff with CSS
  return `
    <div class="diff-container">
      <style>
        .diff-container ins { background-color: #d4edda; text-decoration: none; }
        .diff-container del { background-color: #f8d7da; text-decoration: line-through; }
      </style>
      ${xmlDiff}
    </div>
  `;
}

// Usage
const htmlDiff = renderDiffAsHTML("Hello world", "Hello universe");
document.getElementById('diff-output').innerHTML = htmlDiff;

Google diff-match-patch Format

import { diffWords, convertChangesToDMP } from "diff";

// Create a diff
const changes = diffWords("The quick brown fox", "The slow brown fox");

// Convert to DMP format
const dmpFormat = convertChangesToDMP(changes);
console.log(dmpFormat);
// Output: [
//   [0, "The "],
//   [-1, "quick"],
//   [1, "slow"],
//   [0, " brown fox"]
// ]

// DMP operation codes:
// -1 = DELETE (removed text)
//  0 = EQUAL (unchanged text)  
//  1 = INSERT (added text)

Advanced Usage

Integration with diff-match-patch Library

import { diffLines, convertChangesToDMP } from "diff";
// Assuming you also have the diff-match-patch library installed
// import { diff_match_patch } from "diff-match-patch";

function integrateWithDiffMatchPatch(oldText, newText) {
  // Use jsdiff for initial comparison
  const changes = diffLines(oldText, newText);
  const dmpFormat = convertChangesToDMP(changes);
  
  // Now you can use the DMP format with Google's diff-match-patch library
  // const dmp = new diff_match_patch();
  // const patches = dmp.patch_make(dmpFormat);
  // const patchText = dmp.patch_toText(patches);
  
  return {
    jsdiffChanges: changes,
    dmpFormat: dmpFormat,
    // patches: patches,
    // patchText: patchText
  };
}

// Usage
const integration = integrateWithDiffMatchPatch(originalCode, modifiedCode);
console.log("DMP format:", integration.dmpFormat);

Rich Text Editor Integration

import { diffWords, convertChangesToXML } from "diff";

function createRichTextDiff(originalContent, newContent) {
  const changes = diffWords(originalContent, newContent);
  const xmlDiff = convertChangesToXML(changes);
  
  // Convert to rich text editor format (example for a hypothetical editor)
  return xmlDiff
    .replace(/<del>/g, '<span class="deletion">')
    .replace(/<\/del>/g, '</span>')
    .replace(/<ins>/g, '<span class="insertion">')
    .replace(/<\/ins>/g, '</span>');
}

// Usage
const richTextDiff = createRichTextDiff(
  "The original document text",
  "The modified document text"
);

Multiple Format Export

import { diffLines, convertChangesToXML, convertChangesToDMP } from "diff";

function exportDiffInMultipleFormats(oldContent, newContent, filename) {
  const changes = diffLines(oldContent, newContent);
  
  const formats = {
    xml: convertChangesToXML(changes),
    dmp: convertChangesToDMP(changes),
    json: JSON.stringify(changes, null, 2),
    summary: {
      totalChanges: changes.filter(c => c.added || c.removed).length,
      additions: changes.filter(c => c.added).length,
      deletions: changes.filter(c => c.removed).length,
      unchanged: changes.filter(c => !c.added && !c.removed).length
    }
  };
  
  return formats;
}

// Usage
const multiFormat = exportDiffInMultipleFormats(oldFile, newFile, "changes");
console.log("XML format:", multiFormat.xml);
console.log("DMP format:", multiFormat.dmp);
console.log("Summary:", multiFormat.summary);

Web-based Diff Viewer

import { diffLines, convertChangesToXML } from "diff";

function createWebDiffViewer(oldText, newText, options = {}) {
  const changes = diffLines(oldText, newText);
  const xmlDiff = convertChangesToXML(changes);
  
  const viewerHTML = `
<!DOCTYPE html>
<html>
<head>
  <title>Diff Viewer</title>
  <style>
    body { font-family: monospace; line-height: 1.4; }
    .diff-container { 
      border: 1px solid #ddd; 
      padding: 20px; 
      background: #f9f9f9; 
    }
    ins { 
      background-color: #d4edda; 
      color: #155724; 
      text-decoration: none; 
      padding: 2px 4px;
      border-radius: 3px;
    }
    del { 
      background-color: #f8d7da; 
      color: #721c24; 
      text-decoration: line-through;
      padding: 2px 4px;
      border-radius: 3px;
    }
    .stats {
      margin-bottom: 20px;
      padding: 10px;
      background: #e9ecef;
      border-radius: 5px;
    }
  </style>
</head>
<body>
  <div class="stats">
    <h3>Diff Statistics</h3>
    <p>Changes: ${changes.filter(c => c.added || c.removed).length}</p>
    <p>Lines added: ${changes.filter(c => c.added).length}</p>
    <p>Lines removed: ${changes.filter(c => c.removed).length}</p>
  </div>
  <div class="diff-container">
    <h3>Changes</h3>
    <pre>${xmlDiff}</pre>
  </div>
</body>
</html>`;
  
  return viewerHTML;
}

// Usage
const diffViewer = createWebDiffViewer(oldCode, newCode);
// Save to file or serve via web server

Custom XML Styling

import { diffWords, convertChangesToXML } from "diff";

function convertToCustomXML(changes, options = {}) {
  const {
    insertTag = 'add',
    deleteTag = 'remove',
    attributes = {},
    escapeHtml = true
  } = options;
  
  let result = '';
  
  for (const change of changes) {
    let value = change.value;
    
    if (escapeHtml) {
      value = value
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;');
    }
    
    if (change.added) {
      const attrs = Object.entries(attributes)
        .map(([k, v]) => `${k}="${v}"`)
        .join(' ');
      result += `<${insertTag}${attrs ? ' ' + attrs : ''}>${value}</${insertTag}>`;
    } else if (change.removed) {
      const attrs = Object.entries(attributes)
        .map(([k, v]) => `${k}="${v}"`)
        .join(' ');
      result += `<${deleteTag}${attrs ? ' ' + attrs : ''}>${value}</${deleteTag}>`;
    } else {
      result += value;
    }
  }
  
  return result;
}

// Usage
const changes = diffWords("old text", "new text");
const customXML = convertToCustomXML(changes, {
  insertTag: 'insertion',
  deleteTag: 'deletion',
  attributes: { class: 'diff-change', timestamp: Date.now() },
  escapeHtml: true
});

Markdown Diff Format

import { diffLines, convertChangesToXML } from "diff";

function convertToMarkdownDiff(oldText, newText) {
  const changes = diffLines(oldText, newText);
  
  let markdown = '```diff\n';
  
  changes.forEach(change => {
    const lines = change.value.split('\n');
    lines.forEach(line => {
      if (!line && lines.length === 1) return; // Skip empty single lines
      
      if (change.added) {
        markdown += `+ ${line}\n`;
      } else if (change.removed) {
        markdown += `- ${line}\n`;
      } else {
        markdown += `  ${line}\n`;
      }
    });
  });
  
  markdown += '```';
  return markdown;
}

// Usage
const markdownDiff = convertToMarkdownDiff(oldContent, newContent);
console.log("Markdown diff:\n", markdownDiff);

Performance Considerations

import { diffLines, convertChangesToXML, convertChangesToDMP } from "diff";

function efficientFormatConversion(oldText, newText, targetFormats) {
  // Compute diff once
  const changes = diffLines(oldText, newText);
  
  // Convert to requested formats
  const results = {};
  
  if (targetFormats.includes('xml')) {
    results.xml = convertChangesToXML(changes);
  }
  
  if (targetFormats.includes('dmp')) {
    results.dmp = convertChangesToDMP(changes);
  }
  
  if (targetFormats.includes('json')) {
    results.json = JSON.stringify(changes);
  }
  
  if (targetFormats.includes('stats')) {
    results.stats = {
      additions: changes.filter(c => c.added).length,
      deletions: changes.filter(c => c.removed).length,
      unchanged: changes.filter(c => !c.added && !c.removed).length
    };
  }
  
  return results;
}

// Usage - only compute what's needed
const formats = efficientFormatConversion(text1, text2, ['xml', 'stats']);