Generator.cpp 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. /*
  2. * Copyright (c) 2021, Mustafa Quraish <mustafa@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "Generator.h"
  7. namespace Diff {
  8. Vector<Hunk> from_text(StringView const& old_text, StringView const& new_text)
  9. {
  10. auto old_lines = old_text.lines();
  11. auto new_lines = new_text.lines();
  12. /**
  13. * This is a simple implementation of the Longest Common Subsequence algorithm (over
  14. * the lines of the text as opposed to the characters). A Dynamic programming approach
  15. * is used here.
  16. */
  17. enum class Direction {
  18. Up, // Added a new line
  19. Left, // Removed a line
  20. Diagonal, // Line remained the same
  21. };
  22. // A single cell in the DP-matrix. Cell (i, j) represents the longest common
  23. // sub-sequence of lines between old_lines[0 : i] and new_lines[0 : j].
  24. struct Cell {
  25. size_t length;
  26. Direction direction;
  27. };
  28. auto dp_matrix = Vector<Cell>();
  29. dp_matrix.resize((old_lines.size() + 1) * (new_lines.size() + 1));
  30. auto dp = [&dp_matrix, width = old_lines.size() + 1](size_t i, size_t j) -> Cell& {
  31. return dp_matrix[i + width * j];
  32. };
  33. // Initialize the first row and column
  34. for (size_t i = 0; i <= old_lines.size(); ++i)
  35. dp(i, 0) = { 0, Direction::Left };
  36. for (size_t j = 0; j <= new_lines.size(); ++j)
  37. dp(0, j) = { 0, Direction::Up };
  38. // Fill in the rest of the DP table
  39. for (size_t i = 1; i <= old_lines.size(); ++i) {
  40. for (size_t j = 1; j <= new_lines.size(); ++j) {
  41. if (old_lines[i - 1] == new_lines[j - 1]) {
  42. dp(i, j) = { dp(i - 1, j - 1).length + 1, Direction::Diagonal };
  43. } else {
  44. auto up = dp(i, j - 1).length;
  45. auto left = dp(i - 1, j).length;
  46. if (up > left)
  47. dp(i, j) = { up, Direction::Up };
  48. else
  49. dp(i, j) = { left, Direction::Left };
  50. }
  51. }
  52. }
  53. Vector<Hunk> hunks;
  54. size_t i = old_lines.size();
  55. size_t j = new_lines.size();
  56. // FIXME: This creates a hunk per line, very inefficient.
  57. while (i > 0 && j > 0) {
  58. auto& cell = dp(i, j);
  59. if (cell.direction == Direction::Up) {
  60. --j;
  61. hunks.append({ i, j, {}, { new_lines[j] } });
  62. } else if (cell.direction == Direction::Left) {
  63. --i;
  64. hunks.append({ i, j, { old_lines[i] }, {} });
  65. } else if (cell.direction == Direction::Diagonal) {
  66. --i;
  67. --j;
  68. }
  69. }
  70. hunks.reverse();
  71. return hunks;
  72. }
  73. }