ladybird/Userland/Libraries/LibDiff/Hunks.h
Shannon Booth 828d791a4f LibDiff: Add Diff::apply_patch
Given a set of lines from the file we are patching, and a patch itself,
this function will try and locate where in the file to apply that patch,
and write the result of patching that file (if successful) to the output
stream.
2023-07-13 10:29:30 +01:00

129 lines
3 KiB
C++

/*
* Copyright (c) 2020, Itamar S. <itamar8910@gmail.com>
* Copyright (c) 2023, Shannon Booth <shannon.ml.booth@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Assertions.h>
#include <AK/Format.h>
#include <AK/GenericLexer.h>
#include <AK/String.h>
#include <AK/StringView.h>
#include <AK/Vector.h>
namespace Diff {
struct Range {
size_t start_line { 0 };
size_t number_of_lines { 0 };
};
struct HunkLocation {
Range old_range;
Range new_range;
};
struct Line {
enum class Operation {
Addition = '+',
Removal = '-',
Context = ' ',
// NOTE: This should only be used when deconstructing a hunk into old and new lines (context format)
Change = '!',
};
static constexpr Operation operation_from_symbol(char symbol)
{
switch (symbol) {
case '+':
return Operation::Addition;
case '-':
return Operation::Removal;
case ' ':
return Operation::Context;
default:
VERIFY_NOT_REACHED();
}
}
Operation operation;
String content;
};
struct Hunk {
HunkLocation location;
Vector<Line> lines;
};
enum class Format {
Unified,
Unknown,
};
struct Header {
Format format { Format::Unknown };
String old_file_path;
String new_file_path;
};
struct Patch {
Header header;
Vector<Hunk> hunks;
};
class Parser : public GenericLexer {
public:
using GenericLexer::GenericLexer;
ErrorOr<Vector<Hunk>> parse_hunks();
ErrorOr<Header> parse_header();
private:
Optional<HunkLocation> consume_unified_location();
bool consume_line_number(size_t& number);
};
ErrorOr<Vector<Hunk>> parse_hunks(StringView diff);
}
template<>
struct AK::Formatter<Diff::Line::Operation> : Formatter<FormatString> {
ErrorOr<void> format(FormatBuilder& builder, Diff::Line::Operation operation)
{
return Formatter<FormatString>::format(builder, "{}"sv, static_cast<char>(operation));
}
};
template<>
struct AK::Formatter<Diff::Line> : Formatter<FormatString> {
ErrorOr<void> format(FormatBuilder& builder, Diff::Line const& line)
{
return Formatter<FormatString>::format(builder, "{}{}"sv, line.operation, line.content);
}
};
template<>
struct AK::Formatter<Diff::HunkLocation> : Formatter<FormatString> {
static ErrorOr<void> format(FormatBuilder& format_builder, Diff::HunkLocation const& location)
{
auto& builder = format_builder.builder();
TRY(builder.try_appendff("@@ -{}"sv, location.old_range.start_line));
if (location.old_range.number_of_lines != 1)
TRY(builder.try_appendff(",{}", location.old_range.number_of_lines));
TRY(builder.try_appendff(" +{}", location.new_range.start_line));
if (location.new_range.number_of_lines != 1)
TRY(builder.try_appendff(",{}", location.new_range.number_of_lines));
return builder.try_appendff(" @@");
}
};