diff.js 77 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106
  1. /*!
  2. diff v7.0.0
  3. BSD 3-Clause License
  4. Copyright (c) 2009-2015, Kevin Decker <kpdecker@gmail.com>
  5. All rights reserved.
  6. Redistribution and use in source and binary forms, with or without
  7. modification, are permitted provided that the following conditions are met:
  8. 1. Redistributions of source code must retain the above copyright notice, this
  9. list of conditions and the following disclaimer.
  10. 2. Redistributions in binary form must reproduce the above copyright notice,
  11. this list of conditions and the following disclaimer in the documentation
  12. and/or other materials provided with the distribution.
  13. 3. Neither the name of the copyright holder nor the names of its
  14. contributors may be used to endorse or promote products derived from
  15. this software without specific prior written permission.
  16. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  17. AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  18. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  20. FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  21. DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  22. SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  23. CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  24. OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  25. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. @license
  27. */
  28. (function (global, factory) {
  29. typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  30. typeof define === 'function' && define.amd ? define(['exports'], factory) :
  31. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Diff = {}));
  32. })(this, (function (exports) { 'use strict';
  33. function Diff() {}
  34. Diff.prototype = {
  35. diff: function diff(oldString, newString) {
  36. var _options$timeout;
  37. var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  38. var callback = options.callback;
  39. if (typeof options === 'function') {
  40. callback = options;
  41. options = {};
  42. }
  43. var self = this;
  44. function done(value) {
  45. value = self.postProcess(value, options);
  46. if (callback) {
  47. setTimeout(function () {
  48. callback(value);
  49. }, 0);
  50. return true;
  51. } else {
  52. return value;
  53. }
  54. }
  55. // Allow subclasses to massage the input prior to running
  56. oldString = this.castInput(oldString, options);
  57. newString = this.castInput(newString, options);
  58. oldString = this.removeEmpty(this.tokenize(oldString, options));
  59. newString = this.removeEmpty(this.tokenize(newString, options));
  60. var newLen = newString.length,
  61. oldLen = oldString.length;
  62. var editLength = 1;
  63. var maxEditLength = newLen + oldLen;
  64. if (options.maxEditLength != null) {
  65. maxEditLength = Math.min(maxEditLength, options.maxEditLength);
  66. }
  67. var maxExecutionTime = (_options$timeout = options.timeout) !== null && _options$timeout !== void 0 ? _options$timeout : Infinity;
  68. var abortAfterTimestamp = Date.now() + maxExecutionTime;
  69. var bestPath = [{
  70. oldPos: -1,
  71. lastComponent: undefined
  72. }];
  73. // Seed editLength = 0, i.e. the content starts with the same values
  74. var newPos = this.extractCommon(bestPath[0], newString, oldString, 0, options);
  75. if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
  76. // Identity per the equality and tokenizer
  77. return done(buildValues(self, bestPath[0].lastComponent, newString, oldString, self.useLongestToken));
  78. }
  79. // Once we hit the right edge of the edit graph on some diagonal k, we can
  80. // definitely reach the end of the edit graph in no more than k edits, so
  81. // there's no point in considering any moves to diagonal k+1 any more (from
  82. // which we're guaranteed to need at least k+1 more edits).
  83. // Similarly, once we've reached the bottom of the edit graph, there's no
  84. // point considering moves to lower diagonals.
  85. // We record this fact by setting minDiagonalToConsider and
  86. // maxDiagonalToConsider to some finite value once we've hit the edge of
  87. // the edit graph.
  88. // This optimization is not faithful to the original algorithm presented in
  89. // Myers's paper, which instead pointlessly extends D-paths off the end of
  90. // the edit graph - see page 7 of Myers's paper which notes this point
  91. // explicitly and illustrates it with a diagram. This has major performance
  92. // implications for some common scenarios. For instance, to compute a diff
  93. // where the new text simply appends d characters on the end of the
  94. // original text of length n, the true Myers algorithm will take O(n+d^2)
  95. // time while this optimization needs only O(n+d) time.
  96. var minDiagonalToConsider = -Infinity,
  97. maxDiagonalToConsider = Infinity;
  98. // Main worker method. checks all permutations of a given edit length for acceptance.
  99. function execEditLength() {
  100. for (var diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
  101. var basePath = void 0;
  102. var removePath = bestPath[diagonalPath - 1],
  103. addPath = bestPath[diagonalPath + 1];
  104. if (removePath) {
  105. // No one else is going to attempt to use this value, clear it
  106. bestPath[diagonalPath - 1] = undefined;
  107. }
  108. var canAdd = false;
  109. if (addPath) {
  110. // what newPos will be after we do an insertion:
  111. var addPathNewPos = addPath.oldPos - diagonalPath;
  112. canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
  113. }
  114. var canRemove = removePath && removePath.oldPos + 1 < oldLen;
  115. if (!canAdd && !canRemove) {
  116. // If this path is a terminal then prune
  117. bestPath[diagonalPath] = undefined;
  118. continue;
  119. }
  120. // Select the diagonal that we want to branch from. We select the prior
  121. // path whose position in the old string is the farthest from the origin
  122. // and does not pass the bounds of the diff graph
  123. if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) {
  124. basePath = self.addToPath(addPath, true, false, 0, options);
  125. } else {
  126. basePath = self.addToPath(removePath, false, true, 1, options);
  127. }
  128. newPos = self.extractCommon(basePath, newString, oldString, diagonalPath, options);
  129. if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
  130. // If we have hit the end of both strings, then we are done
  131. return done(buildValues(self, basePath.lastComponent, newString, oldString, self.useLongestToken));
  132. } else {
  133. bestPath[diagonalPath] = basePath;
  134. if (basePath.oldPos + 1 >= oldLen) {
  135. maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
  136. }
  137. if (newPos + 1 >= newLen) {
  138. minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
  139. }
  140. }
  141. }
  142. editLength++;
  143. }
  144. // Performs the length of edit iteration. Is a bit fugly as this has to support the
  145. // sync and async mode which is never fun. Loops over execEditLength until a value
  146. // is produced, or until the edit length exceeds options.maxEditLength (if given),
  147. // in which case it will return undefined.
  148. if (callback) {
  149. (function exec() {
  150. setTimeout(function () {
  151. if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
  152. return callback();
  153. }
  154. if (!execEditLength()) {
  155. exec();
  156. }
  157. }, 0);
  158. })();
  159. } else {
  160. while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
  161. var ret = execEditLength();
  162. if (ret) {
  163. return ret;
  164. }
  165. }
  166. }
  167. },
  168. addToPath: function addToPath(path, added, removed, oldPosInc, options) {
  169. var last = path.lastComponent;
  170. if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
  171. return {
  172. oldPos: path.oldPos + oldPosInc,
  173. lastComponent: {
  174. count: last.count + 1,
  175. added: added,
  176. removed: removed,
  177. previousComponent: last.previousComponent
  178. }
  179. };
  180. } else {
  181. return {
  182. oldPos: path.oldPos + oldPosInc,
  183. lastComponent: {
  184. count: 1,
  185. added: added,
  186. removed: removed,
  187. previousComponent: last
  188. }
  189. };
  190. }
  191. },
  192. extractCommon: function extractCommon(basePath, newString, oldString, diagonalPath, options) {
  193. var newLen = newString.length,
  194. oldLen = oldString.length,
  195. oldPos = basePath.oldPos,
  196. newPos = oldPos - diagonalPath,
  197. commonCount = 0;
  198. while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldString[oldPos + 1], newString[newPos + 1], options)) {
  199. newPos++;
  200. oldPos++;
  201. commonCount++;
  202. if (options.oneChangePerToken) {
  203. basePath.lastComponent = {
  204. count: 1,
  205. previousComponent: basePath.lastComponent,
  206. added: false,
  207. removed: false
  208. };
  209. }
  210. }
  211. if (commonCount && !options.oneChangePerToken) {
  212. basePath.lastComponent = {
  213. count: commonCount,
  214. previousComponent: basePath.lastComponent,
  215. added: false,
  216. removed: false
  217. };
  218. }
  219. basePath.oldPos = oldPos;
  220. return newPos;
  221. },
  222. equals: function equals(left, right, options) {
  223. if (options.comparator) {
  224. return options.comparator(left, right);
  225. } else {
  226. return left === right || options.ignoreCase && left.toLowerCase() === right.toLowerCase();
  227. }
  228. },
  229. removeEmpty: function removeEmpty(array) {
  230. var ret = [];
  231. for (var i = 0; i < array.length; i++) {
  232. if (array[i]) {
  233. ret.push(array[i]);
  234. }
  235. }
  236. return ret;
  237. },
  238. castInput: function castInput(value) {
  239. return value;
  240. },
  241. tokenize: function tokenize(value) {
  242. return Array.from(value);
  243. },
  244. join: function join(chars) {
  245. return chars.join('');
  246. },
  247. postProcess: function postProcess(changeObjects) {
  248. return changeObjects;
  249. }
  250. };
  251. function buildValues(diff, lastComponent, newString, oldString, useLongestToken) {
  252. // First we convert our linked list of components in reverse order to an
  253. // array in the right order:
  254. var components = [];
  255. var nextComponent;
  256. while (lastComponent) {
  257. components.push(lastComponent);
  258. nextComponent = lastComponent.previousComponent;
  259. delete lastComponent.previousComponent;
  260. lastComponent = nextComponent;
  261. }
  262. components.reverse();
  263. var componentPos = 0,
  264. componentLen = components.length,
  265. newPos = 0,
  266. oldPos = 0;
  267. for (; componentPos < componentLen; componentPos++) {
  268. var component = components[componentPos];
  269. if (!component.removed) {
  270. if (!component.added && useLongestToken) {
  271. var value = newString.slice(newPos, newPos + component.count);
  272. value = value.map(function (value, i) {
  273. var oldValue = oldString[oldPos + i];
  274. return oldValue.length > value.length ? oldValue : value;
  275. });
  276. component.value = diff.join(value);
  277. } else {
  278. component.value = diff.join(newString.slice(newPos, newPos + component.count));
  279. }
  280. newPos += component.count;
  281. // Common case
  282. if (!component.added) {
  283. oldPos += component.count;
  284. }
  285. } else {
  286. component.value = diff.join(oldString.slice(oldPos, oldPos + component.count));
  287. oldPos += component.count;
  288. }
  289. }
  290. return components;
  291. }
  292. var characterDiff = new Diff();
  293. function diffChars(oldStr, newStr, options) {
  294. return characterDiff.diff(oldStr, newStr, options);
  295. }
  296. function longestCommonPrefix(str1, str2) {
  297. var i;
  298. for (i = 0; i < str1.length && i < str2.length; i++) {
  299. if (str1[i] != str2[i]) {
  300. return str1.slice(0, i);
  301. }
  302. }
  303. return str1.slice(0, i);
  304. }
  305. function longestCommonSuffix(str1, str2) {
  306. var i;
  307. // Unlike longestCommonPrefix, we need a special case to handle all scenarios
  308. // where we return the empty string since str1.slice(-0) will return the
  309. // entire string.
  310. if (!str1 || !str2 || str1[str1.length - 1] != str2[str2.length - 1]) {
  311. return '';
  312. }
  313. for (i = 0; i < str1.length && i < str2.length; i++) {
  314. if (str1[str1.length - (i + 1)] != str2[str2.length - (i + 1)]) {
  315. return str1.slice(-i);
  316. }
  317. }
  318. return str1.slice(-i);
  319. }
  320. function replacePrefix(string, oldPrefix, newPrefix) {
  321. if (string.slice(0, oldPrefix.length) != oldPrefix) {
  322. throw Error("string ".concat(JSON.stringify(string), " doesn't start with prefix ").concat(JSON.stringify(oldPrefix), "; this is a bug"));
  323. }
  324. return newPrefix + string.slice(oldPrefix.length);
  325. }
  326. function replaceSuffix(string, oldSuffix, newSuffix) {
  327. if (!oldSuffix) {
  328. return string + newSuffix;
  329. }
  330. if (string.slice(-oldSuffix.length) != oldSuffix) {
  331. throw Error("string ".concat(JSON.stringify(string), " doesn't end with suffix ").concat(JSON.stringify(oldSuffix), "; this is a bug"));
  332. }
  333. return string.slice(0, -oldSuffix.length) + newSuffix;
  334. }
  335. function removePrefix(string, oldPrefix) {
  336. return replacePrefix(string, oldPrefix, '');
  337. }
  338. function removeSuffix(string, oldSuffix) {
  339. return replaceSuffix(string, oldSuffix, '');
  340. }
  341. function maximumOverlap(string1, string2) {
  342. return string2.slice(0, overlapCount(string1, string2));
  343. }
  344. // Nicked from https://stackoverflow.com/a/60422853/1709587
  345. function overlapCount(a, b) {
  346. // Deal with cases where the strings differ in length
  347. var startA = 0;
  348. if (a.length > b.length) {
  349. startA = a.length - b.length;
  350. }
  351. var endB = b.length;
  352. if (a.length < b.length) {
  353. endB = a.length;
  354. }
  355. // Create a back-reference for each index
  356. // that should be followed in case of a mismatch.
  357. // We only need B to make these references:
  358. var map = Array(endB);
  359. var k = 0; // Index that lags behind j
  360. map[0] = 0;
  361. for (var j = 1; j < endB; j++) {
  362. if (b[j] == b[k]) {
  363. map[j] = map[k]; // skip over the same character (optional optimisation)
  364. } else {
  365. map[j] = k;
  366. }
  367. while (k > 0 && b[j] != b[k]) {
  368. k = map[k];
  369. }
  370. if (b[j] == b[k]) {
  371. k++;
  372. }
  373. }
  374. // Phase 2: use these references while iterating over A
  375. k = 0;
  376. for (var i = startA; i < a.length; i++) {
  377. while (k > 0 && a[i] != b[k]) {
  378. k = map[k];
  379. }
  380. if (a[i] == b[k]) {
  381. k++;
  382. }
  383. }
  384. return k;
  385. }
  386. /**
  387. * Returns true if the string consistently uses Windows line endings.
  388. */
  389. function hasOnlyWinLineEndings(string) {
  390. return string.includes('\r\n') && !string.startsWith('\n') && !string.match(/[^\r]\n/);
  391. }
  392. /**
  393. * Returns true if the string consistently uses Unix line endings.
  394. */
  395. function hasOnlyUnixLineEndings(string) {
  396. return !string.includes('\r\n') && string.includes('\n');
  397. }
  398. // Based on https://en.wikipedia.org/wiki/Latin_script_in_Unicode
  399. //
  400. // Ranges and exceptions:
  401. // Latin-1 Supplement, 0080–00FF
  402. // - U+00D7 × Multiplication sign
  403. // - U+00F7 ÷ Division sign
  404. // Latin Extended-A, 0100–017F
  405. // Latin Extended-B, 0180–024F
  406. // IPA Extensions, 0250–02AF
  407. // Spacing Modifier Letters, 02B0–02FF
  408. // - U+02C7 ˇ &#711; Caron
  409. // - U+02D8 ˘ &#728; Breve
  410. // - U+02D9 ˙ &#729; Dot Above
  411. // - U+02DA ˚ &#730; Ring Above
  412. // - U+02DB ˛ &#731; Ogonek
  413. // - U+02DC ˜ &#732; Small Tilde
  414. // - U+02DD ˝ &#733; Double Acute Accent
  415. // Latin Extended Additional, 1E00–1EFF
  416. var extendedWordChars = "a-zA-Z0-9_\\u{C0}-\\u{FF}\\u{D8}-\\u{F6}\\u{F8}-\\u{2C6}\\u{2C8}-\\u{2D7}\\u{2DE}-\\u{2FF}\\u{1E00}-\\u{1EFF}";
  417. // Each token is one of the following:
  418. // - A punctuation mark plus the surrounding whitespace
  419. // - A word plus the surrounding whitespace
  420. // - Pure whitespace (but only in the special case where this the entire text
  421. // is just whitespace)
  422. //
  423. // We have to include surrounding whitespace in the tokens because the two
  424. // alternative approaches produce horribly broken results:
  425. // * If we just discard the whitespace, we can't fully reproduce the original
  426. // text from the sequence of tokens and any attempt to render the diff will
  427. // get the whitespace wrong.
  428. // * If we have separate tokens for whitespace, then in a typical text every
  429. // second token will be a single space character. But this often results in
  430. // the optimal diff between two texts being a perverse one that preserves
  431. // the spaces between words but deletes and reinserts actual common words.
  432. // See https://github.com/kpdecker/jsdiff/issues/160#issuecomment-1866099640
  433. // for an example.
  434. //
  435. // Keeping the surrounding whitespace of course has implications for .equals
  436. // and .join, not just .tokenize.
  437. // This regex does NOT fully implement the tokenization rules described above.
  438. // Instead, it gives runs of whitespace their own "token". The tokenize method
  439. // then handles stitching whitespace tokens onto adjacent word or punctuation
  440. // tokens.
  441. var tokenizeIncludingWhitespace = new RegExp("[".concat(extendedWordChars, "]+|\\s+|[^").concat(extendedWordChars, "]"), 'ug');
  442. var wordDiff = new Diff();
  443. wordDiff.equals = function (left, right, options) {
  444. if (options.ignoreCase) {
  445. left = left.toLowerCase();
  446. right = right.toLowerCase();
  447. }
  448. return left.trim() === right.trim();
  449. };
  450. wordDiff.tokenize = function (value) {
  451. var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  452. var parts;
  453. if (options.intlSegmenter) {
  454. if (options.intlSegmenter.resolvedOptions().granularity != 'word') {
  455. throw new Error('The segmenter passed must have a granularity of "word"');
  456. }
  457. parts = Array.from(options.intlSegmenter.segment(value), function (segment) {
  458. return segment.segment;
  459. });
  460. } else {
  461. parts = value.match(tokenizeIncludingWhitespace) || [];
  462. }
  463. var tokens = [];
  464. var prevPart = null;
  465. parts.forEach(function (part) {
  466. if (/\s/.test(part)) {
  467. if (prevPart == null) {
  468. tokens.push(part);
  469. } else {
  470. tokens.push(tokens.pop() + part);
  471. }
  472. } else if (/\s/.test(prevPart)) {
  473. if (tokens[tokens.length - 1] == prevPart) {
  474. tokens.push(tokens.pop() + part);
  475. } else {
  476. tokens.push(prevPart + part);
  477. }
  478. } else {
  479. tokens.push(part);
  480. }
  481. prevPart = part;
  482. });
  483. return tokens;
  484. };
  485. wordDiff.join = function (tokens) {
  486. // Tokens being joined here will always have appeared consecutively in the
  487. // same text, so we can simply strip off the leading whitespace from all the
  488. // tokens except the first (and except any whitespace-only tokens - but such
  489. // a token will always be the first and only token anyway) and then join them
  490. // and the whitespace around words and punctuation will end up correct.
  491. return tokens.map(function (token, i) {
  492. if (i == 0) {
  493. return token;
  494. } else {
  495. return token.replace(/^\s+/, '');
  496. }
  497. }).join('');
  498. };
  499. wordDiff.postProcess = function (changes, options) {
  500. if (!changes || options.oneChangePerToken) {
  501. return changes;
  502. }
  503. var lastKeep = null;
  504. // Change objects representing any insertion or deletion since the last
  505. // "keep" change object. There can be at most one of each.
  506. var insertion = null;
  507. var deletion = null;
  508. changes.forEach(function (change) {
  509. if (change.added) {
  510. insertion = change;
  511. } else if (change.removed) {
  512. deletion = change;
  513. } else {
  514. if (insertion || deletion) {
  515. // May be false at start of text
  516. dedupeWhitespaceInChangeObjects(lastKeep, deletion, insertion, change);
  517. }
  518. lastKeep = change;
  519. insertion = null;
  520. deletion = null;
  521. }
  522. });
  523. if (insertion || deletion) {
  524. dedupeWhitespaceInChangeObjects(lastKeep, deletion, insertion, null);
  525. }
  526. return changes;
  527. };
  528. function diffWords(oldStr, newStr, options) {
  529. // This option has never been documented and never will be (it's clearer to
  530. // just call `diffWordsWithSpace` directly if you need that behavior), but
  531. // has existed in jsdiff for a long time, so we retain support for it here
  532. // for the sake of backwards compatibility.
  533. if ((options === null || options === void 0 ? void 0 : options.ignoreWhitespace) != null && !options.ignoreWhitespace) {
  534. return diffWordsWithSpace(oldStr, newStr, options);
  535. }
  536. return wordDiff.diff(oldStr, newStr, options);
  537. }
  538. function dedupeWhitespaceInChangeObjects(startKeep, deletion, insertion, endKeep) {
  539. // Before returning, we tidy up the leading and trailing whitespace of the
  540. // change objects to eliminate cases where trailing whitespace in one object
  541. // is repeated as leading whitespace in the next.
  542. // Below are examples of the outcomes we want here to explain the code.
  543. // I=insert, K=keep, D=delete
  544. // 1. diffing 'foo bar baz' vs 'foo baz'
  545. // Prior to cleanup, we have K:'foo ' D:' bar ' K:' baz'
  546. // After cleanup, we want: K:'foo ' D:'bar ' K:'baz'
  547. //
  548. // 2. Diffing 'foo bar baz' vs 'foo qux baz'
  549. // Prior to cleanup, we have K:'foo ' D:' bar ' I:' qux ' K:' baz'
  550. // After cleanup, we want K:'foo ' D:'bar' I:'qux' K:' baz'
  551. //
  552. // 3. Diffing 'foo\nbar baz' vs 'foo baz'
  553. // Prior to cleanup, we have K:'foo ' D:'\nbar ' K:' baz'
  554. // After cleanup, we want K'foo' D:'\nbar' K:' baz'
  555. //
  556. // 4. Diffing 'foo baz' vs 'foo\nbar baz'
  557. // Prior to cleanup, we have K:'foo\n' I:'\nbar ' K:' baz'
  558. // After cleanup, we ideally want K'foo' I:'\nbar' K:' baz'
  559. // but don't actually manage this currently (the pre-cleanup change
  560. // objects don't contain enough information to make it possible).
  561. //
  562. // 5. Diffing 'foo bar baz' vs 'foo baz'
  563. // Prior to cleanup, we have K:'foo ' D:' bar ' K:' baz'
  564. // After cleanup, we want K:'foo ' D:' bar ' K:'baz'
  565. //
  566. // Our handling is unavoidably imperfect in the case where there's a single
  567. // indel between keeps and the whitespace has changed. For instance, consider
  568. // diffing 'foo\tbar\nbaz' vs 'foo baz'. Unless we create an extra change
  569. // object to represent the insertion of the space character (which isn't even
  570. // a token), we have no way to avoid losing information about the texts'
  571. // original whitespace in the result we return. Still, we do our best to
  572. // output something that will look sensible if we e.g. print it with
  573. // insertions in green and deletions in red.
  574. // Between two "keep" change objects (or before the first or after the last
  575. // change object), we can have either:
  576. // * A "delete" followed by an "insert"
  577. // * Just an "insert"
  578. // * Just a "delete"
  579. // We handle the three cases separately.
  580. if (deletion && insertion) {
  581. var oldWsPrefix = deletion.value.match(/^\s*/)[0];
  582. var oldWsSuffix = deletion.value.match(/\s*$/)[0];
  583. var newWsPrefix = insertion.value.match(/^\s*/)[0];
  584. var newWsSuffix = insertion.value.match(/\s*$/)[0];
  585. if (startKeep) {
  586. var commonWsPrefix = longestCommonPrefix(oldWsPrefix, newWsPrefix);
  587. startKeep.value = replaceSuffix(startKeep.value, newWsPrefix, commonWsPrefix);
  588. deletion.value = removePrefix(deletion.value, commonWsPrefix);
  589. insertion.value = removePrefix(insertion.value, commonWsPrefix);
  590. }
  591. if (endKeep) {
  592. var commonWsSuffix = longestCommonSuffix(oldWsSuffix, newWsSuffix);
  593. endKeep.value = replacePrefix(endKeep.value, newWsSuffix, commonWsSuffix);
  594. deletion.value = removeSuffix(deletion.value, commonWsSuffix);
  595. insertion.value = removeSuffix(insertion.value, commonWsSuffix);
  596. }
  597. } else if (insertion) {
  598. // The whitespaces all reflect what was in the new text rather than
  599. // the old, so we essentially have no information about whitespace
  600. // insertion or deletion. We just want to dedupe the whitespace.
  601. // We do that by having each change object keep its trailing
  602. // whitespace and deleting duplicate leading whitespace where
  603. // present.
  604. if (startKeep) {
  605. insertion.value = insertion.value.replace(/^\s*/, '');
  606. }
  607. if (endKeep) {
  608. endKeep.value = endKeep.value.replace(/^\s*/, '');
  609. }
  610. // otherwise we've got a deletion and no insertion
  611. } else if (startKeep && endKeep) {
  612. var newWsFull = endKeep.value.match(/^\s*/)[0],
  613. delWsStart = deletion.value.match(/^\s*/)[0],
  614. delWsEnd = deletion.value.match(/\s*$/)[0];
  615. // Any whitespace that comes straight after startKeep in both the old and
  616. // new texts, assign to startKeep and remove from the deletion.
  617. var newWsStart = longestCommonPrefix(newWsFull, delWsStart);
  618. deletion.value = removePrefix(deletion.value, newWsStart);
  619. // Any whitespace that comes straight before endKeep in both the old and
  620. // new texts, and hasn't already been assigned to startKeep, assign to
  621. // endKeep and remove from the deletion.
  622. var newWsEnd = longestCommonSuffix(removePrefix(newWsFull, newWsStart), delWsEnd);
  623. deletion.value = removeSuffix(deletion.value, newWsEnd);
  624. endKeep.value = replacePrefix(endKeep.value, newWsFull, newWsEnd);
  625. // If there's any whitespace from the new text that HASN'T already been
  626. // assigned, assign it to the start:
  627. startKeep.value = replaceSuffix(startKeep.value, newWsFull, newWsFull.slice(0, newWsFull.length - newWsEnd.length));
  628. } else if (endKeep) {
  629. // We are at the start of the text. Preserve all the whitespace on
  630. // endKeep, and just remove whitespace from the end of deletion to the
  631. // extent that it overlaps with the start of endKeep.
  632. var endKeepWsPrefix = endKeep.value.match(/^\s*/)[0];
  633. var deletionWsSuffix = deletion.value.match(/\s*$/)[0];
  634. var overlap = maximumOverlap(deletionWsSuffix, endKeepWsPrefix);
  635. deletion.value = removeSuffix(deletion.value, overlap);
  636. } else if (startKeep) {
  637. // We are at the END of the text. Preserve all the whitespace on
  638. // startKeep, and just remove whitespace from the start of deletion to
  639. // the extent that it overlaps with the end of startKeep.
  640. var startKeepWsSuffix = startKeep.value.match(/\s*$/)[0];
  641. var deletionWsPrefix = deletion.value.match(/^\s*/)[0];
  642. var _overlap = maximumOverlap(startKeepWsSuffix, deletionWsPrefix);
  643. deletion.value = removePrefix(deletion.value, _overlap);
  644. }
  645. }
  646. var wordWithSpaceDiff = new Diff();
  647. wordWithSpaceDiff.tokenize = function (value) {
  648. // Slightly different to the tokenizeIncludingWhitespace regex used above in
  649. // that this one treats each individual newline as a distinct tokens, rather
  650. // than merging them into other surrounding whitespace. This was requested
  651. // in https://github.com/kpdecker/jsdiff/issues/180 &
  652. // https://github.com/kpdecker/jsdiff/issues/211
  653. var regex = new RegExp("(\\r?\\n)|[".concat(extendedWordChars, "]+|[^\\S\\n\\r]+|[^").concat(extendedWordChars, "]"), 'ug');
  654. return value.match(regex) || [];
  655. };
  656. function diffWordsWithSpace(oldStr, newStr, options) {
  657. return wordWithSpaceDiff.diff(oldStr, newStr, options);
  658. }
  659. function generateOptions(options, defaults) {
  660. if (typeof options === 'function') {
  661. defaults.callback = options;
  662. } else if (options) {
  663. for (var name in options) {
  664. /* istanbul ignore else */
  665. if (options.hasOwnProperty(name)) {
  666. defaults[name] = options[name];
  667. }
  668. }
  669. }
  670. return defaults;
  671. }
  672. var lineDiff = new Diff();
  673. lineDiff.tokenize = function (value, options) {
  674. if (options.stripTrailingCr) {
  675. // remove one \r before \n to match GNU diff's --strip-trailing-cr behavior
  676. value = value.replace(/\r\n/g, '\n');
  677. }
  678. var retLines = [],
  679. linesAndNewlines = value.split(/(\n|\r\n)/);
  680. // Ignore the final empty token that occurs if the string ends with a new line
  681. if (!linesAndNewlines[linesAndNewlines.length - 1]) {
  682. linesAndNewlines.pop();
  683. }
  684. // Merge the content and line separators into single tokens
  685. for (var i = 0; i < linesAndNewlines.length; i++) {
  686. var line = linesAndNewlines[i];
  687. if (i % 2 && !options.newlineIsToken) {
  688. retLines[retLines.length - 1] += line;
  689. } else {
  690. retLines.push(line);
  691. }
  692. }
  693. return retLines;
  694. };
  695. lineDiff.equals = function (left, right, options) {
  696. // If we're ignoring whitespace, we need to normalise lines by stripping
  697. // whitespace before checking equality. (This has an annoying interaction
  698. // with newlineIsToken that requires special handling: if newlines get their
  699. // own token, then we DON'T want to trim the *newline* tokens down to empty
  700. // strings, since this would cause us to treat whitespace-only line content
  701. // as equal to a separator between lines, which would be weird and
  702. // inconsistent with the documented behavior of the options.)
  703. if (options.ignoreWhitespace) {
  704. if (!options.newlineIsToken || !left.includes('\n')) {
  705. left = left.trim();
  706. }
  707. if (!options.newlineIsToken || !right.includes('\n')) {
  708. right = right.trim();
  709. }
  710. } else if (options.ignoreNewlineAtEof && !options.newlineIsToken) {
  711. if (left.endsWith('\n')) {
  712. left = left.slice(0, -1);
  713. }
  714. if (right.endsWith('\n')) {
  715. right = right.slice(0, -1);
  716. }
  717. }
  718. return Diff.prototype.equals.call(this, left, right, options);
  719. };
  720. function diffLines(oldStr, newStr, callback) {
  721. return lineDiff.diff(oldStr, newStr, callback);
  722. }
  723. // Kept for backwards compatibility. This is a rather arbitrary wrapper method
  724. // that just calls `diffLines` with `ignoreWhitespace: true`. It's confusing to
  725. // have two ways to do exactly the same thing in the API, so we no longer
  726. // document this one (library users should explicitly use `diffLines` with
  727. // `ignoreWhitespace: true` instead) but we keep it around to maintain
  728. // compatibility with code that used old versions.
  729. function diffTrimmedLines(oldStr, newStr, callback) {
  730. var options = generateOptions(callback, {
  731. ignoreWhitespace: true
  732. });
  733. return lineDiff.diff(oldStr, newStr, options);
  734. }
  735. var sentenceDiff = new Diff();
  736. sentenceDiff.tokenize = function (value) {
  737. return value.split(/(\S.+?[.!?])(?=\s+|$)/);
  738. };
  739. function diffSentences(oldStr, newStr, callback) {
  740. return sentenceDiff.diff(oldStr, newStr, callback);
  741. }
  742. var cssDiff = new Diff();
  743. cssDiff.tokenize = function (value) {
  744. return value.split(/([{}:;,]|\s+)/);
  745. };
  746. function diffCss(oldStr, newStr, callback) {
  747. return cssDiff.diff(oldStr, newStr, callback);
  748. }
  749. function ownKeys(e, r) {
  750. var t = Object.keys(e);
  751. if (Object.getOwnPropertySymbols) {
  752. var o = Object.getOwnPropertySymbols(e);
  753. r && (o = o.filter(function (r) {
  754. return Object.getOwnPropertyDescriptor(e, r).enumerable;
  755. })), t.push.apply(t, o);
  756. }
  757. return t;
  758. }
  759. function _objectSpread2(e) {
  760. for (var r = 1; r < arguments.length; r++) {
  761. var t = null != arguments[r] ? arguments[r] : {};
  762. r % 2 ? ownKeys(Object(t), !0).forEach(function (r) {
  763. _defineProperty(e, r, t[r]);
  764. }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) {
  765. Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r));
  766. });
  767. }
  768. return e;
  769. }
  770. function _toPrimitive(t, r) {
  771. if ("object" != typeof t || !t) return t;
  772. var e = t[Symbol.toPrimitive];
  773. if (void 0 !== e) {
  774. var i = e.call(t, r || "default");
  775. if ("object" != typeof i) return i;
  776. throw new TypeError("@@toPrimitive must return a primitive value.");
  777. }
  778. return ("string" === r ? String : Number)(t);
  779. }
  780. function _toPropertyKey(t) {
  781. var i = _toPrimitive(t, "string");
  782. return "symbol" == typeof i ? i : i + "";
  783. }
  784. function _typeof(o) {
  785. "@babel/helpers - typeof";
  786. return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
  787. return typeof o;
  788. } : function (o) {
  789. return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
  790. }, _typeof(o);
  791. }
  792. function _defineProperty(obj, key, value) {
  793. key = _toPropertyKey(key);
  794. if (key in obj) {
  795. Object.defineProperty(obj, key, {
  796. value: value,
  797. enumerable: true,
  798. configurable: true,
  799. writable: true
  800. });
  801. } else {
  802. obj[key] = value;
  803. }
  804. return obj;
  805. }
  806. function _toConsumableArray(arr) {
  807. return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
  808. }
  809. function _arrayWithoutHoles(arr) {
  810. if (Array.isArray(arr)) return _arrayLikeToArray(arr);
  811. }
  812. function _iterableToArray(iter) {
  813. if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
  814. }
  815. function _unsupportedIterableToArray(o, minLen) {
  816. if (!o) return;
  817. if (typeof o === "string") return _arrayLikeToArray(o, minLen);
  818. var n = Object.prototype.toString.call(o).slice(8, -1);
  819. if (n === "Object" && o.constructor) n = o.constructor.name;
  820. if (n === "Map" || n === "Set") return Array.from(o);
  821. if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
  822. }
  823. function _arrayLikeToArray(arr, len) {
  824. if (len == null || len > arr.length) len = arr.length;
  825. for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
  826. return arr2;
  827. }
  828. function _nonIterableSpread() {
  829. throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
  830. }
  831. var jsonDiff = new Diff();
  832. // Discriminate between two lines of pretty-printed, serialized JSON where one of them has a
  833. // dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output:
  834. jsonDiff.useLongestToken = true;
  835. jsonDiff.tokenize = lineDiff.tokenize;
  836. jsonDiff.castInput = function (value, options) {
  837. var undefinedReplacement = options.undefinedReplacement,
  838. _options$stringifyRep = options.stringifyReplacer,
  839. stringifyReplacer = _options$stringifyRep === void 0 ? function (k, v) {
  840. return typeof v === 'undefined' ? undefinedReplacement : v;
  841. } : _options$stringifyRep;
  842. return typeof value === 'string' ? value : JSON.stringify(canonicalize(value, null, null, stringifyReplacer), stringifyReplacer, ' ');
  843. };
  844. jsonDiff.equals = function (left, right, options) {
  845. return Diff.prototype.equals.call(jsonDiff, left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1'), options);
  846. };
  847. function diffJson(oldObj, newObj, options) {
  848. return jsonDiff.diff(oldObj, newObj, options);
  849. }
  850. // This function handles the presence of circular references by bailing out when encountering an
  851. // object that is already on the "stack" of items being processed. Accepts an optional replacer
  852. function canonicalize(obj, stack, replacementStack, replacer, key) {
  853. stack = stack || [];
  854. replacementStack = replacementStack || [];
  855. if (replacer) {
  856. obj = replacer(key, obj);
  857. }
  858. var i;
  859. for (i = 0; i < stack.length; i += 1) {
  860. if (stack[i] === obj) {
  861. return replacementStack[i];
  862. }
  863. }
  864. var canonicalizedObj;
  865. if ('[object Array]' === Object.prototype.toString.call(obj)) {
  866. stack.push(obj);
  867. canonicalizedObj = new Array(obj.length);
  868. replacementStack.push(canonicalizedObj);
  869. for (i = 0; i < obj.length; i += 1) {
  870. canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack, replacer, key);
  871. }
  872. stack.pop();
  873. replacementStack.pop();
  874. return canonicalizedObj;
  875. }
  876. if (obj && obj.toJSON) {
  877. obj = obj.toJSON();
  878. }
  879. if (_typeof(obj) === 'object' && obj !== null) {
  880. stack.push(obj);
  881. canonicalizedObj = {};
  882. replacementStack.push(canonicalizedObj);
  883. var sortedKeys = [],
  884. _key;
  885. for (_key in obj) {
  886. /* istanbul ignore else */
  887. if (Object.prototype.hasOwnProperty.call(obj, _key)) {
  888. sortedKeys.push(_key);
  889. }
  890. }
  891. sortedKeys.sort();
  892. for (i = 0; i < sortedKeys.length; i += 1) {
  893. _key = sortedKeys[i];
  894. canonicalizedObj[_key] = canonicalize(obj[_key], stack, replacementStack, replacer, _key);
  895. }
  896. stack.pop();
  897. replacementStack.pop();
  898. } else {
  899. canonicalizedObj = obj;
  900. }
  901. return canonicalizedObj;
  902. }
  903. var arrayDiff = new Diff();
  904. arrayDiff.tokenize = function (value) {
  905. return value.slice();
  906. };
  907. arrayDiff.join = arrayDiff.removeEmpty = function (value) {
  908. return value;
  909. };
  910. function diffArrays(oldArr, newArr, callback) {
  911. return arrayDiff.diff(oldArr, newArr, callback);
  912. }
  913. function unixToWin(patch) {
  914. if (Array.isArray(patch)) {
  915. return patch.map(unixToWin);
  916. }
  917. return _objectSpread2(_objectSpread2({}, patch), {}, {
  918. hunks: patch.hunks.map(function (hunk) {
  919. return _objectSpread2(_objectSpread2({}, hunk), {}, {
  920. lines: hunk.lines.map(function (line, i) {
  921. var _hunk$lines;
  922. return line.startsWith('\\') || line.endsWith('\r') || (_hunk$lines = hunk.lines[i + 1]) !== null && _hunk$lines !== void 0 && _hunk$lines.startsWith('\\') ? line : line + '\r';
  923. })
  924. });
  925. })
  926. });
  927. }
  928. function winToUnix(patch) {
  929. if (Array.isArray(patch)) {
  930. return patch.map(winToUnix);
  931. }
  932. return _objectSpread2(_objectSpread2({}, patch), {}, {
  933. hunks: patch.hunks.map(function (hunk) {
  934. return _objectSpread2(_objectSpread2({}, hunk), {}, {
  935. lines: hunk.lines.map(function (line) {
  936. return line.endsWith('\r') ? line.substring(0, line.length - 1) : line;
  937. })
  938. });
  939. })
  940. });
  941. }
  942. /**
  943. * Returns true if the patch consistently uses Unix line endings (or only involves one line and has
  944. * no line endings).
  945. */
  946. function isUnix(patch) {
  947. if (!Array.isArray(patch)) {
  948. patch = [patch];
  949. }
  950. return !patch.some(function (index) {
  951. return index.hunks.some(function (hunk) {
  952. return hunk.lines.some(function (line) {
  953. return !line.startsWith('\\') && line.endsWith('\r');
  954. });
  955. });
  956. });
  957. }
  958. /**
  959. * Returns true if the patch uses Windows line endings and only Windows line endings.
  960. */
  961. function isWin(patch) {
  962. if (!Array.isArray(patch)) {
  963. patch = [patch];
  964. }
  965. return patch.some(function (index) {
  966. return index.hunks.some(function (hunk) {
  967. return hunk.lines.some(function (line) {
  968. return line.endsWith('\r');
  969. });
  970. });
  971. }) && patch.every(function (index) {
  972. return index.hunks.every(function (hunk) {
  973. return hunk.lines.every(function (line, i) {
  974. var _hunk$lines2;
  975. return line.startsWith('\\') || line.endsWith('\r') || ((_hunk$lines2 = hunk.lines[i + 1]) === null || _hunk$lines2 === void 0 ? void 0 : _hunk$lines2.startsWith('\\'));
  976. });
  977. });
  978. });
  979. }
  980. function parsePatch(uniDiff) {
  981. var diffstr = uniDiff.split(/\n/),
  982. list = [],
  983. i = 0;
  984. function parseIndex() {
  985. var index = {};
  986. list.push(index);
  987. // Parse diff metadata
  988. while (i < diffstr.length) {
  989. var line = diffstr[i];
  990. // File header found, end parsing diff metadata
  991. if (/^(\-\-\-|\+\+\+|@@)\s/.test(line)) {
  992. break;
  993. }
  994. // Diff index
  995. var header = /^(?:Index:|diff(?: -r \w+)+)\s+(.+?)\s*$/.exec(line);
  996. if (header) {
  997. index.index = header[1];
  998. }
  999. i++;
  1000. }
  1001. // Parse file headers if they are defined. Unified diff requires them, but
  1002. // there's no technical issues to have an isolated hunk without file header
  1003. parseFileHeader(index);
  1004. parseFileHeader(index);
  1005. // Parse hunks
  1006. index.hunks = [];
  1007. while (i < diffstr.length) {
  1008. var _line = diffstr[i];
  1009. if (/^(Index:\s|diff\s|\-\-\-\s|\+\+\+\s|===================================================================)/.test(_line)) {
  1010. break;
  1011. } else if (/^@@/.test(_line)) {
  1012. index.hunks.push(parseHunk());
  1013. } else if (_line) {
  1014. throw new Error('Unknown line ' + (i + 1) + ' ' + JSON.stringify(_line));
  1015. } else {
  1016. i++;
  1017. }
  1018. }
  1019. }
  1020. // Parses the --- and +++ headers, if none are found, no lines
  1021. // are consumed.
  1022. function parseFileHeader(index) {
  1023. var fileHeader = /^(---|\+\+\+)\s+(.*)\r?$/.exec(diffstr[i]);
  1024. if (fileHeader) {
  1025. var keyPrefix = fileHeader[1] === '---' ? 'old' : 'new';
  1026. var data = fileHeader[2].split('\t', 2);
  1027. var fileName = data[0].replace(/\\\\/g, '\\');
  1028. if (/^".*"$/.test(fileName)) {
  1029. fileName = fileName.substr(1, fileName.length - 2);
  1030. }
  1031. index[keyPrefix + 'FileName'] = fileName;
  1032. index[keyPrefix + 'Header'] = (data[1] || '').trim();
  1033. i++;
  1034. }
  1035. }
  1036. // Parses a hunk
  1037. // This assumes that we are at the start of a hunk.
  1038. function parseHunk() {
  1039. var chunkHeaderIndex = i,
  1040. chunkHeaderLine = diffstr[i++],
  1041. chunkHeader = chunkHeaderLine.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/);
  1042. var hunk = {
  1043. oldStart: +chunkHeader[1],
  1044. oldLines: typeof chunkHeader[2] === 'undefined' ? 1 : +chunkHeader[2],
  1045. newStart: +chunkHeader[3],
  1046. newLines: typeof chunkHeader[4] === 'undefined' ? 1 : +chunkHeader[4],
  1047. lines: []
  1048. };
  1049. // Unified Diff Format quirk: If the chunk size is 0,
  1050. // the first number is one lower than one would expect.
  1051. // https://www.artima.com/weblogs/viewpost.jsp?thread=164293
  1052. if (hunk.oldLines === 0) {
  1053. hunk.oldStart += 1;
  1054. }
  1055. if (hunk.newLines === 0) {
  1056. hunk.newStart += 1;
  1057. }
  1058. var addCount = 0,
  1059. removeCount = 0;
  1060. for (; i < diffstr.length && (removeCount < hunk.oldLines || addCount < hunk.newLines || (_diffstr$i = diffstr[i]) !== null && _diffstr$i !== void 0 && _diffstr$i.startsWith('\\')); i++) {
  1061. var _diffstr$i;
  1062. var operation = diffstr[i].length == 0 && i != diffstr.length - 1 ? ' ' : diffstr[i][0];
  1063. if (operation === '+' || operation === '-' || operation === ' ' || operation === '\\') {
  1064. hunk.lines.push(diffstr[i]);
  1065. if (operation === '+') {
  1066. addCount++;
  1067. } else if (operation === '-') {
  1068. removeCount++;
  1069. } else if (operation === ' ') {
  1070. addCount++;
  1071. removeCount++;
  1072. }
  1073. } else {
  1074. throw new Error("Hunk at line ".concat(chunkHeaderIndex + 1, " contained invalid line ").concat(diffstr[i]));
  1075. }
  1076. }
  1077. // Handle the empty block count case
  1078. if (!addCount && hunk.newLines === 1) {
  1079. hunk.newLines = 0;
  1080. }
  1081. if (!removeCount && hunk.oldLines === 1) {
  1082. hunk.oldLines = 0;
  1083. }
  1084. // Perform sanity checking
  1085. if (addCount !== hunk.newLines) {
  1086. throw new Error('Added line count did not match for hunk at line ' + (chunkHeaderIndex + 1));
  1087. }
  1088. if (removeCount !== hunk.oldLines) {
  1089. throw new Error('Removed line count did not match for hunk at line ' + (chunkHeaderIndex + 1));
  1090. }
  1091. return hunk;
  1092. }
  1093. while (i < diffstr.length) {
  1094. parseIndex();
  1095. }
  1096. return list;
  1097. }
  1098. // Iterator that traverses in the range of [min, max], stepping
  1099. // by distance from a given start position. I.e. for [0, 4], with
  1100. // start of 2, this will iterate 2, 3, 1, 4, 0.
  1101. function distanceIterator (start, minLine, maxLine) {
  1102. var wantForward = true,
  1103. backwardExhausted = false,
  1104. forwardExhausted = false,
  1105. localOffset = 1;
  1106. return function iterator() {
  1107. if (wantForward && !forwardExhausted) {
  1108. if (backwardExhausted) {
  1109. localOffset++;
  1110. } else {
  1111. wantForward = false;
  1112. }
  1113. // Check if trying to fit beyond text length, and if not, check it fits
  1114. // after offset location (or desired location on first iteration)
  1115. if (start + localOffset <= maxLine) {
  1116. return start + localOffset;
  1117. }
  1118. forwardExhausted = true;
  1119. }
  1120. if (!backwardExhausted) {
  1121. if (!forwardExhausted) {
  1122. wantForward = true;
  1123. }
  1124. // Check if trying to fit before text beginning, and if not, check it fits
  1125. // before offset location
  1126. if (minLine <= start - localOffset) {
  1127. return start - localOffset++;
  1128. }
  1129. backwardExhausted = true;
  1130. return iterator();
  1131. }
  1132. // We tried to fit hunk before text beginning and beyond text length, then
  1133. // hunk can't fit on the text. Return undefined
  1134. };
  1135. }
  1136. function applyPatch(source, uniDiff) {
  1137. var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  1138. if (typeof uniDiff === 'string') {
  1139. uniDiff = parsePatch(uniDiff);
  1140. }
  1141. if (Array.isArray(uniDiff)) {
  1142. if (uniDiff.length > 1) {
  1143. throw new Error('applyPatch only works with a single input.');
  1144. }
  1145. uniDiff = uniDiff[0];
  1146. }
  1147. if (options.autoConvertLineEndings || options.autoConvertLineEndings == null) {
  1148. if (hasOnlyWinLineEndings(source) && isUnix(uniDiff)) {
  1149. uniDiff = unixToWin(uniDiff);
  1150. } else if (hasOnlyUnixLineEndings(source) && isWin(uniDiff)) {
  1151. uniDiff = winToUnix(uniDiff);
  1152. }
  1153. }
  1154. // Apply the diff to the input
  1155. var lines = source.split('\n'),
  1156. hunks = uniDiff.hunks,
  1157. compareLine = options.compareLine || function (lineNumber, line, operation, patchContent) {
  1158. return line === patchContent;
  1159. },
  1160. fuzzFactor = options.fuzzFactor || 0,
  1161. minLine = 0;
  1162. if (fuzzFactor < 0 || !Number.isInteger(fuzzFactor)) {
  1163. throw new Error('fuzzFactor must be a non-negative integer');
  1164. }
  1165. // Special case for empty patch.
  1166. if (!hunks.length) {
  1167. return source;
  1168. }
  1169. // Before anything else, handle EOFNL insertion/removal. If the patch tells us to make a change
  1170. // to the EOFNL that is redundant/impossible - i.e. to remove a newline that's not there, or add a
  1171. // newline that already exists - then we either return false and fail to apply the patch (if
  1172. // fuzzFactor is 0) or simply ignore the problem and do nothing (if fuzzFactor is >0).
  1173. // If we do need to remove/add a newline at EOF, this will always be in the final hunk:
  1174. var prevLine = '',
  1175. removeEOFNL = false,
  1176. addEOFNL = false;
  1177. for (var i = 0; i < hunks[hunks.length - 1].lines.length; i++) {
  1178. var line = hunks[hunks.length - 1].lines[i];
  1179. if (line[0] == '\\') {
  1180. if (prevLine[0] == '+') {
  1181. removeEOFNL = true;
  1182. } else if (prevLine[0] == '-') {
  1183. addEOFNL = true;
  1184. }
  1185. }
  1186. prevLine = line;
  1187. }
  1188. if (removeEOFNL) {
  1189. if (addEOFNL) {
  1190. // This means the final line gets changed but doesn't have a trailing newline in either the
  1191. // original or patched version. In that case, we do nothing if fuzzFactor > 0, and if
  1192. // fuzzFactor is 0, we simply validate that the source file has no trailing newline.
  1193. if (!fuzzFactor && lines[lines.length - 1] == '') {
  1194. return false;
  1195. }
  1196. } else if (lines[lines.length - 1] == '') {
  1197. lines.pop();
  1198. } else if (!fuzzFactor) {
  1199. return false;
  1200. }
  1201. } else if (addEOFNL) {
  1202. if (lines[lines.length - 1] != '') {
  1203. lines.push('');
  1204. } else if (!fuzzFactor) {
  1205. return false;
  1206. }
  1207. }
  1208. /**
  1209. * Checks if the hunk can be made to fit at the provided location with at most `maxErrors`
  1210. * insertions, substitutions, or deletions, while ensuring also that:
  1211. * - lines deleted in the hunk match exactly, and
  1212. * - wherever an insertion operation or block of insertion operations appears in the hunk, the
  1213. * immediately preceding and following lines of context match exactly
  1214. *
  1215. * `toPos` should be set such that lines[toPos] is meant to match hunkLines[0].
  1216. *
  1217. * If the hunk can be applied, returns an object with properties `oldLineLastI` and
  1218. * `replacementLines`. Otherwise, returns null.
  1219. */
  1220. function applyHunk(hunkLines, toPos, maxErrors) {
  1221. var hunkLinesI = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
  1222. var lastContextLineMatched = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
  1223. var patchedLines = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : [];
  1224. var patchedLinesLength = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 0;
  1225. var nConsecutiveOldContextLines = 0;
  1226. var nextContextLineMustMatch = false;
  1227. for (; hunkLinesI < hunkLines.length; hunkLinesI++) {
  1228. var hunkLine = hunkLines[hunkLinesI],
  1229. operation = hunkLine.length > 0 ? hunkLine[0] : ' ',
  1230. content = hunkLine.length > 0 ? hunkLine.substr(1) : hunkLine;
  1231. if (operation === '-') {
  1232. if (compareLine(toPos + 1, lines[toPos], operation, content)) {
  1233. toPos++;
  1234. nConsecutiveOldContextLines = 0;
  1235. } else {
  1236. if (!maxErrors || lines[toPos] == null) {
  1237. return null;
  1238. }
  1239. patchedLines[patchedLinesLength] = lines[toPos];
  1240. return applyHunk(hunkLines, toPos + 1, maxErrors - 1, hunkLinesI, false, patchedLines, patchedLinesLength + 1);
  1241. }
  1242. }
  1243. if (operation === '+') {
  1244. if (!lastContextLineMatched) {
  1245. return null;
  1246. }
  1247. patchedLines[patchedLinesLength] = content;
  1248. patchedLinesLength++;
  1249. nConsecutiveOldContextLines = 0;
  1250. nextContextLineMustMatch = true;
  1251. }
  1252. if (operation === ' ') {
  1253. nConsecutiveOldContextLines++;
  1254. patchedLines[patchedLinesLength] = lines[toPos];
  1255. if (compareLine(toPos + 1, lines[toPos], operation, content)) {
  1256. patchedLinesLength++;
  1257. lastContextLineMatched = true;
  1258. nextContextLineMustMatch = false;
  1259. toPos++;
  1260. } else {
  1261. if (nextContextLineMustMatch || !maxErrors) {
  1262. return null;
  1263. }
  1264. // Consider 3 possibilities in sequence:
  1265. // 1. lines contains a *substitution* not included in the patch context, or
  1266. // 2. lines contains an *insertion* not included in the patch context, or
  1267. // 3. lines contains a *deletion* not included in the patch context
  1268. // The first two options are of course only possible if the line from lines is non-null -
  1269. // i.e. only option 3 is possible if we've overrun the end of the old file.
  1270. return lines[toPos] && (applyHunk(hunkLines, toPos + 1, maxErrors - 1, hunkLinesI + 1, false, patchedLines, patchedLinesLength + 1) || applyHunk(hunkLines, toPos + 1, maxErrors - 1, hunkLinesI, false, patchedLines, patchedLinesLength + 1)) || applyHunk(hunkLines, toPos, maxErrors - 1, hunkLinesI + 1, false, patchedLines, patchedLinesLength);
  1271. }
  1272. }
  1273. }
  1274. // Before returning, trim any unmodified context lines off the end of patchedLines and reduce
  1275. // toPos (and thus oldLineLastI) accordingly. This allows later hunks to be applied to a region
  1276. // that starts in this hunk's trailing context.
  1277. patchedLinesLength -= nConsecutiveOldContextLines;
  1278. toPos -= nConsecutiveOldContextLines;
  1279. patchedLines.length = patchedLinesLength;
  1280. return {
  1281. patchedLines: patchedLines,
  1282. oldLineLastI: toPos - 1
  1283. };
  1284. }
  1285. var resultLines = [];
  1286. // Search best fit offsets for each hunk based on the previous ones
  1287. var prevHunkOffset = 0;
  1288. for (var _i = 0; _i < hunks.length; _i++) {
  1289. var hunk = hunks[_i];
  1290. var hunkResult = void 0;
  1291. var maxLine = lines.length - hunk.oldLines + fuzzFactor;
  1292. var toPos = void 0;
  1293. for (var maxErrors = 0; maxErrors <= fuzzFactor; maxErrors++) {
  1294. toPos = hunk.oldStart + prevHunkOffset - 1;
  1295. var iterator = distanceIterator(toPos, minLine, maxLine);
  1296. for (; toPos !== undefined; toPos = iterator()) {
  1297. hunkResult = applyHunk(hunk.lines, toPos, maxErrors);
  1298. if (hunkResult) {
  1299. break;
  1300. }
  1301. }
  1302. if (hunkResult) {
  1303. break;
  1304. }
  1305. }
  1306. if (!hunkResult) {
  1307. return false;
  1308. }
  1309. // Copy everything from the end of where we applied the last hunk to the start of this hunk
  1310. for (var _i2 = minLine; _i2 < toPos; _i2++) {
  1311. resultLines.push(lines[_i2]);
  1312. }
  1313. // Add the lines produced by applying the hunk:
  1314. for (var _i3 = 0; _i3 < hunkResult.patchedLines.length; _i3++) {
  1315. var _line = hunkResult.patchedLines[_i3];
  1316. resultLines.push(_line);
  1317. }
  1318. // Set lower text limit to end of the current hunk, so next ones don't try
  1319. // to fit over already patched text
  1320. minLine = hunkResult.oldLineLastI + 1;
  1321. // Note the offset between where the patch said the hunk should've applied and where we
  1322. // applied it, so we can adjust future hunks accordingly:
  1323. prevHunkOffset = toPos + 1 - hunk.oldStart;
  1324. }
  1325. // Copy over the rest of the lines from the old text
  1326. for (var _i4 = minLine; _i4 < lines.length; _i4++) {
  1327. resultLines.push(lines[_i4]);
  1328. }
  1329. return resultLines.join('\n');
  1330. }
  1331. // Wrapper that supports multiple file patches via callbacks.
  1332. function applyPatches(uniDiff, options) {
  1333. if (typeof uniDiff === 'string') {
  1334. uniDiff = parsePatch(uniDiff);
  1335. }
  1336. var currentIndex = 0;
  1337. function processIndex() {
  1338. var index = uniDiff[currentIndex++];
  1339. if (!index) {
  1340. return options.complete();
  1341. }
  1342. options.loadFile(index, function (err, data) {
  1343. if (err) {
  1344. return options.complete(err);
  1345. }
  1346. var updatedContent = applyPatch(data, index, options);
  1347. options.patched(index, updatedContent, function (err) {
  1348. if (err) {
  1349. return options.complete(err);
  1350. }
  1351. processIndex();
  1352. });
  1353. });
  1354. }
  1355. processIndex();
  1356. }
  1357. function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
  1358. if (!options) {
  1359. options = {};
  1360. }
  1361. if (typeof options === 'function') {
  1362. options = {
  1363. callback: options
  1364. };
  1365. }
  1366. if (typeof options.context === 'undefined') {
  1367. options.context = 4;
  1368. }
  1369. if (options.newlineIsToken) {
  1370. throw new Error('newlineIsToken may not be used with patch-generation functions, only with diffing functions');
  1371. }
  1372. if (!options.callback) {
  1373. return diffLinesResultToPatch(diffLines(oldStr, newStr, options));
  1374. } else {
  1375. var _options = options,
  1376. _callback = _options.callback;
  1377. diffLines(oldStr, newStr, _objectSpread2(_objectSpread2({}, options), {}, {
  1378. callback: function callback(diff) {
  1379. var patch = diffLinesResultToPatch(diff);
  1380. _callback(patch);
  1381. }
  1382. }));
  1383. }
  1384. function diffLinesResultToPatch(diff) {
  1385. // STEP 1: Build up the patch with no "\ No newline at end of file" lines and with the arrays
  1386. // of lines containing trailing newline characters. We'll tidy up later...
  1387. if (!diff) {
  1388. return;
  1389. }
  1390. diff.push({
  1391. value: '',
  1392. lines: []
  1393. }); // Append an empty value to make cleanup easier
  1394. function contextLines(lines) {
  1395. return lines.map(function (entry) {
  1396. return ' ' + entry;
  1397. });
  1398. }
  1399. var hunks = [];
  1400. var oldRangeStart = 0,
  1401. newRangeStart = 0,
  1402. curRange = [],
  1403. oldLine = 1,
  1404. newLine = 1;
  1405. var _loop = function _loop() {
  1406. var current = diff[i],
  1407. lines = current.lines || splitLines(current.value);
  1408. current.lines = lines;
  1409. if (current.added || current.removed) {
  1410. var _curRange;
  1411. // If we have previous context, start with that
  1412. if (!oldRangeStart) {
  1413. var prev = diff[i - 1];
  1414. oldRangeStart = oldLine;
  1415. newRangeStart = newLine;
  1416. if (prev) {
  1417. curRange = options.context > 0 ? contextLines(prev.lines.slice(-options.context)) : [];
  1418. oldRangeStart -= curRange.length;
  1419. newRangeStart -= curRange.length;
  1420. }
  1421. }
  1422. // Output our changes
  1423. (_curRange = curRange).push.apply(_curRange, _toConsumableArray(lines.map(function (entry) {
  1424. return (current.added ? '+' : '-') + entry;
  1425. })));
  1426. // Track the updated file position
  1427. if (current.added) {
  1428. newLine += lines.length;
  1429. } else {
  1430. oldLine += lines.length;
  1431. }
  1432. } else {
  1433. // Identical context lines. Track line changes
  1434. if (oldRangeStart) {
  1435. // Close out any changes that have been output (or join overlapping)
  1436. if (lines.length <= options.context * 2 && i < diff.length - 2) {
  1437. var _curRange2;
  1438. // Overlapping
  1439. (_curRange2 = curRange).push.apply(_curRange2, _toConsumableArray(contextLines(lines)));
  1440. } else {
  1441. var _curRange3;
  1442. // end the range and output
  1443. var contextSize = Math.min(lines.length, options.context);
  1444. (_curRange3 = curRange).push.apply(_curRange3, _toConsumableArray(contextLines(lines.slice(0, contextSize))));
  1445. var _hunk = {
  1446. oldStart: oldRangeStart,
  1447. oldLines: oldLine - oldRangeStart + contextSize,
  1448. newStart: newRangeStart,
  1449. newLines: newLine - newRangeStart + contextSize,
  1450. lines: curRange
  1451. };
  1452. hunks.push(_hunk);
  1453. oldRangeStart = 0;
  1454. newRangeStart = 0;
  1455. curRange = [];
  1456. }
  1457. }
  1458. oldLine += lines.length;
  1459. newLine += lines.length;
  1460. }
  1461. };
  1462. for (var i = 0; i < diff.length; i++) {
  1463. _loop();
  1464. }
  1465. // Step 2: eliminate the trailing `\n` from each line of each hunk, and, where needed, add
  1466. // "\ No newline at end of file".
  1467. for (var _i = 0, _hunks = hunks; _i < _hunks.length; _i++) {
  1468. var hunk = _hunks[_i];
  1469. for (var _i2 = 0; _i2 < hunk.lines.length; _i2++) {
  1470. if (hunk.lines[_i2].endsWith('\n')) {
  1471. hunk.lines[_i2] = hunk.lines[_i2].slice(0, -1);
  1472. } else {
  1473. hunk.lines.splice(_i2 + 1, 0, '\\ No newline at end of file');
  1474. _i2++; // Skip the line we just added, then continue iterating
  1475. }
  1476. }
  1477. }
  1478. return {
  1479. oldFileName: oldFileName,
  1480. newFileName: newFileName,
  1481. oldHeader: oldHeader,
  1482. newHeader: newHeader,
  1483. hunks: hunks
  1484. };
  1485. }
  1486. }
  1487. function formatPatch(diff) {
  1488. if (Array.isArray(diff)) {
  1489. return diff.map(formatPatch).join('\n');
  1490. }
  1491. var ret = [];
  1492. if (diff.oldFileName == diff.newFileName) {
  1493. ret.push('Index: ' + diff.oldFileName);
  1494. }
  1495. ret.push('===================================================================');
  1496. ret.push('--- ' + diff.oldFileName + (typeof diff.oldHeader === 'undefined' ? '' : '\t' + diff.oldHeader));
  1497. ret.push('+++ ' + diff.newFileName + (typeof diff.newHeader === 'undefined' ? '' : '\t' + diff.newHeader));
  1498. for (var i = 0; i < diff.hunks.length; i++) {
  1499. var hunk = diff.hunks[i];
  1500. // Unified Diff Format quirk: If the chunk size is 0,
  1501. // the first number is one lower than one would expect.
  1502. // https://www.artima.com/weblogs/viewpost.jsp?thread=164293
  1503. if (hunk.oldLines === 0) {
  1504. hunk.oldStart -= 1;
  1505. }
  1506. if (hunk.newLines === 0) {
  1507. hunk.newStart -= 1;
  1508. }
  1509. ret.push('@@ -' + hunk.oldStart + ',' + hunk.oldLines + ' +' + hunk.newStart + ',' + hunk.newLines + ' @@');
  1510. ret.push.apply(ret, hunk.lines);
  1511. }
  1512. return ret.join('\n') + '\n';
  1513. }
  1514. function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
  1515. var _options2;
  1516. if (typeof options === 'function') {
  1517. options = {
  1518. callback: options
  1519. };
  1520. }
  1521. if (!((_options2 = options) !== null && _options2 !== void 0 && _options2.callback)) {
  1522. var patchObj = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options);
  1523. if (!patchObj) {
  1524. return;
  1525. }
  1526. return formatPatch(patchObj);
  1527. } else {
  1528. var _options3 = options,
  1529. _callback2 = _options3.callback;
  1530. structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, _objectSpread2(_objectSpread2({}, options), {}, {
  1531. callback: function callback(patchObj) {
  1532. if (!patchObj) {
  1533. _callback2();
  1534. } else {
  1535. _callback2(formatPatch(patchObj));
  1536. }
  1537. }
  1538. }));
  1539. }
  1540. }
  1541. function createPatch(fileName, oldStr, newStr, oldHeader, newHeader, options) {
  1542. return createTwoFilesPatch(fileName, fileName, oldStr, newStr, oldHeader, newHeader, options);
  1543. }
  1544. /**
  1545. * Split `text` into an array of lines, including the trailing newline character (where present)
  1546. */
  1547. function splitLines(text) {
  1548. var hasTrailingNl = text.endsWith('\n');
  1549. var result = text.split('\n').map(function (line) {
  1550. return line + '\n';
  1551. });
  1552. if (hasTrailingNl) {
  1553. result.pop();
  1554. } else {
  1555. result.push(result.pop().slice(0, -1));
  1556. }
  1557. return result;
  1558. }
  1559. function arrayEqual(a, b) {
  1560. if (a.length !== b.length) {
  1561. return false;
  1562. }
  1563. return arrayStartsWith(a, b);
  1564. }
  1565. function arrayStartsWith(array, start) {
  1566. if (start.length > array.length) {
  1567. return false;
  1568. }
  1569. for (var i = 0; i < start.length; i++) {
  1570. if (start[i] !== array[i]) {
  1571. return false;
  1572. }
  1573. }
  1574. return true;
  1575. }
  1576. function calcLineCount(hunk) {
  1577. var _calcOldNewLineCount = calcOldNewLineCount(hunk.lines),
  1578. oldLines = _calcOldNewLineCount.oldLines,
  1579. newLines = _calcOldNewLineCount.newLines;
  1580. if (oldLines !== undefined) {
  1581. hunk.oldLines = oldLines;
  1582. } else {
  1583. delete hunk.oldLines;
  1584. }
  1585. if (newLines !== undefined) {
  1586. hunk.newLines = newLines;
  1587. } else {
  1588. delete hunk.newLines;
  1589. }
  1590. }
  1591. function merge(mine, theirs, base) {
  1592. mine = loadPatch(mine, base);
  1593. theirs = loadPatch(theirs, base);
  1594. var ret = {};
  1595. // For index we just let it pass through as it doesn't have any necessary meaning.
  1596. // Leaving sanity checks on this to the API consumer that may know more about the
  1597. // meaning in their own context.
  1598. if (mine.index || theirs.index) {
  1599. ret.index = mine.index || theirs.index;
  1600. }
  1601. if (mine.newFileName || theirs.newFileName) {
  1602. if (!fileNameChanged(mine)) {
  1603. // No header or no change in ours, use theirs (and ours if theirs does not exist)
  1604. ret.oldFileName = theirs.oldFileName || mine.oldFileName;
  1605. ret.newFileName = theirs.newFileName || mine.newFileName;
  1606. ret.oldHeader = theirs.oldHeader || mine.oldHeader;
  1607. ret.newHeader = theirs.newHeader || mine.newHeader;
  1608. } else if (!fileNameChanged(theirs)) {
  1609. // No header or no change in theirs, use ours
  1610. ret.oldFileName = mine.oldFileName;
  1611. ret.newFileName = mine.newFileName;
  1612. ret.oldHeader = mine.oldHeader;
  1613. ret.newHeader = mine.newHeader;
  1614. } else {
  1615. // Both changed... figure it out
  1616. ret.oldFileName = selectField(ret, mine.oldFileName, theirs.oldFileName);
  1617. ret.newFileName = selectField(ret, mine.newFileName, theirs.newFileName);
  1618. ret.oldHeader = selectField(ret, mine.oldHeader, theirs.oldHeader);
  1619. ret.newHeader = selectField(ret, mine.newHeader, theirs.newHeader);
  1620. }
  1621. }
  1622. ret.hunks = [];
  1623. var mineIndex = 0,
  1624. theirsIndex = 0,
  1625. mineOffset = 0,
  1626. theirsOffset = 0;
  1627. while (mineIndex < mine.hunks.length || theirsIndex < theirs.hunks.length) {
  1628. var mineCurrent = mine.hunks[mineIndex] || {
  1629. oldStart: Infinity
  1630. },
  1631. theirsCurrent = theirs.hunks[theirsIndex] || {
  1632. oldStart: Infinity
  1633. };
  1634. if (hunkBefore(mineCurrent, theirsCurrent)) {
  1635. // This patch does not overlap with any of the others, yay.
  1636. ret.hunks.push(cloneHunk(mineCurrent, mineOffset));
  1637. mineIndex++;
  1638. theirsOffset += mineCurrent.newLines - mineCurrent.oldLines;
  1639. } else if (hunkBefore(theirsCurrent, mineCurrent)) {
  1640. // This patch does not overlap with any of the others, yay.
  1641. ret.hunks.push(cloneHunk(theirsCurrent, theirsOffset));
  1642. theirsIndex++;
  1643. mineOffset += theirsCurrent.newLines - theirsCurrent.oldLines;
  1644. } else {
  1645. // Overlap, merge as best we can
  1646. var mergedHunk = {
  1647. oldStart: Math.min(mineCurrent.oldStart, theirsCurrent.oldStart),
  1648. oldLines: 0,
  1649. newStart: Math.min(mineCurrent.newStart + mineOffset, theirsCurrent.oldStart + theirsOffset),
  1650. newLines: 0,
  1651. lines: []
  1652. };
  1653. mergeLines(mergedHunk, mineCurrent.oldStart, mineCurrent.lines, theirsCurrent.oldStart, theirsCurrent.lines);
  1654. theirsIndex++;
  1655. mineIndex++;
  1656. ret.hunks.push(mergedHunk);
  1657. }
  1658. }
  1659. return ret;
  1660. }
  1661. function loadPatch(param, base) {
  1662. if (typeof param === 'string') {
  1663. if (/^@@/m.test(param) || /^Index:/m.test(param)) {
  1664. return parsePatch(param)[0];
  1665. }
  1666. if (!base) {
  1667. throw new Error('Must provide a base reference or pass in a patch');
  1668. }
  1669. return structuredPatch(undefined, undefined, base, param);
  1670. }
  1671. return param;
  1672. }
  1673. function fileNameChanged(patch) {
  1674. return patch.newFileName && patch.newFileName !== patch.oldFileName;
  1675. }
  1676. function selectField(index, mine, theirs) {
  1677. if (mine === theirs) {
  1678. return mine;
  1679. } else {
  1680. index.conflict = true;
  1681. return {
  1682. mine: mine,
  1683. theirs: theirs
  1684. };
  1685. }
  1686. }
  1687. function hunkBefore(test, check) {
  1688. return test.oldStart < check.oldStart && test.oldStart + test.oldLines < check.oldStart;
  1689. }
  1690. function cloneHunk(hunk, offset) {
  1691. return {
  1692. oldStart: hunk.oldStart,
  1693. oldLines: hunk.oldLines,
  1694. newStart: hunk.newStart + offset,
  1695. newLines: hunk.newLines,
  1696. lines: hunk.lines
  1697. };
  1698. }
  1699. function mergeLines(hunk, mineOffset, mineLines, theirOffset, theirLines) {
  1700. // This will generally result in a conflicted hunk, but there are cases where the context
  1701. // is the only overlap where we can successfully merge the content here.
  1702. var mine = {
  1703. offset: mineOffset,
  1704. lines: mineLines,
  1705. index: 0
  1706. },
  1707. their = {
  1708. offset: theirOffset,
  1709. lines: theirLines,
  1710. index: 0
  1711. };
  1712. // Handle any leading content
  1713. insertLeading(hunk, mine, their);
  1714. insertLeading(hunk, their, mine);
  1715. // Now in the overlap content. Scan through and select the best changes from each.
  1716. while (mine.index < mine.lines.length && their.index < their.lines.length) {
  1717. var mineCurrent = mine.lines[mine.index],
  1718. theirCurrent = their.lines[their.index];
  1719. if ((mineCurrent[0] === '-' || mineCurrent[0] === '+') && (theirCurrent[0] === '-' || theirCurrent[0] === '+')) {
  1720. // Both modified ...
  1721. mutualChange(hunk, mine, their);
  1722. } else if (mineCurrent[0] === '+' && theirCurrent[0] === ' ') {
  1723. var _hunk$lines;
  1724. // Mine inserted
  1725. (_hunk$lines = hunk.lines).push.apply(_hunk$lines, _toConsumableArray(collectChange(mine)));
  1726. } else if (theirCurrent[0] === '+' && mineCurrent[0] === ' ') {
  1727. var _hunk$lines2;
  1728. // Theirs inserted
  1729. (_hunk$lines2 = hunk.lines).push.apply(_hunk$lines2, _toConsumableArray(collectChange(their)));
  1730. } else if (mineCurrent[0] === '-' && theirCurrent[0] === ' ') {
  1731. // Mine removed or edited
  1732. removal(hunk, mine, their);
  1733. } else if (theirCurrent[0] === '-' && mineCurrent[0] === ' ') {
  1734. // Their removed or edited
  1735. removal(hunk, their, mine, true);
  1736. } else if (mineCurrent === theirCurrent) {
  1737. // Context identity
  1738. hunk.lines.push(mineCurrent);
  1739. mine.index++;
  1740. their.index++;
  1741. } else {
  1742. // Context mismatch
  1743. conflict(hunk, collectChange(mine), collectChange(their));
  1744. }
  1745. }
  1746. // Now push anything that may be remaining
  1747. insertTrailing(hunk, mine);
  1748. insertTrailing(hunk, their);
  1749. calcLineCount(hunk);
  1750. }
  1751. function mutualChange(hunk, mine, their) {
  1752. var myChanges = collectChange(mine),
  1753. theirChanges = collectChange(their);
  1754. if (allRemoves(myChanges) && allRemoves(theirChanges)) {
  1755. // Special case for remove changes that are supersets of one another
  1756. if (arrayStartsWith(myChanges, theirChanges) && skipRemoveSuperset(their, myChanges, myChanges.length - theirChanges.length)) {
  1757. var _hunk$lines3;
  1758. (_hunk$lines3 = hunk.lines).push.apply(_hunk$lines3, _toConsumableArray(myChanges));
  1759. return;
  1760. } else if (arrayStartsWith(theirChanges, myChanges) && skipRemoveSuperset(mine, theirChanges, theirChanges.length - myChanges.length)) {
  1761. var _hunk$lines4;
  1762. (_hunk$lines4 = hunk.lines).push.apply(_hunk$lines4, _toConsumableArray(theirChanges));
  1763. return;
  1764. }
  1765. } else if (arrayEqual(myChanges, theirChanges)) {
  1766. var _hunk$lines5;
  1767. (_hunk$lines5 = hunk.lines).push.apply(_hunk$lines5, _toConsumableArray(myChanges));
  1768. return;
  1769. }
  1770. conflict(hunk, myChanges, theirChanges);
  1771. }
  1772. function removal(hunk, mine, their, swap) {
  1773. var myChanges = collectChange(mine),
  1774. theirChanges = collectContext(their, myChanges);
  1775. if (theirChanges.merged) {
  1776. var _hunk$lines6;
  1777. (_hunk$lines6 = hunk.lines).push.apply(_hunk$lines6, _toConsumableArray(theirChanges.merged));
  1778. } else {
  1779. conflict(hunk, swap ? theirChanges : myChanges, swap ? myChanges : theirChanges);
  1780. }
  1781. }
  1782. function conflict(hunk, mine, their) {
  1783. hunk.conflict = true;
  1784. hunk.lines.push({
  1785. conflict: true,
  1786. mine: mine,
  1787. theirs: their
  1788. });
  1789. }
  1790. function insertLeading(hunk, insert, their) {
  1791. while (insert.offset < their.offset && insert.index < insert.lines.length) {
  1792. var line = insert.lines[insert.index++];
  1793. hunk.lines.push(line);
  1794. insert.offset++;
  1795. }
  1796. }
  1797. function insertTrailing(hunk, insert) {
  1798. while (insert.index < insert.lines.length) {
  1799. var line = insert.lines[insert.index++];
  1800. hunk.lines.push(line);
  1801. }
  1802. }
  1803. function collectChange(state) {
  1804. var ret = [],
  1805. operation = state.lines[state.index][0];
  1806. while (state.index < state.lines.length) {
  1807. var line = state.lines[state.index];
  1808. // Group additions that are immediately after subtractions and treat them as one "atomic" modify change.
  1809. if (operation === '-' && line[0] === '+') {
  1810. operation = '+';
  1811. }
  1812. if (operation === line[0]) {
  1813. ret.push(line);
  1814. state.index++;
  1815. } else {
  1816. break;
  1817. }
  1818. }
  1819. return ret;
  1820. }
  1821. function collectContext(state, matchChanges) {
  1822. var changes = [],
  1823. merged = [],
  1824. matchIndex = 0,
  1825. contextChanges = false,
  1826. conflicted = false;
  1827. while (matchIndex < matchChanges.length && state.index < state.lines.length) {
  1828. var change = state.lines[state.index],
  1829. match = matchChanges[matchIndex];
  1830. // Once we've hit our add, then we are done
  1831. if (match[0] === '+') {
  1832. break;
  1833. }
  1834. contextChanges = contextChanges || change[0] !== ' ';
  1835. merged.push(match);
  1836. matchIndex++;
  1837. // Consume any additions in the other block as a conflict to attempt
  1838. // to pull in the remaining context after this
  1839. if (change[0] === '+') {
  1840. conflicted = true;
  1841. while (change[0] === '+') {
  1842. changes.push(change);
  1843. change = state.lines[++state.index];
  1844. }
  1845. }
  1846. if (match.substr(1) === change.substr(1)) {
  1847. changes.push(change);
  1848. state.index++;
  1849. } else {
  1850. conflicted = true;
  1851. }
  1852. }
  1853. if ((matchChanges[matchIndex] || '')[0] === '+' && contextChanges) {
  1854. conflicted = true;
  1855. }
  1856. if (conflicted) {
  1857. return changes;
  1858. }
  1859. while (matchIndex < matchChanges.length) {
  1860. merged.push(matchChanges[matchIndex++]);
  1861. }
  1862. return {
  1863. merged: merged,
  1864. changes: changes
  1865. };
  1866. }
  1867. function allRemoves(changes) {
  1868. return changes.reduce(function (prev, change) {
  1869. return prev && change[0] === '-';
  1870. }, true);
  1871. }
  1872. function skipRemoveSuperset(state, removeChanges, delta) {
  1873. for (var i = 0; i < delta; i++) {
  1874. var changeContent = removeChanges[removeChanges.length - delta + i].substr(1);
  1875. if (state.lines[state.index + i] !== ' ' + changeContent) {
  1876. return false;
  1877. }
  1878. }
  1879. state.index += delta;
  1880. return true;
  1881. }
  1882. function calcOldNewLineCount(lines) {
  1883. var oldLines = 0;
  1884. var newLines = 0;
  1885. lines.forEach(function (line) {
  1886. if (typeof line !== 'string') {
  1887. var myCount = calcOldNewLineCount(line.mine);
  1888. var theirCount = calcOldNewLineCount(line.theirs);
  1889. if (oldLines !== undefined) {
  1890. if (myCount.oldLines === theirCount.oldLines) {
  1891. oldLines += myCount.oldLines;
  1892. } else {
  1893. oldLines = undefined;
  1894. }
  1895. }
  1896. if (newLines !== undefined) {
  1897. if (myCount.newLines === theirCount.newLines) {
  1898. newLines += myCount.newLines;
  1899. } else {
  1900. newLines = undefined;
  1901. }
  1902. }
  1903. } else {
  1904. if (newLines !== undefined && (line[0] === '+' || line[0] === ' ')) {
  1905. newLines++;
  1906. }
  1907. if (oldLines !== undefined && (line[0] === '-' || line[0] === ' ')) {
  1908. oldLines++;
  1909. }
  1910. }
  1911. });
  1912. return {
  1913. oldLines: oldLines,
  1914. newLines: newLines
  1915. };
  1916. }
  1917. function reversePatch(structuredPatch) {
  1918. if (Array.isArray(structuredPatch)) {
  1919. return structuredPatch.map(reversePatch).reverse();
  1920. }
  1921. return _objectSpread2(_objectSpread2({}, structuredPatch), {}, {
  1922. oldFileName: structuredPatch.newFileName,
  1923. oldHeader: structuredPatch.newHeader,
  1924. newFileName: structuredPatch.oldFileName,
  1925. newHeader: structuredPatch.oldHeader,
  1926. hunks: structuredPatch.hunks.map(function (hunk) {
  1927. return {
  1928. oldLines: hunk.newLines,
  1929. oldStart: hunk.newStart,
  1930. newLines: hunk.oldLines,
  1931. newStart: hunk.oldStart,
  1932. lines: hunk.lines.map(function (l) {
  1933. if (l.startsWith('-')) {
  1934. return "+".concat(l.slice(1));
  1935. }
  1936. if (l.startsWith('+')) {
  1937. return "-".concat(l.slice(1));
  1938. }
  1939. return l;
  1940. })
  1941. };
  1942. })
  1943. });
  1944. }
  1945. // See: http://code.google.com/p/google-diff-match-patch/wiki/API
  1946. function convertChangesToDMP(changes) {
  1947. var ret = [],
  1948. change,
  1949. operation;
  1950. for (var i = 0; i < changes.length; i++) {
  1951. change = changes[i];
  1952. if (change.added) {
  1953. operation = 1;
  1954. } else if (change.removed) {
  1955. operation = -1;
  1956. } else {
  1957. operation = 0;
  1958. }
  1959. ret.push([operation, change.value]);
  1960. }
  1961. return ret;
  1962. }
  1963. function convertChangesToXML(changes) {
  1964. var ret = [];
  1965. for (var i = 0; i < changes.length; i++) {
  1966. var change = changes[i];
  1967. if (change.added) {
  1968. ret.push('<ins>');
  1969. } else if (change.removed) {
  1970. ret.push('<del>');
  1971. }
  1972. ret.push(escapeHTML(change.value));
  1973. if (change.added) {
  1974. ret.push('</ins>');
  1975. } else if (change.removed) {
  1976. ret.push('</del>');
  1977. }
  1978. }
  1979. return ret.join('');
  1980. }
  1981. function escapeHTML(s) {
  1982. var n = s;
  1983. n = n.replace(/&/g, '&amp;');
  1984. n = n.replace(/</g, '&lt;');
  1985. n = n.replace(/>/g, '&gt;');
  1986. n = n.replace(/"/g, '&quot;');
  1987. return n;
  1988. }
  1989. exports.Diff = Diff;
  1990. exports.applyPatch = applyPatch;
  1991. exports.applyPatches = applyPatches;
  1992. exports.canonicalize = canonicalize;
  1993. exports.convertChangesToDMP = convertChangesToDMP;
  1994. exports.convertChangesToXML = convertChangesToXML;
  1995. exports.createPatch = createPatch;
  1996. exports.createTwoFilesPatch = createTwoFilesPatch;
  1997. exports.diffArrays = diffArrays;
  1998. exports.diffChars = diffChars;
  1999. exports.diffCss = diffCss;
  2000. exports.diffJson = diffJson;
  2001. exports.diffLines = diffLines;
  2002. exports.diffSentences = diffSentences;
  2003. exports.diffTrimmedLines = diffTrimmedLines;
  2004. exports.diffWords = diffWords;
  2005. exports.diffWordsWithSpace = diffWordsWithSpace;
  2006. exports.formatPatch = formatPatch;
  2007. exports.merge = merge;
  2008. exports.parsePatch = parsePatch;
  2009. exports.reversePatch = reversePatch;
  2010. exports.structuredPatch = structuredPatch;
  2011. }));