Migrate diff-match-patch to a modern fork (#9511)

* Migrate to diff-match-patch-es & update api

* Update acknowledgements

* Update change notes

* Fix editcost attribute not working

* Make library compatible with ES2017
This commit is contained in:
XLBilly
2026-01-04 19:56:25 +08:00
committed by GitHub
parent c4c60933f4
commit c6906120d8
7 changed files with 912 additions and 1735 deletions

View File

@@ -5,3 +5,4 @@ TiddlyWiki incorporates code from these fine OpenSource projects:
* [[The Stanford Javascript Crypto Library|http://bitwiseshiftleft.github.io/sjcl/]]
* [[The Jasmine JavaScript Test Framework|https://jasmine.github.io/]]
* [[modern-normalize by Sindre Sorhus|https://github.com/sindresorhus/modern-normalize]]
* [[diff-match-patch-es by antfu|https://github.com/antfu/diff-match-patch-es]]

View File

@@ -71,107 +71,103 @@ exports.join = makeStringReducingOperator(
},null
);
var dmp = require("$:/core/modules/utils/diff-match-patch/diff_match_patch.js");
const dmp = require("$:/core/modules/utils/diff-match-patch/diff_match_patch.js");
exports.levenshtein = makeStringBinaryOperator(
function(a,b) {
var dmpObject = new dmp.diff_match_patch(),
diffs = dmpObject.diff_main(a,b);
return [dmpObject.diff_levenshtein(diffs) + ""];
const diffs = dmp.diffMain(a,b);
return [dmp.diffLevenshtein(diffs).toString()];
}
);
// these two functions are adapted from https://github.com/google/diff-match-patch/wiki/Line-or-Word-Diffs
function diffLineWordMode(text1,text2,mode) {
var dmpObject = new dmp.diff_match_patch();
var a = diffPartsToChars(text1,text2,mode);
var lineText1 = a.chars1;
var lineText2 = a.chars2;
var lineArray = a.lineArray;
var diffs = dmpObject.diff_main(lineText1,lineText2,false);
dmpObject.diff_charsToLines_(diffs,lineArray);
var diffs = dmp.diffMain(lineText1,lineText2,false);
dmp.diffCharsToLines(diffs,lineArray);
return diffs;
}
function diffPartsToChars(text1,text2,mode) {
var lineArray = [];
var lineHash = {};
lineArray[0] = '';
lineArray[0] = "";
function diff_linesToPartsMunge_(text,mode) {
var chars = '';
var lineStart = 0;
var lineEnd = -1;
var lineArrayLength = lineArray.length,
regexpResult;
var searchRegexp = /\W+/g;
while(lineEnd < text.length - 1) {
if(mode === "words") {
regexpResult = searchRegexp.exec(text);
lineEnd = searchRegexp.lastIndex;
if(regexpResult === null) {
lineEnd = text.length;
}
lineEnd = --lineEnd;
} else {
lineEnd = text.indexOf('\n', lineStart);
if(lineEnd == -1) {
lineEnd = text.length - 1;
}
}
var line = text.substring(lineStart, lineEnd + 1);
function diff_linesToPartsMunge_(text,mode) {
var chars = "";
var lineStart = 0;
var lineEnd = -1;
var lineArrayLength = lineArray.length,
regexpResult;
var searchRegexp = /\W+/g;
while(lineEnd < text.length - 1) {
if(mode === "words") {
regexpResult = searchRegexp.exec(text);
lineEnd = searchRegexp.lastIndex;
if(regexpResult === null) {
lineEnd = text.length;
}
lineEnd = --lineEnd;
} else {
lineEnd = text.indexOf("\n", lineStart);
if(lineEnd == -1) {
lineEnd = text.length - 1;
}
}
var line = text.substring(lineStart, lineEnd + 1);
if(lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : (lineHash[line] !== undefined)) {
if(lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : (lineHash[line] !== undefined)) {
chars += String.fromCharCode(lineHash[line]);
} else {
if(lineArrayLength == maxLines) {
line = text.substring(lineStart);
lineEnd = text.length;
}
chars += String.fromCharCode(lineArrayLength);
lineHash[line] = lineArrayLength;
lineArray[lineArrayLength++] = line;
}
lineStart = lineEnd + 1;
}
return chars;
}
var maxLines = 40000;
var chars1 = diff_linesToPartsMunge_(text1,mode);
maxLines = 65535;
var chars2 = diff_linesToPartsMunge_(text2,mode);
return {chars1: chars1, chars2: chars2, lineArray: lineArray};
} else {
if(lineArrayLength == maxLines) {
line = text.substring(lineStart);
lineEnd = text.length;
}
chars += String.fromCharCode(lineArrayLength);
lineHash[line] = lineArrayLength;
lineArray[lineArrayLength++] = line;
}
lineStart = lineEnd + 1;
}
return chars;
}
var maxLines = 40000;
var chars1 = diff_linesToPartsMunge_(text1,mode);
maxLines = 65535;
var chars2 = diff_linesToPartsMunge_(text2,mode);
return {chars1: chars1, chars2: chars2, lineArray: lineArray};
};
exports.makepatches = function(source,operator,options) {
var dmpObject = new dmp.diff_match_patch(),
suffix = operator.suffix || "",
var suffix = operator.suffix || "",
result = [];
source(function(tiddler,title) {
var diffs, patches;
if(suffix === "lines" || suffix === "words") {
diffs = diffLineWordMode(title,operator.operand,suffix);
patches = dmpObject.patch_make(title,diffs);
} else {
patches = dmpObject.patch_make(title,operator.operand);
}
Array.prototype.push.apply(result,[dmpObject.patch_toText(patches)]);
});
source(function(tiddler,title) {
let diffs, patches;
if(suffix === "lines" || suffix === "words") {
diffs = diffLineWordMode(title,operator.operand,suffix);
patches = dmp.patchMake(title,diffs);
} else {
patches = dmp.patchMake(title,operator.operand);
}
Array.prototype.push.apply(result,[dmp.patchToText(patches)]);
});
return result;
};
exports.applypatches = makeStringBinaryOperator(
function(a,b) {
var dmpObject = new dmp.diff_match_patch(),
patches;
let patches;
try {
patches = dmpObject.patch_fromText(b);
patches = dmp.patchFromText(b);
} catch(e) {
}
if(patches) {
return [dmpObject.patch_apply(patches,a)[0]];
return [dmp.patchApply(patches,a)[0]];
} else {
return [a];
}

File diff suppressed because one or more lines are too long

View File

@@ -9,8 +9,8 @@ Widget to display a diff between two texts
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget,
dmp = require("$:/core/modules/utils/diff-match-patch/diff_match_patch.js");
var Widget = require("$:/core/modules/widgets/widget.js").widget;
const dmp = require("$:/core/modules/utils/diff-match-patch/diff_match_patch.js");
var DiffTextWidget = function(parseTreeNode,options) {
this.initialise(parseTreeNode,options);
@@ -35,19 +35,18 @@ DiffTextWidget.prototype.render = function(parent,nextSibling) {
this.computeAttributes();
this.execute();
// Create the diff object
var dmpObject = new dmp.diff_match_patch();
dmpObject.Diff_EditCost = $tw.utils.parseNumber(this.getAttribute("editcost","4"));
var diffs = dmpObject.diff_main(this.getAttribute("source",""),this.getAttribute("dest",""));
const editCost = $tw.utils.parseNumber(this.getAttribute("editcost","4"));
const diffs = dmp.diffMain(this.getAttribute("source",""),this.getAttribute("dest",""),{diffEditCost: editCost});
// Apply required cleanup
switch(this.getAttribute("cleanup","semantic")) {
case "none":
// No cleanup
break;
case "efficiency":
dmpObject.diff_cleanupEfficiency(diffs);
dmp.diffCleanupEfficiency(diffs, {diffEditCost: editCost});
break;
default: // case "semantic"
dmpObject.diff_cleanupSemantic(diffs);
dmp.diffCleanupSemantic(diffs);
break;
}
// Create the elements

View File

@@ -0,0 +1,11 @@
title: $:/changenotes/5.4.0/#9511/impacts/api
changenote: $:/changenotes/5.4.0/#9511
created: 20251220010540143
modified: 20251220010540143
tags: $:/tags/ImpactNote
description: The diff-match-patch-es library uses different APIs
impact-type: compatibility-break
* Default export and the class constructor has been removed
* Function name has been unified to camelCase
* Previous options like Diff_Timeout and Diff_EditCost are now passed as an options object in the arguments if needed

View File

@@ -0,0 +1,10 @@
title: $:/changenotes/5.4.0/#9511
description: Migrate diff-match-patch library to diff-match-patch-es
release: 5.4.0
tags: $:/tags/ChangeNote
change-type: enhancement
change-category: developer
github-links: https://github.com/TiddlyWiki/TiddlyWiki5/pull/9511
github-contributors: Leilei332
Migrate the unmaintained [[diff-match-patch|https://github.com/google/diff-match-patch]] library to the maintained and modern fork [[diff-match-patch-es|https://github.com/antfu/diff-match-patch-es]].