Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[yokurang]: Side-by-side diff view #32

Merged
merged 22 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 75 additions & 17 deletions lib/interactive_viewer.ml
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,7 @@ let ui_of_operation operation =
Ui.hcat
[ W.string "Modification of "; W.string ~attr:blue_bold_attr path ]

let ui_of_hunk hunk =
let line_to_string line =
match line with
| `Their text -> W.string ~attr:Notty.A.(fg red) text
| `Mine text -> W.string ~attr:Notty.A.(fg green) text
| `Common text -> W.string text
in
Ui.vcat @@ List.map line_to_string hunk.Patch.lines
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This diff is an artifact of being based off #57, which has been reverted since. Could you rebase or merge main ?

let string_of_hunk = Format.asprintf "%a" Patch.pp_hunk

let current_operation z_patches : ui Lwd.t =
let$ z = Lwd.get z_patches in
Expand All @@ -64,8 +57,8 @@ let current_operation z_patches : ui Lwd.t =
let current_hunks z_patches : ui Lwd.t =
let$ z = Lwd.get z_patches in
let p = Zipper.get_focus z in
let hunks = List.map ui_of_hunk p.Patch.hunks in
Ui.vcat hunks
let hunks = List.map (fun h -> W.string (string_of_hunk h)) p.Patch.hunks in
Ui.vcat @@ hunks

type direction = Prev | Next

Expand Down Expand Up @@ -111,6 +104,65 @@ let change_summary z_patches : ui Lwd.t =
in
W.string ~attr:Notty.A.(fg lightcyan) operation_count

(** Side by side diff view implementation **)

type view_mode = SideBySide | Normal

let view_mode = Lwd.var Normal

let toggle_view_mode () =
match Lwd.peek view_mode with
| Normal -> Lwd.set view_mode SideBySide
| SideBySide -> Lwd.set view_mode Normal

let rec split_and_align_hunk hunks mine_acc their_acc =
match hunks with
| [] -> (List.rev mine_acc, List.rev their_acc)
| (`Common _ as common) :: t ->
split_and_align_hunk t (common :: mine_acc) (common :: their_acc)
| `Mine s :: `Their s' :: t ->
split_and_align_hunk t (`Mine s :: mine_acc) (`Their s' :: their_acc)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pairs only one added line with one removed line. This might not be the output we expect, for example:

18 11     W.scrollbox
19 12       (W.vbox
20 13          [
21    -          pure_str "Hello world!";
22    -          string_of_counter counter_d;
   14 +          pure_str (string_of_operation patch.operation);
23 15            Lwd.pure
24 16            @@ Ui.keyboard_area

This diff renders like this in side-by-side:

  W.scrollbox                                                 W.scrollbox
    (W.vbox                                                     (W.vbox
       [                                                           [
         pure_str "Hello world!";
         string_of_counter counter_d;                                pure_str (string_of_operation patch.operation);
         Lwd.pure                                                    Lwd.pure
         @@ Ui.keyboard_area                                         @@ Ui.keyboard_area

It would be more intuitive if the whole block of removed line was paired with the added line.
Visually aligning the first removed line of the block with the first added line, leaving the empty line in the right side to be below the green line instead of above.

| `Mine s :: t ->
split_and_align_hunk t (`Mine s :: mine_acc) (`Common "" :: their_acc)
| `Their s :: t ->
split_and_align_hunk t (`Common "" :: mine_acc) (`Their s :: their_acc)

let lines_to_ui lines attr_change =
List.map
(fun line ->
let content, attr =
match line with
| `Common s -> (s, Notty.A.empty)
| `Mine s -> (s, attr_change)
| `Their s -> (s, attr_change)
in
W.string ~attr content)
lines

let ui_of_hunk_side_by_side hunk =
let mine_lines, their_lines = split_and_align_hunk hunk.Patch.lines [] [] in

let attr_mine = Notty.A.(fg red ++ st bold) in
let attr_their = Notty.A.(fg green ++ st bold) in

let mine_ui = lines_to_ui mine_lines attr_mine in
let their_ui = lines_to_ui their_lines attr_their in
let space = Ui.space 1 0 in
Ui.hcat
[
Ui.resize ~w:0 ~sw:2 (Ui.vcat mine_ui);
space;
Ui.resize ~w:0 ~sw:2 (Ui.vcat their_ui);
]

let current_hunks_side_by_side z_patches : ui Lwd.t =
let$ z = Lwd.get z_patches in
let p = Zipper.get_focus z in
let hunks_ui = List.map (fun h -> ui_of_hunk_side_by_side h) p.Patch.hunks in
Ui.vcat @@ hunks_ui

(** end of side by side diff view implementation **)

let view (patches : Patch.t list) =
let help_panel =
Ui.vcat
Expand All @@ -120,14 +172,21 @@ let view (patches : Patch.t list) =
W.string "q: Quit the diffcessible viewer";
W.string "n: Move to the next operation, if present";
W.string "p: Move to the previous operation, if present";
W.string "g: Scroll back to the top of the displayed operation.";
W.string "t: Toggle view mode";
W.string "l: Toggle line numbers";
]
in
let z_patches : 'a Zipper.t Lwd.var =
match Zipper.zipper_of_list patches with
| Some z -> Lwd.var z
| None -> failwith "zipper_of_list: empty list"
in
let hunks_ui =
Lwd.bind (Lwd.get view_mode) ~f:(fun mode ->
match mode with
| Normal -> current_hunks z_patches
| SideBySide -> current_hunks_side_by_side z_patches)
in
let curr_scroll_state = Lwd.var W.default_scroll_state in
let change_scroll_state _action state =
let off_screen = state.W.position > state.W.bound in
Expand Down Expand Up @@ -158,8 +217,7 @@ let view (patches : Patch.t list) =
current_operation z_patches;
W.vscroll_area
~state:(Lwd.get curr_scroll_state)
~change:change_scroll_state
@@ current_hunks z_patches;
~change:change_scroll_state hunks_ui;
Lwd.pure
@@ Ui.keyboard_area
(function
Expand All @@ -175,14 +233,14 @@ let view (patches : Patch.t list) =
| `ASCII 'h', [] ->
Lwd.set help true;
`Handled
| `ASCII 'g', [] ->
Lwd.set curr_scroll_state
{ (Lwd.peek curr_scroll_state) with W.position = 0 };
| `ASCII 't', [] ->
toggle_view_mode ();
`Handled
| _ -> `Unhandled)
(W.string
"Type 'h' to go to the help panel, 'q' to quit, 'n' to go to \
the next operation, 'p' to go to the previous operation");
the next operation, 'p' to go to the previous operation. \
Press 't' to toggle view mode.");
]
in
W.vbox [ ui ]
Expand Down
87 changes: 80 additions & 7 deletions test/cram/test.t
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ This is a cram test for the new executable.



Type 'h' to go to the help panel, 'q' to quit, 'n' to go to the next operation, 'p' to go to the previous operation
Type 'h' to go to the help panel, 'q' to quit, 'n' to go to the next operation, 'p' to go to the previous operation. Press 't' to toggle view mode.
$ dummy_terminal example.diff n
Operation 2 of 14, 1 hunk
3 additions, 1 removal
Expand All @@ -42,7 +42,7 @@ This is a cram test for the new executable.



Type 'h' to go to the help panel, 'q' to quit, 'n' to go to the next operation, 'p' to go to the previous operation
Type 'h' to go to the help panel, 'q' to quit, 'n' to go to the next operation, 'p' to go to the previous operation. Press 't' to toggle view mode.
$ dummy_terminal example.diff n n
Operation 3 of 14, 1 hunk
1 addition, 1 removal
Expand All @@ -63,16 +63,16 @@ This is a cram test for the new executable.



Type 'h' to go to the help panel, 'q' to quit, 'n' to go to the next operation, 'p' to go to the previous operation
Type 'h' to go to the help panel, 'q' to quit, 'n' to go to the next operation, 'p' to go to the previous operation. Press 't' to toggle view mode.
$ dummy_terminal example.diff h
Help Panel:

h: Open the help panel
q: Quit the diffcessible viewer
n: Move to the next operation, if present
p: Move to the previous operation, if present
g: Scroll back to the top of the displayed operation.

t: Toggle view mode
l: Toggle line numbers



Expand Down Expand Up @@ -105,7 +105,7 @@ This is a cram test for the new executable.



Type 'h' to go to the help panel, 'q' to quit, 'n' to go to the next operation, 'p' to go to the previous operation
Type 'h' to go to the help panel, 'q' to quit, 'n' to go to the next operation, 'p' to go to the previous operation. Press 't' to toggle view mode.
$ dummy_terminal more-examples.diff n
Operation 2 of 2, 1 hunk
2 additions, 1 removal
Expand All @@ -126,4 +126,77 @@ This is a cram test for the new executable.



Type 'h' to go to the help panel, 'q' to quit, 'n' to go to the next operation, 'p' to go to the previous operation
Type 'h' to go to the help panel, 'q' to quit, 'n' to go to the next operation, 'p' to go to the previous operation. Press 't' to toggle view mode.






# Testing for side by side view
$ dummy_terminal more-examples.diff t
Operation 1 of 2, 1 hunk
2 additions, 1 removal
Modification of file.txt
Hi everyone! Hello World!
This is the diffcessible project.














Type 'h' to go to the help panel, 'q' to quit, 'n' to go to the next operation, 'p' to go to the previous operation. Press 't' to toggle view mode.

$ dummy_terminal more-examples.diff n t
Operation 2 of 2, 1 hunk
2 additions, 1 removal
Modification of file.txt
This file starts at line 3. This file starts at line 5.
This is the second test case in this file.














Type 'h' to go to the help panel, 'q' to quit, 'n' to go to the next operation, 'p' to go to the previous operation. Press 't' to toggle view mode.

$ dummy_terminal more-examples.diff n n t
Operation 2 of 2, 1 hunk
2 additions, 1 removal
Modification of file.txt
This file starts at line 3. This file starts at line 5.
This is the second test case in this file.














Type 'h' to go to the help panel, 'q' to quit, 'n' to go to the next operation, 'p' to go to the previous operation. Press 't' to toggle view mode.