joinAlignedDiffs.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', {
  3. value: true
  4. });
  5. exports.joinAlignedDiffsNoExpand = exports.joinAlignedDiffsExpand = void 0;
  6. var _cleanupSemantic = require('./cleanupSemantic');
  7. /**
  8. * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
  9. *
  10. * This source code is licensed under the MIT license found in the
  11. * LICENSE file in the root directory of this source tree.
  12. */
  13. const formatTrailingSpaces = (line, trailingSpaceFormatter) =>
  14. line.replace(/\s+$/, match => trailingSpaceFormatter(match));
  15. const printDiffLine = (
  16. line,
  17. isFirstOrLast,
  18. color,
  19. indicator,
  20. trailingSpaceFormatter,
  21. emptyFirstOrLastLinePlaceholder
  22. ) =>
  23. line.length !== 0
  24. ? color(
  25. indicator + ' ' + formatTrailingSpaces(line, trailingSpaceFormatter)
  26. )
  27. : indicator !== ' '
  28. ? color(indicator)
  29. : isFirstOrLast && emptyFirstOrLastLinePlaceholder.length !== 0
  30. ? color(indicator + ' ' + emptyFirstOrLastLinePlaceholder)
  31. : '';
  32. const printDeleteLine = (
  33. line,
  34. isFirstOrLast,
  35. {
  36. aColor,
  37. aIndicator,
  38. changeLineTrailingSpaceColor,
  39. emptyFirstOrLastLinePlaceholder
  40. }
  41. ) =>
  42. printDiffLine(
  43. line,
  44. isFirstOrLast,
  45. aColor,
  46. aIndicator,
  47. changeLineTrailingSpaceColor,
  48. emptyFirstOrLastLinePlaceholder
  49. );
  50. const printInsertLine = (
  51. line,
  52. isFirstOrLast,
  53. {
  54. bColor,
  55. bIndicator,
  56. changeLineTrailingSpaceColor,
  57. emptyFirstOrLastLinePlaceholder
  58. }
  59. ) =>
  60. printDiffLine(
  61. line,
  62. isFirstOrLast,
  63. bColor,
  64. bIndicator,
  65. changeLineTrailingSpaceColor,
  66. emptyFirstOrLastLinePlaceholder
  67. );
  68. const printCommonLine = (
  69. line,
  70. isFirstOrLast,
  71. {
  72. commonColor,
  73. commonIndicator,
  74. commonLineTrailingSpaceColor,
  75. emptyFirstOrLastLinePlaceholder
  76. }
  77. ) =>
  78. printDiffLine(
  79. line,
  80. isFirstOrLast,
  81. commonColor,
  82. commonIndicator,
  83. commonLineTrailingSpaceColor,
  84. emptyFirstOrLastLinePlaceholder
  85. ); // In GNU diff format, indexes are one-based instead of zero-based.
  86. const createPatchMark = (aStart, aEnd, bStart, bEnd, {patchColor}) =>
  87. patchColor(
  88. `@@ -${aStart + 1},${aEnd - aStart} +${bStart + 1},${bEnd - bStart} @@`
  89. ); // jest --no-expand
  90. //
  91. // Given array of aligned strings with inverse highlight formatting,
  92. // return joined lines with diff formatting (and patch marks, if needed).
  93. const joinAlignedDiffsNoExpand = (diffs, options) => {
  94. const iLength = diffs.length;
  95. const nContextLines = options.contextLines;
  96. const nContextLines2 = nContextLines + nContextLines; // First pass: count output lines and see if it has patches.
  97. let jLength = iLength;
  98. let hasExcessAtStartOrEnd = false;
  99. let nExcessesBetweenChanges = 0;
  100. let i = 0;
  101. while (i !== iLength) {
  102. const iStart = i;
  103. while (i !== iLength && diffs[i][0] === _cleanupSemantic.DIFF_EQUAL) {
  104. i += 1;
  105. }
  106. if (iStart !== i) {
  107. if (iStart === 0) {
  108. // at start
  109. if (i > nContextLines) {
  110. jLength -= i - nContextLines; // subtract excess common lines
  111. hasExcessAtStartOrEnd = true;
  112. }
  113. } else if (i === iLength) {
  114. // at end
  115. const n = i - iStart;
  116. if (n > nContextLines) {
  117. jLength -= n - nContextLines; // subtract excess common lines
  118. hasExcessAtStartOrEnd = true;
  119. }
  120. } else {
  121. // between changes
  122. const n = i - iStart;
  123. if (n > nContextLines2) {
  124. jLength -= n - nContextLines2; // subtract excess common lines
  125. nExcessesBetweenChanges += 1;
  126. }
  127. }
  128. }
  129. while (i !== iLength && diffs[i][0] !== _cleanupSemantic.DIFF_EQUAL) {
  130. i += 1;
  131. }
  132. }
  133. const hasPatch = nExcessesBetweenChanges !== 0 || hasExcessAtStartOrEnd;
  134. if (nExcessesBetweenChanges !== 0) {
  135. jLength += nExcessesBetweenChanges + 1; // add patch lines
  136. } else if (hasExcessAtStartOrEnd) {
  137. jLength += 1; // add patch line
  138. }
  139. const jLast = jLength - 1;
  140. const lines = [];
  141. let jPatchMark = 0; // index of placeholder line for current patch mark
  142. if (hasPatch) {
  143. lines.push(''); // placeholder line for first patch mark
  144. } // Indexes of expected or received lines in current patch:
  145. let aStart = 0;
  146. let bStart = 0;
  147. let aEnd = 0;
  148. let bEnd = 0;
  149. const pushCommonLine = line => {
  150. const j = lines.length;
  151. lines.push(printCommonLine(line, j === 0 || j === jLast, options));
  152. aEnd += 1;
  153. bEnd += 1;
  154. };
  155. const pushDeleteLine = line => {
  156. const j = lines.length;
  157. lines.push(printDeleteLine(line, j === 0 || j === jLast, options));
  158. aEnd += 1;
  159. };
  160. const pushInsertLine = line => {
  161. const j = lines.length;
  162. lines.push(printInsertLine(line, j === 0 || j === jLast, options));
  163. bEnd += 1;
  164. }; // Second pass: push lines with diff formatting (and patch marks, if needed).
  165. i = 0;
  166. while (i !== iLength) {
  167. let iStart = i;
  168. while (i !== iLength && diffs[i][0] === _cleanupSemantic.DIFF_EQUAL) {
  169. i += 1;
  170. }
  171. if (iStart !== i) {
  172. if (iStart === 0) {
  173. // at beginning
  174. if (i > nContextLines) {
  175. iStart = i - nContextLines;
  176. aStart = iStart;
  177. bStart = iStart;
  178. aEnd = aStart;
  179. bEnd = bStart;
  180. }
  181. for (let iCommon = iStart; iCommon !== i; iCommon += 1) {
  182. pushCommonLine(diffs[iCommon][1]);
  183. }
  184. } else if (i === iLength) {
  185. // at end
  186. const iEnd = i - iStart > nContextLines ? iStart + nContextLines : i;
  187. for (let iCommon = iStart; iCommon !== iEnd; iCommon += 1) {
  188. pushCommonLine(diffs[iCommon][1]);
  189. }
  190. } else {
  191. // between changes
  192. const nCommon = i - iStart;
  193. if (nCommon > nContextLines2) {
  194. const iEnd = iStart + nContextLines;
  195. for (let iCommon = iStart; iCommon !== iEnd; iCommon += 1) {
  196. pushCommonLine(diffs[iCommon][1]);
  197. }
  198. lines[jPatchMark] = createPatchMark(
  199. aStart,
  200. aEnd,
  201. bStart,
  202. bEnd,
  203. options
  204. );
  205. jPatchMark = lines.length;
  206. lines.push(''); // placeholder line for next patch mark
  207. const nOmit = nCommon - nContextLines2;
  208. aStart = aEnd + nOmit;
  209. bStart = bEnd + nOmit;
  210. aEnd = aStart;
  211. bEnd = bStart;
  212. for (let iCommon = i - nContextLines; iCommon !== i; iCommon += 1) {
  213. pushCommonLine(diffs[iCommon][1]);
  214. }
  215. } else {
  216. for (let iCommon = iStart; iCommon !== i; iCommon += 1) {
  217. pushCommonLine(diffs[iCommon][1]);
  218. }
  219. }
  220. }
  221. }
  222. while (i !== iLength && diffs[i][0] === _cleanupSemantic.DIFF_DELETE) {
  223. pushDeleteLine(diffs[i][1]);
  224. i += 1;
  225. }
  226. while (i !== iLength && diffs[i][0] === _cleanupSemantic.DIFF_INSERT) {
  227. pushInsertLine(diffs[i][1]);
  228. i += 1;
  229. }
  230. }
  231. if (hasPatch) {
  232. lines[jPatchMark] = createPatchMark(aStart, aEnd, bStart, bEnd, options);
  233. }
  234. return lines.join('\n');
  235. }; // jest --expand
  236. //
  237. // Given array of aligned strings with inverse highlight formatting,
  238. // return joined lines with diff formatting.
  239. exports.joinAlignedDiffsNoExpand = joinAlignedDiffsNoExpand;
  240. const joinAlignedDiffsExpand = (diffs, options) =>
  241. diffs
  242. .map((diff, i, diffs) => {
  243. const line = diff[1];
  244. const isFirstOrLast = i === 0 || i === diffs.length - 1;
  245. switch (diff[0]) {
  246. case _cleanupSemantic.DIFF_DELETE:
  247. return printDeleteLine(line, isFirstOrLast, options);
  248. case _cleanupSemantic.DIFF_INSERT:
  249. return printInsertLine(line, isFirstOrLast, options);
  250. default:
  251. return printCommonLine(line, isFirstOrLast, options);
  252. }
  253. })
  254. .join('\n');
  255. exports.joinAlignedDiffsExpand = joinAlignedDiffsExpand;