c509a553e33c8cd4b90335f528ba38d4d39a407a08be8b90b770c6bc0160c078bdf26f0e9838645f165c02cb864821f10183946d5468754bef69ceb88e678e 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.AttributeMap = exports.OpIterator = exports.Op = void 0;
  4. const diff = require("fast-diff");
  5. const cloneDeep = require("lodash.clonedeep");
  6. const isEqual = require("lodash.isequal");
  7. const AttributeMap_1 = require("./AttributeMap");
  8. exports.AttributeMap = AttributeMap_1.default;
  9. const Op_1 = require("./Op");
  10. exports.Op = Op_1.default;
  11. const OpIterator_1 = require("./OpIterator");
  12. exports.OpIterator = OpIterator_1.default;
  13. const NULL_CHARACTER = String.fromCharCode(0); // Placeholder char for embed in diff()
  14. const getEmbedTypeAndData = (a, b) => {
  15. if (typeof a !== 'object' || a === null) {
  16. throw new Error(`cannot retain a ${typeof a}`);
  17. }
  18. if (typeof b !== 'object' || b === null) {
  19. throw new Error(`cannot retain a ${typeof b}`);
  20. }
  21. const embedType = Object.keys(a)[0];
  22. if (!embedType || embedType !== Object.keys(b)[0]) {
  23. throw new Error(`embed types not matched: ${embedType} != ${Object.keys(b)[0]}`);
  24. }
  25. return [embedType, a[embedType], b[embedType]];
  26. };
  27. class Delta {
  28. constructor(ops) {
  29. // Assume we are given a well formed ops
  30. if (Array.isArray(ops)) {
  31. this.ops = ops;
  32. }
  33. else if (ops != null && Array.isArray(ops.ops)) {
  34. this.ops = ops.ops;
  35. }
  36. else {
  37. this.ops = [];
  38. }
  39. }
  40. static registerEmbed(embedType, handler) {
  41. this.handlers[embedType] = handler;
  42. }
  43. static unregisterEmbed(embedType) {
  44. delete this.handlers[embedType];
  45. }
  46. static getHandler(embedType) {
  47. const handler = this.handlers[embedType];
  48. if (!handler) {
  49. throw new Error(`no handlers for embed type "${embedType}"`);
  50. }
  51. return handler;
  52. }
  53. insert(arg, attributes) {
  54. const newOp = {};
  55. if (typeof arg === 'string' && arg.length === 0) {
  56. return this;
  57. }
  58. newOp.insert = arg;
  59. if (attributes != null &&
  60. typeof attributes === 'object' &&
  61. Object.keys(attributes).length > 0) {
  62. newOp.attributes = attributes;
  63. }
  64. return this.push(newOp);
  65. }
  66. delete(length) {
  67. if (length <= 0) {
  68. return this;
  69. }
  70. return this.push({ delete: length });
  71. }
  72. retain(length, attributes) {
  73. if (typeof length === 'number' && length <= 0) {
  74. return this;
  75. }
  76. const newOp = { retain: length };
  77. if (attributes != null &&
  78. typeof attributes === 'object' &&
  79. Object.keys(attributes).length > 0) {
  80. newOp.attributes = attributes;
  81. }
  82. return this.push(newOp);
  83. }
  84. push(newOp) {
  85. let index = this.ops.length;
  86. let lastOp = this.ops[index - 1];
  87. newOp = cloneDeep(newOp);
  88. if (typeof lastOp === 'object') {
  89. if (typeof newOp.delete === 'number' &&
  90. typeof lastOp.delete === 'number') {
  91. this.ops[index - 1] = { delete: lastOp.delete + newOp.delete };
  92. return this;
  93. }
  94. // Since it does not matter if we insert before or after deleting at the same index,
  95. // always prefer to insert first
  96. if (typeof lastOp.delete === 'number' && newOp.insert != null) {
  97. index -= 1;
  98. lastOp = this.ops[index - 1];
  99. if (typeof lastOp !== 'object') {
  100. this.ops.unshift(newOp);
  101. return this;
  102. }
  103. }
  104. if (isEqual(newOp.attributes, lastOp.attributes)) {
  105. if (typeof newOp.insert === 'string' &&
  106. typeof lastOp.insert === 'string') {
  107. this.ops[index - 1] = { insert: lastOp.insert + newOp.insert };
  108. if (typeof newOp.attributes === 'object') {
  109. this.ops[index - 1].attributes = newOp.attributes;
  110. }
  111. return this;
  112. }
  113. else if (typeof newOp.retain === 'number' &&
  114. typeof lastOp.retain === 'number') {
  115. this.ops[index - 1] = { retain: lastOp.retain + newOp.retain };
  116. if (typeof newOp.attributes === 'object') {
  117. this.ops[index - 1].attributes = newOp.attributes;
  118. }
  119. return this;
  120. }
  121. }
  122. }
  123. if (index === this.ops.length) {
  124. this.ops.push(newOp);
  125. }
  126. else {
  127. this.ops.splice(index, 0, newOp);
  128. }
  129. return this;
  130. }
  131. chop() {
  132. const lastOp = this.ops[this.ops.length - 1];
  133. if (lastOp && typeof lastOp.retain === 'number' && !lastOp.attributes) {
  134. this.ops.pop();
  135. }
  136. return this;
  137. }
  138. filter(predicate) {
  139. return this.ops.filter(predicate);
  140. }
  141. forEach(predicate) {
  142. this.ops.forEach(predicate);
  143. }
  144. map(predicate) {
  145. return this.ops.map(predicate);
  146. }
  147. partition(predicate) {
  148. const passed = [];
  149. const failed = [];
  150. this.forEach((op) => {
  151. const target = predicate(op) ? passed : failed;
  152. target.push(op);
  153. });
  154. return [passed, failed];
  155. }
  156. reduce(predicate, initialValue) {
  157. return this.ops.reduce(predicate, initialValue);
  158. }
  159. changeLength() {
  160. return this.reduce((length, elem) => {
  161. if (elem.insert) {
  162. return length + Op_1.default.length(elem);
  163. }
  164. else if (elem.delete) {
  165. return length - elem.delete;
  166. }
  167. return length;
  168. }, 0);
  169. }
  170. length() {
  171. return this.reduce((length, elem) => {
  172. return length + Op_1.default.length(elem);
  173. }, 0);
  174. }
  175. slice(start = 0, end = Infinity) {
  176. const ops = [];
  177. const iter = new OpIterator_1.default(this.ops);
  178. let index = 0;
  179. while (index < end && iter.hasNext()) {
  180. let nextOp;
  181. if (index < start) {
  182. nextOp = iter.next(start - index);
  183. }
  184. else {
  185. nextOp = iter.next(end - index);
  186. ops.push(nextOp);
  187. }
  188. index += Op_1.default.length(nextOp);
  189. }
  190. return new Delta(ops);
  191. }
  192. compose(other) {
  193. const thisIter = new OpIterator_1.default(this.ops);
  194. const otherIter = new OpIterator_1.default(other.ops);
  195. const ops = [];
  196. const firstOther = otherIter.peek();
  197. if (firstOther != null &&
  198. typeof firstOther.retain === 'number' &&
  199. firstOther.attributes == null) {
  200. let firstLeft = firstOther.retain;
  201. while (thisIter.peekType() === 'insert' &&
  202. thisIter.peekLength() <= firstLeft) {
  203. firstLeft -= thisIter.peekLength();
  204. ops.push(thisIter.next());
  205. }
  206. if (firstOther.retain - firstLeft > 0) {
  207. otherIter.next(firstOther.retain - firstLeft);
  208. }
  209. }
  210. const delta = new Delta(ops);
  211. while (thisIter.hasNext() || otherIter.hasNext()) {
  212. if (otherIter.peekType() === 'insert') {
  213. delta.push(otherIter.next());
  214. }
  215. else if (thisIter.peekType() === 'delete') {
  216. delta.push(thisIter.next());
  217. }
  218. else {
  219. const length = Math.min(thisIter.peekLength(), otherIter.peekLength());
  220. const thisOp = thisIter.next(length);
  221. const otherOp = otherIter.next(length);
  222. if (otherOp.retain) {
  223. const newOp = {};
  224. if (typeof thisOp.retain === 'number') {
  225. newOp.retain =
  226. typeof otherOp.retain === 'number' ? length : otherOp.retain;
  227. }
  228. else {
  229. if (typeof otherOp.retain === 'number') {
  230. if (thisOp.retain == null) {
  231. newOp.insert = thisOp.insert;
  232. }
  233. else {
  234. newOp.retain = thisOp.retain;
  235. }
  236. }
  237. else {
  238. const action = thisOp.retain == null ? 'insert' : 'retain';
  239. const [embedType, thisData, otherData] = getEmbedTypeAndData(thisOp[action], otherOp.retain);
  240. const handler = Delta.getHandler(embedType);
  241. newOp[action] = {
  242. [embedType]: handler.compose(thisData, otherData, action === 'retain'),
  243. };
  244. }
  245. }
  246. // Preserve null when composing with a retain, otherwise remove it for inserts
  247. const attributes = AttributeMap_1.default.compose(thisOp.attributes, otherOp.attributes, typeof thisOp.retain === 'number');
  248. if (attributes) {
  249. newOp.attributes = attributes;
  250. }
  251. delta.push(newOp);
  252. // Optimization if rest of other is just retain
  253. if (!otherIter.hasNext() &&
  254. isEqual(delta.ops[delta.ops.length - 1], newOp)) {
  255. const rest = new Delta(thisIter.rest());
  256. return delta.concat(rest).chop();
  257. }
  258. // Other op should be delete, we could be an insert or retain
  259. // Insert + delete cancels out
  260. }
  261. else if (typeof otherOp.delete === 'number' &&
  262. (typeof thisOp.retain === 'number' ||
  263. (typeof thisOp.retain === 'object' && thisOp.retain !== null))) {
  264. delta.push(otherOp);
  265. }
  266. }
  267. }
  268. return delta.chop();
  269. }
  270. concat(other) {
  271. const delta = new Delta(this.ops.slice());
  272. if (other.ops.length > 0) {
  273. delta.push(other.ops[0]);
  274. delta.ops = delta.ops.concat(other.ops.slice(1));
  275. }
  276. return delta;
  277. }
  278. diff(other, cursor) {
  279. if (this.ops === other.ops) {
  280. return new Delta();
  281. }
  282. const strings = [this, other].map((delta) => {
  283. return delta
  284. .map((op) => {
  285. if (op.insert != null) {
  286. return typeof op.insert === 'string' ? op.insert : NULL_CHARACTER;
  287. }
  288. const prep = delta === other ? 'on' : 'with';
  289. throw new Error('diff() called ' + prep + ' non-document');
  290. })
  291. .join('');
  292. });
  293. const retDelta = new Delta();
  294. const diffResult = diff(strings[0], strings[1], cursor, true);
  295. const thisIter = new OpIterator_1.default(this.ops);
  296. const otherIter = new OpIterator_1.default(other.ops);
  297. diffResult.forEach((component) => {
  298. let length = component[1].length;
  299. while (length > 0) {
  300. let opLength = 0;
  301. switch (component[0]) {
  302. case diff.INSERT:
  303. opLength = Math.min(otherIter.peekLength(), length);
  304. retDelta.push(otherIter.next(opLength));
  305. break;
  306. case diff.DELETE:
  307. opLength = Math.min(length, thisIter.peekLength());
  308. thisIter.next(opLength);
  309. retDelta.delete(opLength);
  310. break;
  311. case diff.EQUAL:
  312. opLength = Math.min(thisIter.peekLength(), otherIter.peekLength(), length);
  313. const thisOp = thisIter.next(opLength);
  314. const otherOp = otherIter.next(opLength);
  315. if (isEqual(thisOp.insert, otherOp.insert)) {
  316. retDelta.retain(opLength, AttributeMap_1.default.diff(thisOp.attributes, otherOp.attributes));
  317. }
  318. else {
  319. retDelta.push(otherOp).delete(opLength);
  320. }
  321. break;
  322. }
  323. length -= opLength;
  324. }
  325. });
  326. return retDelta.chop();
  327. }
  328. eachLine(predicate, newline = '\n') {
  329. const iter = new OpIterator_1.default(this.ops);
  330. let line = new Delta();
  331. let i = 0;
  332. while (iter.hasNext()) {
  333. if (iter.peekType() !== 'insert') {
  334. return;
  335. }
  336. const thisOp = iter.peek();
  337. const start = Op_1.default.length(thisOp) - iter.peekLength();
  338. const index = typeof thisOp.insert === 'string'
  339. ? thisOp.insert.indexOf(newline, start) - start
  340. : -1;
  341. if (index < 0) {
  342. line.push(iter.next());
  343. }
  344. else if (index > 0) {
  345. line.push(iter.next(index));
  346. }
  347. else {
  348. if (predicate(line, iter.next(1).attributes || {}, i) === false) {
  349. return;
  350. }
  351. i += 1;
  352. line = new Delta();
  353. }
  354. }
  355. if (line.length() > 0) {
  356. predicate(line, {}, i);
  357. }
  358. }
  359. invert(base) {
  360. const inverted = new Delta();
  361. this.reduce((baseIndex, op) => {
  362. if (op.insert) {
  363. inverted.delete(Op_1.default.length(op));
  364. }
  365. else if (typeof op.retain === 'number' && op.attributes == null) {
  366. inverted.retain(op.retain);
  367. return baseIndex + op.retain;
  368. }
  369. else if (op.delete || typeof op.retain === 'number') {
  370. const length = (op.delete || op.retain);
  371. const slice = base.slice(baseIndex, baseIndex + length);
  372. slice.forEach((baseOp) => {
  373. if (op.delete) {
  374. inverted.push(baseOp);
  375. }
  376. else if (op.retain && op.attributes) {
  377. inverted.retain(Op_1.default.length(baseOp), AttributeMap_1.default.invert(op.attributes, baseOp.attributes));
  378. }
  379. });
  380. return baseIndex + length;
  381. }
  382. else if (typeof op.retain === 'object' && op.retain !== null) {
  383. const slice = base.slice(baseIndex, baseIndex + 1);
  384. const baseOp = new OpIterator_1.default(slice.ops).next();
  385. const [embedType, opData, baseOpData] = getEmbedTypeAndData(op.retain, baseOp.insert);
  386. const handler = Delta.getHandler(embedType);
  387. inverted.retain({ [embedType]: handler.invert(opData, baseOpData) }, AttributeMap_1.default.invert(op.attributes, baseOp.attributes));
  388. return baseIndex + 1;
  389. }
  390. return baseIndex;
  391. }, 0);
  392. return inverted.chop();
  393. }
  394. transform(arg, priority = false) {
  395. priority = !!priority;
  396. if (typeof arg === 'number') {
  397. return this.transformPosition(arg, priority);
  398. }
  399. const other = arg;
  400. const thisIter = new OpIterator_1.default(this.ops);
  401. const otherIter = new OpIterator_1.default(other.ops);
  402. const delta = new Delta();
  403. while (thisIter.hasNext() || otherIter.hasNext()) {
  404. if (thisIter.peekType() === 'insert' &&
  405. (priority || otherIter.peekType() !== 'insert')) {
  406. delta.retain(Op_1.default.length(thisIter.next()));
  407. }
  408. else if (otherIter.peekType() === 'insert') {
  409. delta.push(otherIter.next());
  410. }
  411. else {
  412. const length = Math.min(thisIter.peekLength(), otherIter.peekLength());
  413. const thisOp = thisIter.next(length);
  414. const otherOp = otherIter.next(length);
  415. if (thisOp.delete) {
  416. // Our delete either makes their delete redundant or removes their retain
  417. continue;
  418. }
  419. else if (otherOp.delete) {
  420. delta.push(otherOp);
  421. }
  422. else {
  423. const thisData = thisOp.retain;
  424. const otherData = otherOp.retain;
  425. let transformedData = typeof otherData === 'object' && otherData !== null
  426. ? otherData
  427. : length;
  428. if (typeof thisData === 'object' &&
  429. thisData !== null &&
  430. typeof otherData === 'object' &&
  431. otherData !== null) {
  432. const embedType = Object.keys(thisData)[0];
  433. if (embedType === Object.keys(otherData)[0]) {
  434. const handler = Delta.getHandler(embedType);
  435. if (handler) {
  436. transformedData = {
  437. [embedType]: handler.transform(thisData[embedType], otherData[embedType], priority),
  438. };
  439. }
  440. }
  441. }
  442. // We retain either their retain or insert
  443. delta.retain(transformedData, AttributeMap_1.default.transform(thisOp.attributes, otherOp.attributes, priority));
  444. }
  445. }
  446. }
  447. return delta.chop();
  448. }
  449. transformPosition(index, priority = false) {
  450. priority = !!priority;
  451. const thisIter = new OpIterator_1.default(this.ops);
  452. let offset = 0;
  453. while (thisIter.hasNext() && offset <= index) {
  454. const length = thisIter.peekLength();
  455. const nextType = thisIter.peekType();
  456. thisIter.next();
  457. if (nextType === 'delete') {
  458. index -= Math.min(length, index - offset);
  459. continue;
  460. }
  461. else if (nextType === 'insert' && (offset < index || !priority)) {
  462. index += length;
  463. }
  464. offset += length;
  465. }
  466. return index;
  467. }
  468. }
  469. Delta.Op = Op_1.default;
  470. Delta.OpIterator = OpIterator_1.default;
  471. Delta.AttributeMap = AttributeMap_1.default;
  472. Delta.handlers = {};
  473. exports.default = Delta;
  474. if (typeof module === 'object') {
  475. module.exports = Delta;
  476. module.exports.default = Delta;
  477. }
  478. //# sourceMappingURL=Delta.js.map