Hunks.h 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. /*
  2. * Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
  3. * Copyright (c) 2023, Shannon Booth <shannon@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #pragma once
  8. #include <AK/Assertions.h>
  9. #include <AK/Format.h>
  10. #include <AK/GenericLexer.h>
  11. #include <AK/String.h>
  12. #include <AK/StringView.h>
  13. #include <AK/Vector.h>
  14. namespace Diff {
  15. struct Range {
  16. size_t start_line { 0 };
  17. size_t number_of_lines { 0 };
  18. };
  19. struct HunkLocation {
  20. Range old_range;
  21. Range new_range;
  22. };
  23. struct Line {
  24. enum class Operation {
  25. Addition = '+',
  26. Removal = '-',
  27. Context = ' ',
  28. // NOTE: This should only be used when deconstructing a hunk into old and new lines (context format)
  29. Change = '!',
  30. };
  31. static constexpr Operation operation_from_symbol(char symbol)
  32. {
  33. switch (symbol) {
  34. case '+':
  35. return Operation::Addition;
  36. case '-':
  37. return Operation::Removal;
  38. case ' ':
  39. return Operation::Context;
  40. default:
  41. VERIFY_NOT_REACHED();
  42. }
  43. }
  44. Operation operation;
  45. String content;
  46. };
  47. struct Hunk {
  48. HunkLocation location;
  49. Vector<Line> lines;
  50. };
  51. enum class Format {
  52. Unified,
  53. Unknown,
  54. };
  55. struct Header {
  56. Format format { Format::Unknown };
  57. String old_file_path;
  58. String new_file_path;
  59. };
  60. struct Patch {
  61. Header header;
  62. Vector<Hunk> hunks;
  63. };
  64. class Parser : public GenericLexer {
  65. public:
  66. using GenericLexer::GenericLexer;
  67. ErrorOr<Patch> parse_patch(Optional<size_t> const& strip_count = {});
  68. ErrorOr<Vector<Hunk>> parse_hunks();
  69. private:
  70. ErrorOr<Header> parse_header(Optional<size_t> const& strip_count);
  71. ErrorOr<String> parse_file_line(Optional<size_t> const& strip_count);
  72. Optional<HunkLocation> consume_unified_location();
  73. bool consume_line_number(size_t& number);
  74. };
  75. ErrorOr<Vector<Hunk>> parse_hunks(StringView diff);
  76. }
  77. template<>
  78. struct AK::Formatter<Diff::Line::Operation> : Formatter<FormatString> {
  79. ErrorOr<void> format(FormatBuilder& builder, Diff::Line::Operation operation)
  80. {
  81. return Formatter<FormatString>::format(builder, "{}"sv, static_cast<char>(operation));
  82. }
  83. };
  84. template<>
  85. struct AK::Formatter<Diff::Line> : Formatter<FormatString> {
  86. ErrorOr<void> format(FormatBuilder& builder, Diff::Line const& line)
  87. {
  88. return Formatter<FormatString>::format(builder, "{}{}"sv, line.operation, line.content);
  89. }
  90. };
  91. template<>
  92. struct AK::Formatter<Diff::HunkLocation> : Formatter<FormatString> {
  93. static ErrorOr<void> format(FormatBuilder& format_builder, Diff::HunkLocation const& location)
  94. {
  95. auto& builder = format_builder.builder();
  96. TRY(builder.try_appendff("@@ -{}"sv, location.old_range.start_line));
  97. if (location.old_range.number_of_lines != 1)
  98. TRY(builder.try_appendff(",{}", location.old_range.number_of_lines));
  99. TRY(builder.try_appendff(" +{}", location.new_range.start_line));
  100. if (location.new_range.number_of_lines != 1)
  101. TRY(builder.try_appendff(",{}", location.new_range.number_of_lines));
  102. return builder.try_appendff(" @@");
  103. }
  104. };