getAlignedDiffs.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', {
  3. value: true
  4. });
  5. exports.default = void 0;
  6. var _cleanupSemantic = require('./cleanupSemantic');
  7. var _printDiffs = require('./printDiffs');
  8. function _defineProperty(obj, key, value) {
  9. if (key in obj) {
  10. Object.defineProperty(obj, key, {
  11. value: value,
  12. enumerable: true,
  13. configurable: true,
  14. writable: true
  15. });
  16. } else {
  17. obj[key] = value;
  18. }
  19. return obj;
  20. }
  21. // Encapsulate change lines until either a common newline or the end.
  22. class ChangeBuffer {
  23. // incomplete line
  24. // complete lines
  25. constructor(op) {
  26. _defineProperty(this, 'op', void 0);
  27. _defineProperty(this, 'line', void 0);
  28. _defineProperty(this, 'lines', void 0);
  29. this.op = op;
  30. this.line = [];
  31. this.lines = [];
  32. }
  33. pushSubstring(substring) {
  34. this.pushDiff(new _cleanupSemantic.Diff(this.op, substring));
  35. }
  36. pushLine() {
  37. // Assume call only if line has at least one diff,
  38. // therefore an empty line must have a diff which has an empty string.
  39. this.lines.push(
  40. new _cleanupSemantic.Diff(
  41. this.op,
  42. (0, _printDiffs.getHighlightedString)(this.op, this.line)
  43. )
  44. );
  45. this.line.length = 0;
  46. }
  47. isLineEmpty() {
  48. return this.line.length === 0;
  49. } // Minor input to buffer.
  50. pushDiff(diff) {
  51. this.line.push(diff);
  52. } // Main input to buffer.
  53. align(diff) {
  54. const string = diff[1];
  55. if (_printDiffs.MULTILINE_REGEXP.test(string)) {
  56. const substrings = string.split('\n');
  57. const iLast = substrings.length - 1;
  58. substrings.forEach((substring, i) => {
  59. if (i < iLast) {
  60. // The first substring completes the current change line.
  61. // A middle substring is a change line.
  62. this.pushSubstring(substring);
  63. this.pushLine();
  64. } else if (substring.length !== 0) {
  65. // The last substring starts a change line, if it is not empty.
  66. // Important: This non-empty condition also automatically omits
  67. // the newline appended to the end of expected and received strings.
  68. this.pushSubstring(substring);
  69. }
  70. });
  71. } else {
  72. // Append non-multiline string to current change line.
  73. this.pushDiff(diff);
  74. }
  75. } // Output from buffer.
  76. moveLinesTo(lines) {
  77. if (!this.isLineEmpty()) {
  78. this.pushLine();
  79. }
  80. lines.push(...this.lines);
  81. this.lines.length = 0;
  82. }
  83. } // Encapsulate common and change lines.
  84. class CommonBuffer {
  85. constructor(deleteBuffer, insertBuffer) {
  86. _defineProperty(this, 'deleteBuffer', void 0);
  87. _defineProperty(this, 'insertBuffer', void 0);
  88. _defineProperty(this, 'lines', void 0);
  89. this.deleteBuffer = deleteBuffer;
  90. this.insertBuffer = insertBuffer;
  91. this.lines = [];
  92. }
  93. pushDiffCommonLine(diff) {
  94. this.lines.push(diff);
  95. }
  96. pushDiffChangeLines(diff) {
  97. const isDiffEmpty = diff[1].length === 0; // An empty diff string is redundant, unless a change line is empty.
  98. if (!isDiffEmpty || this.deleteBuffer.isLineEmpty()) {
  99. this.deleteBuffer.pushDiff(diff);
  100. }
  101. if (!isDiffEmpty || this.insertBuffer.isLineEmpty()) {
  102. this.insertBuffer.pushDiff(diff);
  103. }
  104. }
  105. flushChangeLines() {
  106. this.deleteBuffer.moveLinesTo(this.lines);
  107. this.insertBuffer.moveLinesTo(this.lines);
  108. } // Input to buffer.
  109. align(diff) {
  110. const op = diff[0];
  111. const string = diff[1];
  112. if (_printDiffs.MULTILINE_REGEXP.test(string)) {
  113. const substrings = string.split('\n');
  114. const iLast = substrings.length - 1;
  115. substrings.forEach((substring, i) => {
  116. if (i === 0) {
  117. const subdiff = new _cleanupSemantic.Diff(op, substring);
  118. if (
  119. this.deleteBuffer.isLineEmpty() &&
  120. this.insertBuffer.isLineEmpty()
  121. ) {
  122. // If both current change lines are empty,
  123. // then the first substring is a common line.
  124. this.flushChangeLines();
  125. this.pushDiffCommonLine(subdiff);
  126. } else {
  127. // If either current change line is non-empty,
  128. // then the first substring completes the change lines.
  129. this.pushDiffChangeLines(subdiff);
  130. this.flushChangeLines();
  131. }
  132. } else if (i < iLast) {
  133. // A middle substring is a common line.
  134. this.pushDiffCommonLine(new _cleanupSemantic.Diff(op, substring));
  135. } else if (substring.length !== 0) {
  136. // The last substring starts a change line, if it is not empty.
  137. // Important: This non-empty condition also automatically omits
  138. // the newline appended to the end of expected and received strings.
  139. this.pushDiffChangeLines(new _cleanupSemantic.Diff(op, substring));
  140. }
  141. });
  142. } else {
  143. // Append non-multiline string to current change lines.
  144. // Important: It cannot be at the end following empty change lines,
  145. // because newline appended to the end of expected and received strings.
  146. this.pushDiffChangeLines(diff);
  147. }
  148. } // Output from buffer.
  149. getLines() {
  150. this.flushChangeLines();
  151. return this.lines;
  152. }
  153. } // Given diffs from expected and received strings,
  154. // return new array of diffs split or joined into lines.
  155. //
  156. // To correctly align a change line at the end, the algorithm:
  157. // * assumes that a newline was appended to the strings
  158. // * omits the last newline from the output array
  159. //
  160. // Assume the function is not called:
  161. // * if either expected or received is empty string
  162. // * if neither expected nor received is multiline string
  163. const getAlignedDiffs = diffs => {
  164. const deleteBuffer = new ChangeBuffer(_cleanupSemantic.DIFF_DELETE);
  165. const insertBuffer = new ChangeBuffer(_cleanupSemantic.DIFF_INSERT);
  166. const commonBuffer = new CommonBuffer(deleteBuffer, insertBuffer);
  167. diffs.forEach(diff => {
  168. switch (diff[0]) {
  169. case _cleanupSemantic.DIFF_DELETE:
  170. deleteBuffer.align(diff);
  171. break;
  172. case _cleanupSemantic.DIFF_INSERT:
  173. insertBuffer.align(diff);
  174. break;
  175. default:
  176. commonBuffer.align(diff);
  177. }
  178. });
  179. return commonBuffer.getLines();
  180. };
  181. var _default = getAlignedDiffs;
  182. exports.default = _default;