Hunks.cpp 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /*
  2. * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
  3. * Copyright (c) 2023, Shannon Booth <shannon.ml.booth@gmail.com>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include "Hunks.h"
  8. #include <AK/Debug.h>
  9. namespace Diff {
  10. Optional<HunkLocation> Parser::consume_unified_location()
  11. {
  12. auto consume_range = [this](Range& range) {
  13. if (!consume_line_number(range.start_line))
  14. return false;
  15. if (consume_specific(',')) {
  16. if (!consume_line_number(range.number_of_lines))
  17. return false;
  18. } else {
  19. range.number_of_lines = 1;
  20. }
  21. return true;
  22. };
  23. if (!consume_specific("@@ -"))
  24. return {};
  25. HunkLocation location;
  26. if (!consume_range(location.old_range))
  27. return {};
  28. if (!consume_specific(" +"))
  29. return {};
  30. if (!consume_range(location.new_range))
  31. return {};
  32. if (!consume_specific(" @@"))
  33. return {};
  34. return location;
  35. }
  36. bool Parser::consume_line_number(size_t& number)
  37. {
  38. auto line = consume_while(is_ascii_digit);
  39. auto maybe_number = line.to_uint<size_t>();
  40. if (!maybe_number.has_value())
  41. return false;
  42. number = maybe_number.value();
  43. return true;
  44. }
  45. ErrorOr<Header> Parser::parse_header()
  46. {
  47. Header header;
  48. while (!is_eof()) {
  49. if (consume_specific("+++ ")) {
  50. header.new_file_path = TRY(String::from_utf8(consume_line()));
  51. continue;
  52. }
  53. if (consume_specific("--- ")) {
  54. header.old_file_path = TRY(String::from_utf8(consume_line()));
  55. continue;
  56. }
  57. if (next_is("@@ ")) {
  58. header.format = Format::Unified;
  59. return header;
  60. }
  61. consume_line();
  62. }
  63. return Error::from_string_literal("Unable to find any patch");
  64. }
  65. ErrorOr<Vector<Hunk>> Parser::parse_hunks()
  66. {
  67. Vector<Hunk> hunks;
  68. while (!is_eof()) {
  69. // Try an locate a hunk location in this hunk. It may be prefixed with information.
  70. auto maybe_location = consume_unified_location();
  71. consume_line();
  72. if (!maybe_location.has_value())
  73. continue;
  74. Hunk hunk { *maybe_location, {} };
  75. auto old_lines_expected = hunk.location.old_range.number_of_lines;
  76. auto new_lines_expected = hunk.location.new_range.number_of_lines;
  77. // We've found a location. Now parse out all of the expected content lines.
  78. while (old_lines_expected != 0 || new_lines_expected != 0) {
  79. StringView line = consume_line();
  80. if (line.is_empty())
  81. return Error::from_string_literal("Malformed empty content line in patch");
  82. if (line[0] != ' ' && line[0] != '+' && line[0] != '-')
  83. return Error::from_string_literal("Invaid operation in patch");
  84. auto const operation = Line::operation_from_symbol(line[0]);
  85. if (operation != Line::Operation::Removal) {
  86. if (new_lines_expected == 0)
  87. return Error::from_string_literal("Found more removal and context lines in patch than expected");
  88. --new_lines_expected;
  89. }
  90. if (operation != Line::Operation::Addition) {
  91. if (old_lines_expected == 0)
  92. return Error::from_string_literal("Found more addition and context lines in patch than expected");
  93. --old_lines_expected;
  94. }
  95. auto const content = line.substring_view(1, line.length() - 1);
  96. TRY(hunk.lines.try_append(Line { operation, TRY(String::from_utf8(content)) }));
  97. }
  98. TRY(hunks.try_append(hunk));
  99. }
  100. if constexpr (HUNKS_DEBUG) {
  101. for (auto const& hunk : hunks) {
  102. dbgln("{}", hunk.location);
  103. for (auto const& line : hunk.lines)
  104. dbgln("{}", line);
  105. }
  106. }
  107. return hunks;
  108. }
  109. ErrorOr<Vector<Hunk>> parse_hunks(StringView diff)
  110. {
  111. Parser lexer(diff);
  112. return lexer.parse_hunks();
  113. }
  114. }