UNPKG

lib0

Version:

> Monorepo of isomorphic utility functions

1 lines 13.7 kB
{"version":3,"file":"patience.cjs","sources":["../diff/patience.js"],"sourcesContent":["/**\n * A very simple diff algorithm. Slightly adapted to support splitting at different stages (e.g.\n * first diff lines, then diff words)\n *\n * https://bramcohen.livejournal.com/73318.html\n *\n * @experiemantal This API will likely change.\n */\n\nimport * as map from '../map.js'\nimport * as math from '../math.js'\nimport * as array from '../array.js'\n\n/**\n * Implementation of patience diff. Expects that content is pre-split (e.g. by newline).\n *\n * @param {Array<string>} as\n * @param {Array<string>} bs\n * @return {Array<{ index: number, remove: Array<string>, insert: Array<string>}>} changeset @todo should use delta instead\n */\nexport const diff = (as, bs) => {\n const {\n middleAs,\n middleBs,\n commonPrefix\n } = removeCommonPrefixAndSuffix(as, bs)\n return lcs(middleAs, middleBs, commonPrefix)\n}\n\n/**\n * @param {string} a\n * @param {string} b\n * @param {RegExp|string} _regexp\n */\nexport const diffSplitBy = (a, b, _regexp) => {\n const isStringSeparator = typeof _regexp === 'string'\n const separator = isStringSeparator ? _regexp : ''\n const regexp = isStringSeparator ? new RegExp(_regexp, 'g') : _regexp\n const as = splitByRegexp(a, regexp, !isStringSeparator)\n const bs = splitByRegexp(b, regexp, !isStringSeparator)\n const changes = diff(as, bs)\n let prevSplitIndex = 0\n let prevStringIndex = 0\n return changes.map(change => {\n for (; prevSplitIndex < change.index; prevSplitIndex++) {\n prevStringIndex += as[prevSplitIndex].length\n }\n return {\n index: prevStringIndex,\n remove: change.remove.join(separator),\n insert: change.insert.join(separator)\n }\n })\n}\n\n/**\n * Sensible default for diffing strings using patience (it's fast though).\n *\n * Perform different types of patience diff on the content. Diff first by newline, then paragraphs, then by word\n * (split by space, brackets, punctuation)\n *\n * @param {string} a\n * @param {string} b\n */\nexport const diffAuto = (a, b) =>\n diffSplitBy(a, b, '\\n').map(d =>\n diffSplitBy(d.remove, d.insert, /\\. |[a-zA-Z0-9]+|[. ()[\\],;{}]/g).map(dd => ({\n insert: dd.insert,\n remove: dd.remove,\n index: dd.index + d.index\n }))\n ).flat()\n\n/**\n * @param {Array<string>} as\n * @param {Array<string>} bs\n */\nconst removeCommonPrefixAndSuffix = (as, bs) => {\n const commonLen = math.min(as.length, bs.length)\n let commonPrefix = 0\n let commonSuffix = 0\n // match start\n for (; commonPrefix < commonLen && as[commonPrefix] === bs[commonPrefix]; commonPrefix++) { /* nop */ }\n // match end\n for (; commonSuffix < commonLen - commonPrefix && as[as.length - 1 - commonSuffix] === bs[bs.length - 1 - commonSuffix]; commonSuffix++) { /* nop */ }\n const middleAs = as.slice(commonPrefix, as.length - commonSuffix)\n const middleBs = bs.slice(commonPrefix, bs.length - commonSuffix)\n return {\n middleAs, middleBs, commonPrefix, commonSuffix\n }\n}\n\n/**\n * Splits string by regex and returns all strings as an array. The matched parts are also returned.\n *\n * @param {string} str\n * @param {RegExp} regexp\n * @param {boolean} includeSeparator\n */\nconst splitByRegexp = (str, regexp, includeSeparator) => {\n const matches = [...str.matchAll(regexp)]\n let prevIndex = 0\n /**\n * @type {Array<string>}\n */\n const res = []\n matches.forEach(m => {\n prevIndex < (m.index || 0) && res.push(str.slice(prevIndex, m.index))\n includeSeparator && res.push(m[0]) // is always non-empty\n prevIndex = /** @type {number} */ (m.index) + m[0].length\n })\n const end = str.slice(prevIndex)\n end.length > 0 && res.push(end)\n return res\n}\n\n/**\n * An item may have multiple occurances (not when matching unique entries). It also may have a\n * reference to the stack of other items (from as to bs).\n */\nclass Item {\n constructor () {\n /**\n * @type {Array<number>}\n */\n this.indexes = []\n /**\n * The matching item from the other side\n * @type {Item?}\n */\n this.match = null\n /**\n * For patience sort. Reference (index of the stack) to the previous pile.\n *\n * @type {Item?}\n */\n this.ref = null\n }\n}\n\n/**\n * @param {Array<string>} xs\n */\nconst partition = xs => {\n /**\n * @type {Map<string,Item>}\n */\n const refs = map.create()\n xs.forEach((x, index) => {\n map.setIfUndefined(refs, x, () => new Item()).indexes.push(index)\n })\n return refs\n}\n\n/**\n * Find the longest common subsequence of items using patience sort.\n *\n * @param {Array<string>} as\n * @param {Array<string>} bs\n * @param {number} indexAdjust\n */\nconst lcs = (as, bs, indexAdjust) => {\n if (as.length === 0 && bs.length === 0) return []\n const aParts = partition(as)\n const bParts = partition(bs)\n /**\n * @type {Array<Array<Item>>} I.e. Array<Pile<Item>>\n */\n const piles = []\n aParts.forEach((aItem, aKey) => {\n // skip if no match or if either item is not unique\n if (aItem.indexes.length > 1 || (aItem.match = bParts.get(aKey) || null) == null || aItem.match.indexes.length > 1) return\n for (let i = 0; i < piles.length; i++) {\n const pile = piles[i]\n if (aItem.match.indexes[0] < /** @type {Item} */ (pile[pile.length - 1].match).indexes[0]) {\n pile.push(aItem)\n if (i > 0) aItem.ref = array.last(piles[i - 1])\n return\n }\n }\n piles.length > 0 && (aItem.ref = array.last(piles[piles.length - 1]))\n piles.push([aItem])\n })\n /**\n * References to all matched items\n *\n * @type {Array<Item>}\n */\n const matches = []\n /**\n * @type {Item?}\n */\n let currPileItem = piles[piles.length - 1]?.[0]\n while (currPileItem != null) {\n matches.push(currPileItem)\n currPileItem = currPileItem.ref\n }\n matches.reverse()\n // add pseude match (assume the string terminal always matches)\n const pseudoA = new Item()\n const pseudoB = new Item()\n pseudoA.match = pseudoB\n pseudoA.indexes.push(as.length)\n pseudoB.indexes.push(bs.length)\n matches.push(pseudoA)\n /**\n * @type {Array<{ index: number, remove: Array<string>, insert: Array<string>}>}\n */\n const changeset = []\n let diffAStart = 0\n let diffBStart = 0\n for (let i = 0; i < matches.length; i++) {\n const m = matches[i]\n const delLength = m.indexes[0] - diffAStart\n const insLength = /** @type {Item} */ (m.match).indexes[0] - diffBStart\n if (delLength !== 0 || insLength !== 0) {\n const stripped = removeCommonPrefixAndSuffix(as.slice(diffAStart, diffAStart + delLength), bs.slice(diffBStart, diffBStart + insLength))\n if (stripped.middleAs.length !== 0 || stripped.middleBs.length !== 0) {\n changeset.push({ index: diffAStart + indexAdjust + stripped.commonPrefix, remove: stripped.middleAs, insert: stripped.middleBs })\n }\n }\n diffAStart = m.indexes[0] + 1\n diffBStart = /** @type {Item} */ (m.match).indexes[0] + 1\n }\n return changeset\n}\n"],"names":["math.min","map.create","map.setIfUndefined","array.last"],"mappings":"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"}