Hunks.cpp 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  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<Vector<Hunk>> Parser::parse_hunks()
  46. {
  47. Vector<Hunk> hunks;
  48. while (!is_eof()) {
  49. // Try an locate a hunk location in this hunk. It may be prefixed with information.
  50. auto maybe_location = consume_unified_location();
  51. consume_line();
  52. if (!maybe_location.has_value())
  53. continue;
  54. Hunk hunk { *maybe_location, {} };
  55. auto old_lines_expected = hunk.location.old_range.number_of_lines;
  56. auto new_lines_expected = hunk.location.new_range.number_of_lines;
  57. // We've found a location. Now parse out all of the expected content lines.
  58. while (old_lines_expected != 0 || new_lines_expected != 0) {
  59. StringView line = consume_line();
  60. if (line.is_empty())
  61. return Error::from_string_literal("Malformed empty content line in patch");
  62. if (line[0] != ' ' && line[0] != '+' && line[0] != '-')
  63. return Error::from_string_literal("Invaid operation in patch");
  64. auto const operation = Line::operation_from_symbol(line[0]);
  65. if (operation != Line::Operation::Removal) {
  66. if (new_lines_expected == 0)
  67. return Error::from_string_literal("Found more removal and context lines in patch than expected");
  68. --new_lines_expected;
  69. }
  70. if (operation != Line::Operation::Addition) {
  71. if (old_lines_expected == 0)
  72. return Error::from_string_literal("Found more addition and context lines in patch than expected");
  73. --old_lines_expected;
  74. }
  75. auto const content = line.substring_view(1, line.length() - 1);
  76. TRY(hunk.lines.try_append(Line { operation, TRY(String::from_utf8(content)) }));
  77. }
  78. TRY(hunks.try_append(hunk));
  79. }
  80. if constexpr (HUNKS_DEBUG) {
  81. for (auto const& hunk : hunks) {
  82. dbgln("{}", hunk.location);
  83. for (auto const& line : hunk.lines)
  84. dbgln("{}", line);
  85. }
  86. }
  87. return hunks;
  88. }
  89. ErrorOr<Vector<Hunk>> parse_hunks(StringView diff)
  90. {
  91. Parser lexer(diff);
  92. return lexer.parse_hunks();
  93. }
  94. }