CUI

Components / Data display

Diff viewer

Compares two versions of a text line by line.

Functions: cui_diff_view

Diff viewer

Usage

static int mode = 0, change = 0;   /* 0 split, 1 inline; the selected change */
static const char *const modes[] = { "Split", "Inline" };
cui_segmented(ui, "mode", modes, 2, &mode, .size = CUI_SIZE_SM);
cui_diff_result r = cui_diff_view(ui, "diff", before, after, .mode = mode ? CUI_DIFF_INLINE : CUI_DIFF_SPLIT,
                                  .current = &change, .language = "c",
                                  .old_title = "config.c (main)", .new_title = "config.c");
if (cui_icon_button(ui, CUI_ICON_ARROW_DOWN, .tooltip = "Next change", .shortcut = "N", .disabled = change >= r.changes - 1))
    change++;   /* the view scrolls to it */
cui_textf(ui, "+%d -%d", r.added, r.removed);

Split mode puts the old text on the left and the new on the right, rows aligned with a quiet filler opposite added or removed lines; inline mode shows removed lines above the lines that replaced them. Below split_min_width (560 points by default) split shows inline, so the view reads on a phone, and r.mode says which mode is shown. Each side has its line numbers and a + or - sign, added lines are tinted with the success color and removed lines with the danger color, and the words that changed inside a replaced line get a stronger tint. With .language the code keeps its syntax colors. Lines longer than the view scroll sideways, both sides together.

Unchanged stretches keep .context lines (3) next to each change and fold the rest into a "42 unchanged lines" row that opens on a click (.no_fold shows everything). With the view focused, N or Alt+Down goes to the next change and P or Alt+Up to the previous one, E opens every fold and Shift+E folds them again, arrows and Page Up/Down scroll, and clicking a change selects it. .current is read and written, so buttons only have to change the number; the result also has the +added -removed summary, the change count and whether the texts were diffed again this frame.

The texts are copied and diffed only when one of them changes, and only the rows in sight are built. The diff is Myers' shortest edit script in linear memory; lines with no match in the other text are set aside first, which keeps large diffs fast and exact. Texts that would need a very long search (many repeated lines, rewritten throughout) stop after about 16 million steps and show the rest as replaced, still a correct diff, flagged by r.approximate. cui_diff_compute returns the hunks without a view, cui_diff_words the changed words of two lines, and cui_diff_view_rows the rows as shown, for line comments.

Keyboard

  • With the view focused, N or Alt+Down goes to the next change and P or Alt+Up to the previous one, E opens every fold and Shift+E folds them again, arrows and Page Up/Down scroll, and clicking a change selects it.

Options of cui_diff_view

OptionTypeDefaultDescription
.modecui_diff_modeOne of CUI_DIFF_SPLIT or CUI_DIFF_INLINE.
.languageconst char *syntax colors, as cui_code_block: "c", "js", "python", ...
.old_titleconst char *a header above the sides, e.g. file names, with the +/- summary
.new_titleconst char *a header above the sides, e.g. file names, with the +/- summary
.currentint *the selected change, read and written (may be NULL); set it to scroll to a change
.contextint3unchanged lines kept beside each change, default 3
.no_foldboolshow every unchanged line
.ignore_eolbool"\r\n" equals "\n", and a missing final newline does not count
.split_min_widthfloat560below this width split shows inline, default 560
.widthcui_sizefilldefault fill
.heightfloatthe rows, up to 420default: the rows, up to 420; negative fills the space
.sizefloat12font size, default 12

cui_diff_result

FieldTypeDescription
addedintlines
removedintlines
changesintchange blocks (hunks without context)
currentintthe selected change, -1 none
current_changedboolthis frame, by the keys, a click or .current
modecui_diff_modewhat is shown: inline when split did not fit
foldsintfolded rows shown, and the unchanged lines they hide
hiddenintfolded rows shown, and the unchanged lines they hide
recomputedboola text changed and was diffed this frame
approximateboolthe search hit its limit: correct, maybe not the shortest

cui_diff_mode: CUI_DIFF_SPLIT, CUI_DIFF_INLINE

Related

  • Icon button: A round button with only an icon, ghost by default.
  • Segmented control: A compact set of two to five choices where one is always selected, for switching between views of the same content.
  • Property inspector: A panel of named properties in folding groups, each edited with the control that fits its type.
  • Code editor: An editable code field with syntax highlighting, for shader editors, script consoles and config files.