Created
February 16, 2021 13:22
-
-
Save fnordfish/1dd9bec0fa903ff6d8d2bde56a025408 to your computer and use it in GitHub Desktop.
DMP - diff-match-patch using it's JS distribution and mini_racer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
# renamed files for gist compatibility - should be in sub-folders (dmp/*) | |
require_relative "dmp__diff" | |
require_relative "dmp__render" | |
# Diff-Match-Path | |
# https://github.com/google/diff-match-patch | |
module DMP | |
extend Diff | |
extend Renderer | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
require "mini_racer" | |
# Diff-Match-Path | |
# Converts the original "-1, 0, 1" diff identifiers to non-negative numbers | |
# https://github.com/google/diff-match-patch | |
module DMP | |
module Diff | |
DIFF_EQUAL = 0 | |
DIFF_DELETE = 1 # converted from -1 | |
DIFF_INSERT = 2 # converted from 1 | |
CONVERSION = { 0 => DIFF_EQUAL, -1 => DIFF_DELETE, 1 => DIFF_INSERT }.freeze | |
NL_REGEXP = /(\r\n|\n|\r)/ | |
LF = "\n" | |
# @private | |
SCRIPT = <<~JS | |
;function dmp(left, right, semantic_cleanup) { | |
var dmp = new diff_match_patch(); | |
// Number of seconds to map a diff before giving up (0 for infinity). | |
dmp.Diff_Timeout = 2.0; | |
// Cost of an empty edit operation in terms of edit characters. | |
dmp.Diff_EditCost = 4; | |
var diffs = dmp.diff_main(left, right); | |
if (semantic_cleanup) { | |
dmp.diff_cleanupSemantic(diffs); | |
} | |
return diffs; | |
} | |
JS | |
# @private | |
DMP_JS_SNAPSHOT = MiniRacer::Snapshot | |
.new(File.read("#{__dir__}/diff_match_patch.min.js") + SCRIPT) | |
.warmup!("new diff_match_patch();") | |
def diff(text_old, text_new, semantic_clean: true, normalize_nl: true) | |
ctx = MiniRacer::Context.new(snapshot: DMP_JS_SNAPSHOT, timeout: 2_500) | |
diffs = ctx.call( | |
"dmp", | |
(normalize_nl ? text_old.gsub(NL_REGEXP, LF) : text_old), | |
(normalize_nl ? text_new.gsub(NL_REGEXP, LF) : text_new), | |
semantic_clean | |
) | |
diffs.map { |diff| [CONVERSION[diff["0"]], diff["1"]] } | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Diff Match and Patch | |
* Copyright 2018 The diff-match-patch Authors. | |
* https://github.com/google/diff-match-patch | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
var diff_match_patch=function(){this.Diff_Timeout=1,this.Diff_EditCost=4,this.Match_Threshold=.5,this.Match_Distance=1e3,this.Patch_DeleteThreshold=.5,this.Patch_Margin=4,this.Match_MaxBits=32},DIFF_DELETE=-1,DIFF_INSERT=1,DIFF_EQUAL=0;diff_match_patch.Diff=function(t,i){this[0]=t,this[1]=i},diff_match_patch.Diff.prototype.length=2,diff_match_patch.Diff.prototype.toString=function(){return this[0]+","+this[1]},diff_match_patch.prototype.diff_main=function(t,i,h,f){void 0===f&&(f=this.Diff_Timeout<=0?Number.MAX_VALUE:(new Date).getTime()+1e3*this.Diff_Timeout);var n=f;if(null==t||null==i)throw new Error("Null input. (diff_main)");if(t==i)return t?[new diff_match_patch.Diff(DIFF_EQUAL,t)]:[];void 0===h&&(h=!0);var e=h,a=this.diff_commonPrefix(t,i),r=t.substring(0,a);t=t.substring(a),i=i.substring(a),a=this.diff_commonSuffix(t,i);var s=t.substring(t.length-a);t=t.substring(0,t.length-a),i=i.substring(0,i.length-a);var _=this.diff_compute_(t,i,e,n);return r&&_.unshift(new diff_match_patch.Diff(DIFF_EQUAL,r)),s&&_.push(new diff_match_patch.Diff(DIFF_EQUAL,s)),this.diff_cleanupMerge(_),_},diff_match_patch.prototype.diff_compute_=function(t,i,h,f){var n;if(!t)return[new diff_match_patch.Diff(DIFF_INSERT,i)];if(!i)return[new diff_match_patch.Diff(DIFF_DELETE,t)];var e=t.length>i.length?t:i,a=t.length>i.length?i:t,r=e.indexOf(a);if(-1!=r)return n=[new diff_match_patch.Diff(DIFF_INSERT,e.substring(0,r)),new diff_match_patch.Diff(DIFF_EQUAL,a),new diff_match_patch.Diff(DIFF_INSERT,e.substring(r+a.length))],t.length>i.length&&(n[0][0]=n[2][0]=DIFF_DELETE),n;if(1==a.length)return[new diff_match_patch.Diff(DIFF_DELETE,t),new diff_match_patch.Diff(DIFF_INSERT,i)];var s=this.diff_halfMatch_(t,i);if(s){var _=s[0],c=s[1],l=s[2],g=s[3],o=s[4],p=this.diff_main(_,l,h,f),d=this.diff_main(c,g,h,f);return p.concat([new diff_match_patch.Diff(DIFF_EQUAL,o)],d)}return h&&t.length>100&&i.length>100?this.diff_lineMode_(t,i,f):this.diff_bisect_(t,i,f)},diff_match_patch.prototype.diff_lineMode_=function(t,i,h){var f=this.diff_linesToChars_(t,i);t=f.chars1,i=f.chars2;var n=f.lineArray,e=this.diff_main(t,i,!1,h);this.diff_charsToLines_(e,n),this.diff_cleanupSemantic(e),e.push(new diff_match_patch.Diff(DIFF_EQUAL,""));for(var a=0,r=0,s=0,_="",c="";a<e.length;){switch(e[a][0]){case DIFF_INSERT:s++,c+=e[a][1];break;case DIFF_DELETE:r++,_+=e[a][1];break;case DIFF_EQUAL:if(r>=1&&s>=1){e.splice(a-r-s,r+s),a=a-r-s;for(var l=this.diff_main(_,c,!1,h),g=l.length-1;g>=0;g--)e.splice(a,0,l[g]);a+=l.length}s=0,r=0,_="",c=""}a++}return e.pop(),e},diff_match_patch.prototype.diff_bisect_=function(t,i,h){for(var f=t.length,n=i.length,e=Math.ceil((f+n)/2),a=e,r=2*e,s=new Array(r),_=new Array(r),c=0;c<r;c++)s[c]=-1,_[c]=-1;s[a+1]=0,_[a+1]=0;for(var l=f-n,g=l%2!=0,o=0,p=0,d=0,u=0,m=0;m<e&&!((new Date).getTime()>h);m++){for(var F=-m+o;F<=m-p;F+=2){for(var D=a+F,E=(L=F==-m||F!=m&&s[D-1]<s[D+1]?s[D+1]:s[D-1]+1)-F;L<f&&E<n&&t.charAt(L)==i.charAt(E);)L++,E++;if(s[D]=L,L>f)p+=2;else if(E>n)o+=2;else if(g){if((v=a+l-F)>=0&&v<r&&-1!=_[v])if(L>=(b=f-_[v]))return this.diff_bisectSplit_(t,i,L,E,h)}}for(var I=-m+d;I<=m-u;I+=2){for(var b,v=a+I,w=(b=I==-m||I!=m&&_[v-1]<_[v+1]?_[v+1]:_[v-1]+1)-I;b<f&&w<n&&t.charAt(f-b-1)==i.charAt(n-w-1);)b++,w++;if(_[v]=b,b>f)u+=2;else if(w>n)d+=2;else if(!g){if((D=a+l-I)>=0&&D<r&&-1!=s[D]){var L;E=a+(L=s[D])-D;if(L>=(b=f-b))return this.diff_bisectSplit_(t,i,L,E,h)}}}}return[new diff_match_patch.Diff(DIFF_DELETE,t),new diff_match_patch.Diff(DIFF_INSERT,i)]},diff_match_patch.prototype.diff_bisectSplit_=function(t,i,h,f,n){var e=t.substring(0,h),a=i.substring(0,f),r=t.substring(h),s=i.substring(f),_=this.diff_main(e,a,!1,n),c=this.diff_main(r,s,!1,n);return _.concat(c)},diff_match_patch.prototype.diff_linesToChars_=function(t,i){var h=[],f={};function n(t){for(var i="",n=0,a=-1,r=h.length;a<t.length-1;){-1==(a=t.indexOf("\n",n))&&(a=t.length-1);var s=t.substring(n,a+1);(f.hasOwnProperty?f.hasOwnProperty(s):void 0!==f[s])?i+=String.fromCharCode(f[s]):(r==e&&(s=t.substring(n),a=t.length),i+=String.fromCharCode(r),f[s]=r,h[r++]=s),n=a+1}return i}h[0]="";var e=4e4,a=n(t);return e=65535,{chars1:a,chars2:n(i),lineArray:h}},diff_match_patch.prototype.diff_charsToLines_=function(t,i){for(var h=0;h<t.length;h++){for(var f=t[h][1],n=[],e=0;e<f.length;e++)n[e]=i[f.charCodeAt(e)];t[h][1]=n.join("")}},diff_match_patch.prototype.diff_commonPrefix=function(t,i){if(!t||!i||t.charAt(0)!=i.charAt(0))return 0;for(var h=0,f=Math.min(t.length,i.length),n=f,e=0;h<n;)t.substring(e,n)==i.substring(e,n)?e=h=n:f=n,n=Math.floor((f-h)/2+h);return n},diff_match_patch.prototype.diff_commonSuffix=function(t,i){if(!t||!i||t.charAt(t.length-1)!=i.charAt(i.length-1))return 0;for(var h=0,f=Math.min(t.length,i.length),n=f,e=0;h<n;)t.substring(t.length-n,t.length-e)==i.substring(i.length-n,i.length-e)?e=h=n:f=n,n=Math.floor((f-h)/2+h);return n},diff_match_patch.prototype.diff_commonOverlap_=function(t,i){var h=t.length,f=i.length;if(0==h||0==f)return 0;h>f?t=t.substring(h-f):h<f&&(i=i.substring(0,h));var n=Math.min(h,f);if(t==i)return n;for(var e=0,a=1;;){var r=t.substring(n-a),s=i.indexOf(r);if(-1==s)return e;a+=s,0!=s&&t.substring(n-a)!=i.substring(0,a)||(e=a,a++)}},diff_match_patch.prototype.diff_halfMatch_=function(t,i){if(this.Diff_Timeout<=0)return null;var h=t.length>i.length?t:i,f=t.length>i.length?i:t;if(h.length<4||2*f.length<h.length)return null;var n=this;function e(t,i,h){for(var f,e,a,r,s=t.substring(h,h+Math.floor(t.length/4)),_=-1,c="";-1!=(_=i.indexOf(s,_+1));){var l=n.diff_commonPrefix(t.substring(h),i.substring(_)),g=n.diff_commonSuffix(t.substring(0,h),i.substring(0,_));c.length<g+l&&(c=i.substring(_-g,_)+i.substring(_,_+l),f=t.substring(0,h-g),e=t.substring(h+l),a=i.substring(0,_-g),r=i.substring(_+l))}return 2*c.length>=t.length?[f,e,a,r,c]:null}var a,r,s,_,c,l=e(h,f,Math.ceil(h.length/4)),g=e(h,f,Math.ceil(h.length/2));return l||g?(a=g?l&&l[4].length>g[4].length?l:g:l,t.length>i.length?(r=a[0],s=a[1],_=a[2],c=a[3]):(_=a[0],c=a[1],r=a[2],s=a[3]),[r,s,_,c,a[4]]):null},diff_match_patch.prototype.diff_cleanupSemantic=function(t){for(var i=!1,h=[],f=0,n=null,e=0,a=0,r=0,s=0,_=0;e<t.length;)t[e][0]==DIFF_EQUAL?(h[f++]=e,a=s,r=_,s=0,_=0,n=t[e][1]):(t[e][0]==DIFF_INSERT?s+=t[e][1].length:_+=t[e][1].length,n&&n.length<=Math.max(a,r)&&n.length<=Math.max(s,_)&&(t.splice(h[f-1],0,new diff_match_patch.Diff(DIFF_DELETE,n)),t[h[f-1]+1][0]=DIFF_INSERT,f--,e=--f>0?h[f-1]:-1,a=0,r=0,s=0,_=0,n=null,i=!0)),e++;for(i&&this.diff_cleanupMerge(t),this.diff_cleanupSemanticLossless(t),e=1;e<t.length;){if(t[e-1][0]==DIFF_DELETE&&t[e][0]==DIFF_INSERT){var c=t[e-1][1],l=t[e][1],g=this.diff_commonOverlap_(c,l),o=this.diff_commonOverlap_(l,c);g>=o?(g>=c.length/2||g>=l.length/2)&&(t.splice(e,0,new diff_match_patch.Diff(DIFF_EQUAL,l.substring(0,g))),t[e-1][1]=c.substring(0,c.length-g),t[e+1][1]=l.substring(g),e++):(o>=c.length/2||o>=l.length/2)&&(t.splice(e,0,new diff_match_patch.Diff(DIFF_EQUAL,c.substring(0,o))),t[e-1][0]=DIFF_INSERT,t[e-1][1]=l.substring(0,l.length-o),t[e+1][0]=DIFF_DELETE,t[e+1][1]=c.substring(o),e++),e++}e++}},diff_match_patch.prototype.diff_cleanupSemanticLossless=function(t){function i(t,i){if(!t||!i)return 6;var h=t.charAt(t.length-1),f=i.charAt(0),n=h.match(diff_match_patch.nonAlphaNumericRegex_),e=f.match(diff_match_patch.nonAlphaNumericRegex_),a=n&&h.match(diff_match_patch.whitespaceRegex_),r=e&&f.match(diff_match_patch.whitespaceRegex_),s=a&&h.match(diff_match_patch.linebreakRegex_),_=r&&f.match(diff_match_patch.linebreakRegex_),c=s&&t.match(diff_match_patch.blanklineEndRegex_),l=_&&i.match(diff_match_patch.blanklineStartRegex_);return c||l?5:s||_?4:n&&!a&&r?3:a||r?2:n||e?1:0}for(var h=1;h<t.length-1;){if(t[h-1][0]==DIFF_EQUAL&&t[h+1][0]==DIFF_EQUAL){var f=t[h-1][1],n=t[h][1],e=t[h+1][1],a=this.diff_commonSuffix(f,n);if(a){var r=n.substring(n.length-a);f=f.substring(0,f.length-a),n=r+n.substring(0,n.length-a),e=r+e}for(var s=f,_=n,c=e,l=i(f,n)+i(n,e);n.charAt(0)===e.charAt(0);){f+=n.charAt(0),n=n.substring(1)+e.charAt(0),e=e.substring(1);var g=i(f,n)+i(n,e);g>=l&&(l=g,s=f,_=n,c=e)}t[h-1][1]!=s&&(s?t[h-1][1]=s:(t.splice(h-1,1),h--),t[h][1]=_,c?t[h+1][1]=c:(t.splice(h+1,1),h--))}h++}},diff_match_patch.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/,diff_match_patch.whitespaceRegex_=/\s/,diff_match_patch.linebreakRegex_=/[\r\n]/,diff_match_patch.blanklineEndRegex_=/\n\r?\n$/,diff_match_patch.blanklineStartRegex_=/^\r?\n\r?\n/,diff_match_patch.prototype.diff_cleanupEfficiency=function(t){for(var i=!1,h=[],f=0,n=null,e=0,a=!1,r=!1,s=!1,_=!1;e<t.length;)t[e][0]==DIFF_EQUAL?(t[e][1].length<this.Diff_EditCost&&(s||_)?(h[f++]=e,a=s,r=_,n=t[e][1]):(f=0,n=null),s=_=!1):(t[e][0]==DIFF_DELETE?_=!0:s=!0,n&&(a&&r&&s&&_||n.length<this.Diff_EditCost/2&&a+r+s+_==3)&&(t.splice(h[f-1],0,new diff_match_patch.Diff(DIFF_DELETE,n)),t[h[f-1]+1][0]=DIFF_INSERT,f--,n=null,a&&r?(s=_=!0,f=0):(e=--f>0?h[f-1]:-1,s=_=!1),i=!0)),e++;i&&this.diff_cleanupMerge(t)},diff_match_patch.prototype.diff_cleanupMerge=function(t){t.push(new diff_match_patch.Diff(DIFF_EQUAL,""));for(var i,h=0,f=0,n=0,e="",a="";h<t.length;)switch(t[h][0]){case DIFF_INSERT:n++,a+=t[h][1],h++;break;case DIFF_DELETE:f++,e+=t[h][1],h++;break;case DIFF_EQUAL:f+n>1?(0!==f&&0!==n&&(0!==(i=this.diff_commonPrefix(a,e))&&(h-f-n>0&&t[h-f-n-1][0]==DIFF_EQUAL?t[h-f-n-1][1]+=a.substring(0,i):(t.splice(0,0,new diff_match_patch.Diff(DIFF_EQUAL,a.substring(0,i))),h++),a=a.substring(i),e=e.substring(i)),0!==(i=this.diff_commonSuffix(a,e))&&(t[h][1]=a.substring(a.length-i)+t[h][1],a=a.substring(0,a.length-i),e=e.substring(0,e.length-i))),h-=f+n,t.splice(h,f+n),e.length&&(t.splice(h,0,new diff_match_patch.Diff(DIFF_DELETE,e)),h++),a.length&&(t.splice(h,0,new diff_match_patch.Diff(DIFF_INSERT,a)),h++),h++):0!==h&&t[h-1][0]==DIFF_EQUAL?(t[h-1][1]+=t[h][1],t.splice(h,1)):h++,n=0,f=0,e="",a=""}""===t[t.length-1][1]&&t.pop();var r=!1;for(h=1;h<t.length-1;)t[h-1][0]==DIFF_EQUAL&&t[h+1][0]==DIFF_EQUAL&&(t[h][1].substring(t[h][1].length-t[h-1][1].length)==t[h-1][1]?(t[h][1]=t[h-1][1]+t[h][1].substring(0,t[h][1].length-t[h-1][1].length),t[h+1][1]=t[h-1][1]+t[h+1][1],t.splice(h-1,1),r=!0):t[h][1].substring(0,t[h+1][1].length)==t[h+1][1]&&(t[h-1][1]+=t[h+1][1],t[h][1]=t[h][1].substring(t[h+1][1].length)+t[h+1][1],t.splice(h+1,1),r=!0)),h++;r&&this.diff_cleanupMerge(t)},diff_match_patch.prototype.diff_xIndex=function(t,i){var h,f=0,n=0,e=0,a=0;for(h=0;h<t.length&&(t[h][0]!==DIFF_INSERT&&(f+=t[h][1].length),t[h][0]!==DIFF_DELETE&&(n+=t[h][1].length),!(f>i));h++)e=f,a=n;return t.length!=h&&t[h][0]===DIFF_DELETE?a:a+(i-e)},diff_match_patch.prototype.diff_prettyHtml=function(t){for(var i=[],h=/&/g,f=/</g,n=/>/g,e=/\n/g,a=0;a<t.length;a++){var r=t[a][0],s=t[a][1].replace(h,"&").replace(f,"<").replace(n,">").replace(e,"¶<br>");switch(r){case DIFF_INSERT:i[a]='<ins style="background:#e6ffe6;">'+s+"</ins>";break;case DIFF_DELETE:i[a]='<del style="background:#ffe6e6;">'+s+"</del>";break;case DIFF_EQUAL:i[a]="<span>"+s+"</span>"}}return i.join("")},diff_match_patch.prototype.diff_text1=function(t){for(var i=[],h=0;h<t.length;h++)t[h][0]!==DIFF_INSERT&&(i[h]=t[h][1]);return i.join("")},diff_match_patch.prototype.diff_text2=function(t){for(var i=[],h=0;h<t.length;h++)t[h][0]!==DIFF_DELETE&&(i[h]=t[h][1]);return i.join("")},diff_match_patch.prototype.diff_levenshtein=function(t){for(var i=0,h=0,f=0,n=0;n<t.length;n++){var e=t[n][0],a=t[n][1];switch(e){case DIFF_INSERT:h+=a.length;break;case DIFF_DELETE:f+=a.length;break;case DIFF_EQUAL:i+=Math.max(h,f),h=0,f=0}}return i+=Math.max(h,f)},diff_match_patch.prototype.diff_toDelta=function(t){for(var i=[],h=0;h<t.length;h++)switch(t[h][0]){case DIFF_INSERT:i[h]="+"+encodeURI(t[h][1]);break;case DIFF_DELETE:i[h]="-"+t[h][1].length;break;case DIFF_EQUAL:i[h]="="+t[h][1].length}return i.join("\t").replace(/%20/g," ")},diff_match_patch.prototype.diff_fromDelta=function(t,i){for(var h=[],f=0,n=0,e=i.split(/\t/g),a=0;a<e.length;a++){var r=e[a].substring(1);switch(e[a].charAt(0)){case"+":try{h[f++]=new diff_match_patch.Diff(DIFF_INSERT,decodeURI(r))}catch(t){throw new Error("Illegal escape in diff_fromDelta: "+r)}break;case"-":case"=":var s=parseInt(r,10);if(isNaN(s)||s<0)throw new Error("Invalid number in diff_fromDelta: "+r);var _=t.substring(n,n+=s);"="==e[a].charAt(0)?h[f++]=new diff_match_patch.Diff(DIFF_EQUAL,_):h[f++]=new diff_match_patch.Diff(DIFF_DELETE,_);break;default:if(e[a])throw new Error("Invalid diff operation in diff_fromDelta: "+e[a])}}if(n!=t.length)throw new Error("Delta length ("+n+") does not equal source text length ("+t.length+").");return h},diff_match_patch.prototype.match_main=function(t,i,h){if(null==t||null==i||null==h)throw new Error("Null input. (match_main)");return h=Math.max(0,Math.min(h,t.length)),t==i?0:t.length?t.substring(h,h+i.length)==i?h:this.match_bitap_(t,i,h):-1},diff_match_patch.prototype.match_bitap_=function(t,i,h){if(i.length>this.Match_MaxBits)throw new Error("Pattern too long for this browser.");var f=this.match_alphabet_(i),n=this;function e(t,f){var e=t/i.length,a=Math.abs(h-f);return n.Match_Distance?e+a/n.Match_Distance:a?1:e}var a=this.Match_Threshold,r=t.indexOf(i,h);-1!=r&&(a=Math.min(e(0,r),a),-1!=(r=t.lastIndexOf(i,h+i.length))&&(a=Math.min(e(0,r),a)));var s,_,c=1<<i.length-1;r=-1;for(var l,g=i.length+t.length,o=0;o<i.length;o++){for(s=0,_=g;s<_;)e(o,h+_)<=a?s=_:g=_,_=Math.floor((g-s)/2+s);g=_;var p=Math.max(1,h-_+1),d=Math.min(h+_,t.length)+i.length,u=Array(d+2);u[d+1]=(1<<o)-1;for(var m=d;m>=p;m--){var F=f[t.charAt(m-1)];if(u[m]=0===o?(u[m+1]<<1|1)&F:(u[m+1]<<1|1)&F|(l[m+1]|l[m])<<1|1|l[m+1],u[m]&c){var D=e(o,m-1);if(D<=a){if(a=D,!((r=m-1)>h))break;p=Math.max(1,2*h-r)}}}if(e(o+1,h)>a)break;l=u}return r},diff_match_patch.prototype.match_alphabet_=function(t){for(var i={},h=0;h<t.length;h++)i[t.charAt(h)]=0;for(h=0;h<t.length;h++)i[t.charAt(h)]|=1<<t.length-h-1;return i},diff_match_patch.prototype.patch_addContext_=function(t,i){if(0!=i.length){if(null===t.start2)throw Error("patch not initialized");for(var h=i.substring(t.start2,t.start2+t.length1),f=0;i.indexOf(h)!=i.lastIndexOf(h)&&h.length<this.Match_MaxBits-this.Patch_Margin-this.Patch_Margin;)f+=this.Patch_Margin,h=i.substring(t.start2-f,t.start2+t.length1+f);f+=this.Patch_Margin;var n=i.substring(t.start2-f,t.start2);n&&t.diffs.unshift(new diff_match_patch.Diff(DIFF_EQUAL,n));var e=i.substring(t.start2+t.length1,t.start2+t.length1+f);e&&t.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL,e)),t.start1-=n.length,t.start2-=n.length,t.length1+=n.length+e.length,t.length2+=n.length+e.length}},diff_match_patch.prototype.patch_make=function(t,i,h){var f,n;if("string"==typeof t&&"string"==typeof i&&void 0===h)f=t,(n=this.diff_main(f,i,!0)).length>2&&(this.diff_cleanupSemantic(n),this.diff_cleanupEfficiency(n));else if(t&&"object"==typeof t&&void 0===i&&void 0===h)n=t,f=this.diff_text1(n);else if("string"==typeof t&&i&&"object"==typeof i&&void 0===h)f=t,n=i;else{if("string"!=typeof t||"string"!=typeof i||!h||"object"!=typeof h)throw new Error("Unknown call format to patch_make.");f=t,n=h}if(0===n.length)return[];for(var e=[],a=new diff_match_patch.patch_obj,r=0,s=0,_=0,c=f,l=f,g=0;g<n.length;g++){var o=n[g][0],p=n[g][1];switch(r||o===DIFF_EQUAL||(a.start1=s,a.start2=_),o){case DIFF_INSERT:a.diffs[r++]=n[g],a.length2+=p.length,l=l.substring(0,_)+p+l.substring(_);break;case DIFF_DELETE:a.length1+=p.length,a.diffs[r++]=n[g],l=l.substring(0,_)+l.substring(_+p.length);break;case DIFF_EQUAL:p.length<=2*this.Patch_Margin&&r&&n.length!=g+1?(a.diffs[r++]=n[g],a.length1+=p.length,a.length2+=p.length):p.length>=2*this.Patch_Margin&&r&&(this.patch_addContext_(a,c),e.push(a),a=new diff_match_patch.patch_obj,r=0,c=l,s=_)}o!==DIFF_INSERT&&(s+=p.length),o!==DIFF_DELETE&&(_+=p.length)}return r&&(this.patch_addContext_(a,c),e.push(a)),e},diff_match_patch.prototype.patch_deepCopy=function(t){for(var i=[],h=0;h<t.length;h++){var f=t[h],n=new diff_match_patch.patch_obj;n.diffs=[];for(var e=0;e<f.diffs.length;e++)n.diffs[e]=new diff_match_patch.Diff(f.diffs[e][0],f.diffs[e][1]);n.start1=f.start1,n.start2=f.start2,n.length1=f.length1,n.length2=f.length2,i[h]=n}return i},diff_match_patch.prototype.patch_apply=function(t,i){if(0==t.length)return[i,[]];t=this.patch_deepCopy(t);var h=this.patch_addPadding(t);i=h+i+h,this.patch_splitMax(t);for(var f=0,n=[],e=0;e<t.length;e++){var a,r,s=t[e].start2+f,_=this.diff_text1(t[e].diffs),c=-1;if(_.length>this.Match_MaxBits?-1!=(a=this.match_main(i,_.substring(0,this.Match_MaxBits),s))&&(-1==(c=this.match_main(i,_.substring(_.length-this.Match_MaxBits),s+_.length-this.Match_MaxBits))||a>=c)&&(a=-1):a=this.match_main(i,_,s),-1==a)n[e]=!1,f-=t[e].length2-t[e].length1;else if(n[e]=!0,f=a-s,_==(r=-1==c?i.substring(a,a+_.length):i.substring(a,c+this.Match_MaxBits)))i=i.substring(0,a)+this.diff_text2(t[e].diffs)+i.substring(a+_.length);else{var l=this.diff_main(_,r,!1);if(_.length>this.Match_MaxBits&&this.diff_levenshtein(l)/_.length>this.Patch_DeleteThreshold)n[e]=!1;else{this.diff_cleanupSemanticLossless(l);for(var g,o=0,p=0;p<t[e].diffs.length;p++){var d=t[e].diffs[p];d[0]!==DIFF_EQUAL&&(g=this.diff_xIndex(l,o)),d[0]===DIFF_INSERT?i=i.substring(0,a+g)+d[1]+i.substring(a+g):d[0]===DIFF_DELETE&&(i=i.substring(0,a+g)+i.substring(a+this.diff_xIndex(l,o+d[1].length))),d[0]!==DIFF_DELETE&&(o+=d[1].length)}}}}return[i=i.substring(h.length,i.length-h.length),n]},diff_match_patch.prototype.patch_addPadding=function(t){for(var i=this.Patch_Margin,h="",f=1;f<=i;f++)h+=String.fromCharCode(f);for(f=0;f<t.length;f++)t[f].start1+=i,t[f].start2+=i;var n=t[0],e=n.diffs;if(0==e.length||e[0][0]!=DIFF_EQUAL)e.unshift(new diff_match_patch.Diff(DIFF_EQUAL,h)),n.start1-=i,n.start2-=i,n.length1+=i,n.length2+=i;else if(i>e[0][1].length){var a=i-e[0][1].length;e[0][1]=h.substring(e[0][1].length)+e[0][1],n.start1-=a,n.start2-=a,n.length1+=a,n.length2+=a}if(0==(e=(n=t[t.length-1]).diffs).length||e[e.length-1][0]!=DIFF_EQUAL)e.push(new diff_match_patch.Diff(DIFF_EQUAL,h)),n.length1+=i,n.length2+=i;else if(i>e[e.length-1][1].length){a=i-e[e.length-1][1].length;e[e.length-1][1]+=h.substring(0,a),n.length1+=a,n.length2+=a}return h},diff_match_patch.prototype.patch_splitMax=function(t){for(var i=this.Match_MaxBits,h=0;h<t.length;h++)if(!(t[h].length1<=i)){var f=t[h];t.splice(h--,1);for(var n=f.start1,e=f.start2,a="";0!==f.diffs.length;){var r=new diff_match_patch.patch_obj,s=!0;for(r.start1=n-a.length,r.start2=e-a.length,""!==a&&(r.length1=r.length2=a.length,r.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL,a)));0!==f.diffs.length&&r.length1<i-this.Patch_Margin;){var _=f.diffs[0][0],c=f.diffs[0][1];_===DIFF_INSERT?(r.length2+=c.length,e+=c.length,r.diffs.push(f.diffs.shift()),s=!1):_===DIFF_DELETE&&1==r.diffs.length&&r.diffs[0][0]==DIFF_EQUAL&&c.length>2*i?(r.length1+=c.length,n+=c.length,s=!1,r.diffs.push(new diff_match_patch.Diff(_,c)),f.diffs.shift()):(c=c.substring(0,i-r.length1-this.Patch_Margin),r.length1+=c.length,n+=c.length,_===DIFF_EQUAL?(r.length2+=c.length,e+=c.length):s=!1,r.diffs.push(new diff_match_patch.Diff(_,c)),c==f.diffs[0][1]?f.diffs.shift():f.diffs[0][1]=f.diffs[0][1].substring(c.length))}a=(a=this.diff_text2(r.diffs)).substring(a.length-this.Patch_Margin);var l=this.diff_text1(f.diffs).substring(0,this.Patch_Margin);""!==l&&(r.length1+=l.length,r.length2+=l.length,0!==r.diffs.length&&r.diffs[r.diffs.length-1][0]===DIFF_EQUAL?r.diffs[r.diffs.length-1][1]+=l:r.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL,l))),s||t.splice(++h,0,r)}}},diff_match_patch.prototype.patch_toText=function(t){for(var i=[],h=0;h<t.length;h++)i[h]=t[h];return i.join("")},diff_match_patch.prototype.patch_fromText=function(t){var i=[];if(!t)return i;for(var h=t.split("\n"),f=0,n=/^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/;f<h.length;){var e=h[f].match(n);if(!e)throw new Error("Invalid patch string: "+h[f]);var a=new diff_match_patch.patch_obj;for(i.push(a),a.start1=parseInt(e[1],10),""===e[2]?(a.start1--,a.length1=1):"0"==e[2]?a.length1=0:(a.start1--,a.length1=parseInt(e[2],10)),a.start2=parseInt(e[3],10),""===e[4]?(a.start2--,a.length2=1):"0"==e[4]?a.length2=0:(a.start2--,a.length2=parseInt(e[4],10)),f++;f<h.length;){var r=h[f].charAt(0);try{var s=decodeURI(h[f].substring(1))}catch(t){throw new Error("Illegal escape in patch_fromText: "+s)}if("-"==r)a.diffs.push(new diff_match_patch.Diff(DIFF_DELETE,s));else if("+"==r)a.diffs.push(new diff_match_patch.Diff(DIFF_INSERT,s));else if(" "==r)a.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL,s));else{if("@"==r)break;if(""!==r)throw new Error('Invalid patch mode "'+r+'" in: '+s)}f++}}return i},diff_match_patch.patch_obj=function(){this.diffs=[],this.start1=null,this.start2=null,this.length1=0,this.length2=0},diff_match_patch.patch_obj.prototype.toString=function(){for(var t,i=["@@ -"+(0===this.length1?this.start1+",0":1==this.length1?this.start1+1:this.start1+1+","+this.length1)+" +"+(0===this.length2?this.start2+",0":1==this.length2?this.start2+1:this.start2+1+","+this.length2)+" @@\n"],h=0;h<this.diffs.length;h++){switch(this.diffs[h][0]){case DIFF_INSERT:t="+";break;case DIFF_DELETE:t="-";break;case DIFF_EQUAL:t=" "}i[h+1]=t+encodeURI(this.diffs[h][1])+"\n"}return i.join("").replace(/%20/g," ")},this.diff_match_patch=diff_match_patch,this.DIFF_DELETE=DIFF_DELETE,this.DIFF_INSERT=DIFF_INSERT,this.DIFF_EQUAL=DIFF_EQUAL; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
module DMP | |
module Renderer | |
DIFF_HTMLS = { | |
DMP::Diff::DIFF_DELETE => ["<del>", "</del>"].freeze, | |
DMP::Diff::DIFF_INSERT => ["<ins>", "</ins>"].freeze, | |
DMP::Diff::DIFF_EQUAL => ["<span>", "</span>"].freeze | |
}.freeze | |
ROW_CLASSES = { | |
DMP::Diff::DIFF_EQUAL => "dmp-diff-unchanged", | |
DMP::Diff::DIFF_DELETE => "dmp-diff-deletion", | |
DMP::Diff::DIFF_INSERT => "dmp-diff-addition", | |
(DMP::Diff::DIFF_INSERT | DMP::Diff::DIFF_DELETE) => "dmp-diff-changed" | |
}.freeze | |
NL_REGEXP = /(\r\n|\n|\r)/ | |
LF = "\n" | |
BR = "<br>" | |
WRAPPER_HTML_CLASS = "dmp-diff" | |
NB_CELL_HTML_CLASS = "dmp-diff-line" | |
TEXT_CELL_HTML_CLASS = "dmp-diff-text" | |
# Generates a html table with "td"s for line numbers and text | |
# @param diffs [Array<Integer,String>] DMP diff | |
def pretty_html_table(diffs) | |
open_line = nil | |
line_changes = 0 | |
line_number = 0 | |
content = +"<table class=\"#{WRAPPER_HTML_CLASS}\"><tbody>#{LF}" | |
diffs.each_with_index.inject(content) do |buff, (diff, diff_idx)| | |
mode = diff[0] | |
text = CGI.escapeHTML(diff[1]) | |
open_html, close_html = DIFF_HTMLS[mode] | |
chunk_lines = text.lines | |
chunk_html = chunk_lines.each_with_index.each_with_object(+"") { |(line, chunk_idx), acc| | |
l = line.chomp | |
line_changes |= mode # bitwise or! | |
open_line ||= +"" | |
open_line << "#{open_html}#{l}#{close_html}" | |
# reached line end or last line of last diff, flush line buffer into content | |
if line.end_with?(LF) || (chunk_lines.size - 1 == chunk_idx && diffs.size - 1 == diff_idx) | |
line_number += 1 | |
tr_class = ROW_CLASSES[line_changes] | |
acc << <<~HTML.chop | |
<tr class="#{tr_class}"><td class="#{NB_CELL_HTML_CLASS}">#{line_number}</td><td class="#{TEXT_CELL_HTML_CLASS}">#{open_line}</td></tr>#{LF} | |
HTML | |
open_line = nil | |
line_changes = 0 | |
end | |
acc | |
} | |
buff << chunk_html | |
end | |
content << "</tbody></table>" | |
end | |
# Each line in it's own div, all in one wrapper | |
# @param diffs [Array<Integer,String>] DMP diff | |
def pretty_html_divs(diffs) | |
open_line = nil | |
content = +"<div class=\"#{WRAPPER_HTML_CLASS}\">#{LF}" | |
diffs.each_with_index.inject(content) do |buff, (diff, diff_idx)| | |
mode = diff[0] | |
text = CGI.escapeHTML(diff[1]) | |
open_html, close_html = DIFF_HTMLS[mode] | |
chunk_lines = text.lines | |
chunk_html = chunk_lines.each_with_index.each_with_object(+"") { |(line, chunk_idx), acc| | |
l = line.chomp | |
open_line ||= +"" | |
open_line << "#{open_html}#{l}#{close_html}" | |
# reached line end or last line of last diff, flush line buffer into content | |
if line.end_with?(LF) || (chunk_lines.size - 1 == chunk_idx && diffs.size - 1 == diff_idx) | |
acc << <<~HTML.chop | |
<div class="#{TEXT_CELL_HTML_CLASS}">#{open_line}</div>#{LF} | |
HTML | |
open_line = nil | |
end | |
acc | |
} | |
buff << chunk_html | |
end | |
content << "</div>" | |
end | |
# All text in one wrapper | |
# @param diffs [Array<Integer,String>] DMP diff | |
# @param nl2br [Boolean|String] when sring is given, substitute new lines with that staring | |
def pretty_html_simple(diffs, nl2br: true) | |
diffs.inject(+"") do |buff, diff| | |
mode = diff[0] | |
text = CGI.escapeHTML(diff[1]) | |
text.gsub!(NL_REGEXP, nl2br === true ? BR : nl2br) if nl2br # rubocop:disable Style/CaseEquality | |
open_html, close_html = DIFF_HTMLS[mode] | |
buff << "<div class=\"#{WRAPPER_HTML_CLASS}\">#{open_html}#{text}#{close_html}</div>" | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment