浏览代码

LibDiff: Add support for writing formatted context hunks

There is a little bit more complexity involved here than the other
formats. In particular, this is due to the need to determine whether
an addition line or removal line is just that, or a 'change'.
Shannon Booth 2 年之前
父节点
当前提交
f02cf2704c
共有 3 个文件被更改,包括 119 次插入0 次删除
  1. 116 0
      Userland/Libraries/LibDiff/Format.cpp
  2. 1 0
      Userland/Libraries/LibDiff/Format.h
  3. 2 0
      Userland/Libraries/LibDiff/Hunks.h

+ 116 - 0
Userland/Libraries/LibDiff/Format.cpp

@@ -7,6 +7,7 @@
  */
 
 #include "Format.h"
+#include <AK/Assertions.h>
 #include <AK/DeprecatedString.h>
 #include <AK/Stream.h>
 #include <AK/StringBuilder.h>
@@ -94,4 +95,119 @@ ErrorOr<void> write_normal(Hunk const& hunk, Stream& stream, ColorOutput color_o
     return {};
 }
 
+struct SplitLines {
+    Vector<Line> old_lines;
+    Vector<Line> new_lines;
+};
+
+static ErrorOr<SplitLines> split_hunk_into_old_and_new_lines(Hunk const& hunk)
+{
+    size_t new_lines_last_context = 0;
+    size_t old_lines_last_context = 0;
+    SplitLines lines;
+
+    auto operation = Line::Operation::Context;
+
+    bool is_all_insertions = true;
+    bool is_all_deletions = true;
+
+    auto check_if_line_is_a_change = [&](Line::Operation op) {
+        if (operation != op) {
+            // We've switched from additions to removals or vice-versa.
+            // All lines starting from the last context line we saw must be changes.
+            operation = Line::Operation::Change;
+            for (size_t i = new_lines_last_context; i < lines.new_lines.size(); ++i)
+                lines.new_lines[i].operation = Line::Operation::Change;
+            for (size_t i = old_lines_last_context; i < lines.old_lines.size(); ++i)
+                lines.old_lines[i].operation = Line::Operation::Change;
+        }
+    };
+
+    for (auto const& line : hunk.lines) {
+        switch (line.operation) {
+        case Line::Operation::Context:
+            VERIFY(lines.old_lines.size() < hunk.location.old_range.number_of_lines);
+            VERIFY(lines.new_lines.size() < hunk.location.new_range.number_of_lines);
+
+            operation = Line::Operation::Context;
+            TRY(lines.new_lines.try_append(Line { operation, line.content }));
+            TRY(lines.old_lines.try_append(Line { operation, line.content }));
+            new_lines_last_context = lines.new_lines.size();
+            old_lines_last_context = lines.old_lines.size();
+            break;
+        case Line::Operation::Addition:
+            VERIFY(lines.new_lines.size() < hunk.location.new_range.number_of_lines);
+
+            if (operation != Line::Operation::Context)
+                check_if_line_is_a_change(Line::Operation::Addition);
+            else
+                operation = Line::Operation::Addition;
+
+            TRY(lines.new_lines.try_append(Line { operation, line.content }));
+            is_all_deletions = false;
+            break;
+        case Line::Operation::Removal:
+            VERIFY(lines.old_lines.size() < hunk.location.old_range.number_of_lines);
+
+            if (operation != Line::Operation::Context)
+                check_if_line_is_a_change(Line::Operation::Removal);
+            else
+                operation = Line::Operation::Removal;
+
+            TRY(lines.old_lines.try_append(Line { operation, line.content }));
+            is_all_insertions = false;
+            break;
+        default:
+            VERIFY_NOT_REACHED();
+        }
+    }
+
+    VERIFY(lines.new_lines.size() == hunk.location.new_range.number_of_lines && lines.old_lines.size() == hunk.location.old_range.number_of_lines);
+
+    if (is_all_insertions)
+        lines.old_lines.clear();
+    else if (is_all_deletions)
+        lines.new_lines.clear();
+
+    return lines;
+}
+
+static ErrorOr<void> write_hunk_as_context(Vector<Line> const& old_lines, Vector<Line> const& new_lines, HunkLocation const& location, Stream& stream, ColorOutput color_output)
+{
+    TRY(stream.write_formatted("*** {}", location.old_range.start_line));
+
+    if (location.old_range.number_of_lines > 1)
+        TRY(stream.write_formatted(",{}", location.old_range.start_line + location.old_range.number_of_lines - 1));
+
+    TRY(stream.write_formatted(" ****\n"));
+
+    for (auto const& line : old_lines) {
+        if (color_output == ColorOutput::Yes && (line.operation == Line::Operation::Removal || line.operation == Line::Operation::Change))
+            TRY(stream.write_formatted("\033[31;1m{} {}\033[0m\n", line.operation, line.content));
+        else
+            TRY(stream.write_formatted("{} {}\n", line.operation, line.content));
+    }
+
+    TRY(stream.write_formatted("--- {}", location.new_range.start_line));
+    if (location.new_range.number_of_lines > 1)
+        TRY(stream.write_formatted(",{}", location.new_range.start_line + location.new_range.number_of_lines - 1));
+
+    TRY(stream.write_formatted(" ----\n"));
+
+    for (auto const& line : new_lines) {
+        if (color_output == ColorOutput::Yes && (line.operation == Line::Operation::Addition || line.operation == Line::Operation::Change))
+            TRY(stream.write_formatted("\033[32;1m{} {}\033[0m\n", line.operation, line.content));
+        else
+            TRY(stream.write_formatted("{} {}\n", line.operation, line.content));
+    }
+
+    return {};
+}
+
+ErrorOr<void> write_context(Hunk const& hunk, Stream& stream, ColorOutput color_output)
+{
+    auto const split_lines = TRY(split_hunk_into_old_and_new_lines(hunk));
+    return write_hunk_as_context(split_lines.old_lines, split_lines.new_lines, hunk.location, stream, color_output);
+}
+
 }

+ 1 - 0
Userland/Libraries/LibDiff/Format.h

@@ -25,4 +25,5 @@ ErrorOr<void> write_unified_header(StringView old_path, StringView new_path, Str
 
 ErrorOr<void> write_normal(Hunk const& hunk, Stream& stream, ColorOutput color_output = ColorOutput::No);
 
+ErrorOr<void> write_context(Hunk const& hunk, Stream& stream, ColorOutput color_output = ColorOutput::No);
 }

+ 2 - 0
Userland/Libraries/LibDiff/Hunks.h

@@ -31,6 +31,8 @@ struct Line {
         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)