|
|
|
|
@@ -3,106 +3,106 @@
|
|
|
|
|
// FIX known issues:
|
|
|
|
|
|
|
|
|
|
const diff =
|
|
|
|
|
\\diff --git a/build.zig.zon b/build.zig.zon
|
|
|
|
|
\\index a039487..944fc49 100644
|
|
|
|
|
\\--- a/build.zig.zon
|
|
|
|
|
\\+++ b/build.zig.zon
|
|
|
|
|
\\@@ -3,8 +3,8 @@
|
|
|
|
|
\\ .version = "0.0.1",
|
|
|
|
|
\\ .dependencies = .{
|
|
|
|
|
\\ .zterm = .{
|
|
|
|
|
\\- .url = "git+https://gitea.yves-biener.de/yves-biener/zterm#e972a2ea0f7a9f8caffd439ef206474b46475f91",
|
|
|
|
|
\\- .hash = "zterm-0.3.0-1xmmENkhHAB2rmNJFH-9rRqiRLnT673xwuMrqLwOnlT_",
|
|
|
|
|
\\+ .url = "git+https://gitea.yves-biener.de/yves-biener/zterm#b1a0d60ae379bb91b862d7c4a8a2210cd74c4387",
|
|
|
|
|
\\+ .hash = "zterm-0.3.0-1xmmEMotHACXUXNtsX1P2iz3XQgabNz1PfF7oazYtNam",
|
|
|
|
|
\\ },
|
|
|
|
|
\\ },
|
|
|
|
|
\\ .minimum_zig_version = "0.16.0-dev.1254+bf15c791f",
|
|
|
|
|
\\diff --git a/src/elements.zig b/src/elements.zig
|
|
|
|
|
\\index c0225ae..fb1ad68 100644
|
|
|
|
|
\\--- a/src/elements.zig
|
|
|
|
|
\\+++ b/src/elements.zig
|
|
|
|
|
\\@@ -59,6 +59,7 @@ pub fn Tree(App: type) type {
|
|
|
|
|
\\ .ptr = this,
|
|
|
|
|
\\ .vtable = &.{
|
|
|
|
|
\\ .resize = resize,
|
|
|
|
|
\\+ .minSize = minSize,
|
|
|
|
|
\\ .handle = handle,
|
|
|
|
|
\\ .content = content,
|
|
|
|
|
\\ },
|
|
|
|
|
\\@@ -70,6 +71,19 @@ pub fn Tree(App: type) type {
|
|
|
|
|
\\ this.size = size;
|
|
|
|
|
\\ }
|
|
|
|
|
\\
|
|
|
|
|
\\+ fn minSize(ctx: *anyopaque, model: *const App.Model, _: Point) Point {
|
|
|
|
|
\\+ // NOTE as we assume the model contents do not change we could calculate the
|
|
|
|
|
\\+ // maximum width required for this `Element` and return that, instead of
|
|
|
|
|
\\+ // calculating the width every time anew. For now this works fine.
|
|
|
|
|
\\+ const this: *@This() = @ptrCast(@alignCast(ctx));
|
|
|
|
|
\\+ const changes: []const Model.Index = model.changes.keys();
|
|
|
|
|
\\+ const files = changes[this.scrollback..];
|
|
|
|
|
\\+ var width: u16 = 0;
|
|
|
|
|
\\+ for (files) |file| width = @max(@as(u16, @intCast(file.len)), width);
|
|
|
|
|
\\+ return .{ .x = width };
|
|
|
|
|
\\+ }
|
|
|
|
|
\\+
|
|
|
|
|
\\+
|
|
|
|
|
\\ fn handle(ctx: *anyopaque, model: *App.Model, event: App.Event) !void {
|
|
|
|
|
\\ const this: *@This() = @ptrCast(@alignCast(ctx));
|
|
|
|
|
\\ switch (event) {
|
|
|
|
|
\\@@ -78,7 +92,17 @@ pub fn Tree(App: type) type {
|
|
|
|
|
\\ this.scrollback = 0;
|
|
|
|
|
\\ this.idx = 0;
|
|
|
|
|
\\ },
|
|
|
|
|
\\- // TODO also support key inputs to change the current file?
|
|
|
|
|
\\+ .key => |key| {
|
|
|
|
|
\\+ if (key.eql(.{ .cp = 'J' }) and this.idx < this.len - 1) {
|
|
|
|
|
\\+ this.idx += 1;
|
|
|
|
|
\\+ this.queue.push(.{ .file = this.idx });
|
|
|
|
|
\\+ }
|
|
|
|
|
\\+
|
|
|
|
|
\\+ if (key.eql(.{ .cp = 'K' }) and this.idx > 0) {
|
|
|
|
|
\\+ this.idx -= 1;
|
|
|
|
|
\\+ this.queue.push(.{ .file = this.idx });
|
|
|
|
|
\\+ }
|
|
|
|
|
\\+ },
|
|
|
|
|
\\ .mouse => |mouse| if (this.len > 0) switch (mouse.button) {
|
|
|
|
|
\\ .left => if (mouse.y + this.scrollback < this.len) {
|
|
|
|
|
\\ this.idx = mouse.y + this.scrollback;
|
|
|
|
|
\\@@ -112,7 +136,6 @@ pub fn Tree(App: type) type {
|
|
|
|
|
\\ const row_color: zterm.Color = if (this.idx == idx) .blue else .default;
|
|
|
|
|
\\ cell_idx = row * size.x;
|
|
|
|
|
\\ for (0..size.x) |_| {
|
|
|
|
|
\\- cell_idx += 1;
|
|
|
|
|
\\ if (value_idx >= value.len) break;
|
|
|
|
|
\\ const cp = value[value_idx];
|
|
|
|
|
\\ defer value_idx += 1;
|
|
|
|
|
\\@@ -128,6 +151,7 @@ pub fn Tree(App: type) type {
|
|
|
|
|
\\ else => cp,
|
|
|
|
|
\\ };
|
|
|
|
|
\\ cells[cell_idx].style.fg = row_color;
|
|
|
|
|
\\+ cell_idx += 1;
|
|
|
|
|
\\ }
|
|
|
|
|
\\ }
|
|
|
|
|
\\ }
|
|
|
|
|
\\diff --git a/src/root.zig b/src/root.zig
|
|
|
|
|
\\index 29aa832..54d3631 100644
|
|
|
|
|
\\--- a/src/root.zig
|
|
|
|
|
\\+++ b/src/root.zig
|
|
|
|
|
\\@@ -17,7 +17,11 @@ pub fn Container(App: type, gpa: Allocator, element: *elements.Root(App), tree:
|
|
|
|
|
\\ },
|
|
|
|
|
\\ },
|
|
|
|
|
\\ }, element.element());
|
|
|
|
|
\\- try root.append(try .init(gpa, .{}, tree.element()));
|
|
|
|
|
\\+ try root.append(try .init(gpa, .{
|
|
|
|
|
\\+ .size = .{
|
|
|
|
|
\\+ .grow = .vertical,
|
|
|
|
|
\\+ },
|
|
|
|
|
\\+ }, tree.element()));
|
|
|
|
|
\\ try root.append(try .init(gpa, .{}, .{})); // empty container holding the scrollable element for the diff of each file
|
|
|
|
|
\\ return root;
|
|
|
|
|
\\ }
|
|
|
|
|
\\
|
|
|
|
|
\\diff --git a/build.zig.zon b/build.zig.zon
|
|
|
|
|
\\index a039487..944fc49 100644
|
|
|
|
|
\\--- a/build.zig.zon
|
|
|
|
|
\\+++ b/build.zig.zon
|
|
|
|
|
\\@@ -3,8 +3,8 @@
|
|
|
|
|
\\ .version = "0.0.1",
|
|
|
|
|
\\ .dependencies = .{
|
|
|
|
|
\\ .zterm = .{
|
|
|
|
|
\\- .url = "git+https://gitea.yves-biener.de/yves-biener/zterm#e972a2ea0f7a9f8caffd439ef206474b46475f91",
|
|
|
|
|
\\- .hash = "zterm-0.3.0-1xmmENkhHAB2rmNJFH-9rRqiRLnT673xwuMrqLwOnlT_",
|
|
|
|
|
\\+ .url = "git+https://gitea.yves-biener.de/yves-biener/zterm#b1a0d60ae379bb91b862d7c4a8a2210cd74c4387",
|
|
|
|
|
\\+ .hash = "zterm-0.3.0-1xmmEMotHACXUXNtsX1P2iz3XQgabNz1PfF7oazYtNam",
|
|
|
|
|
\\ },
|
|
|
|
|
\\ },
|
|
|
|
|
\\ .minimum_zig_version = "0.16.0-dev.1254+bf15c791f",
|
|
|
|
|
\\diff --git a/src/elements.zig b/src/elements.zig
|
|
|
|
|
\\index c0225ae..fb1ad68 100644
|
|
|
|
|
\\--- a/src/elements.zig
|
|
|
|
|
\\+++ b/src/elements.zig
|
|
|
|
|
\\@@ -59,6 +59,7 @@ pub fn Tree(App: type) type {
|
|
|
|
|
\\ .ptr = this,
|
|
|
|
|
\\ .vtable = &.{
|
|
|
|
|
\\ .resize = resize,
|
|
|
|
|
\\+ .minSize = minSize,
|
|
|
|
|
\\ .handle = handle,
|
|
|
|
|
\\ .content = content,
|
|
|
|
|
\\ },
|
|
|
|
|
\\@@ -70,6 +71,19 @@ pub fn Tree(App: type) type {
|
|
|
|
|
\\ this.size = size;
|
|
|
|
|
\\ }
|
|
|
|
|
\\
|
|
|
|
|
\\+ fn minSize(ctx: *anyopaque, model: *const App.Model, _: Point) Point {
|
|
|
|
|
\\+ // NOTE as we assume the model contents do not change we could calculate the
|
|
|
|
|
\\+ // maximum width required for this `Element` and return that, instead of
|
|
|
|
|
\\+ // calculating the width every time anew. For now this works fine.
|
|
|
|
|
\\+ const this: *@This() = @ptrCast(@alignCast(ctx));
|
|
|
|
|
\\+ const changes: []const Model.Index = model.changes.keys();
|
|
|
|
|
\\+ const files = changes[this.scrollback..];
|
|
|
|
|
\\+ var width: u16 = 0;
|
|
|
|
|
\\+ for (files) |file| width = @max(@as(u16, @intCast(file.len)), width);
|
|
|
|
|
\\+ return .{ .x = width };
|
|
|
|
|
\\+ }
|
|
|
|
|
\\+
|
|
|
|
|
\\+
|
|
|
|
|
\\ fn handle(ctx: *anyopaque, model: *App.Model, event: App.Event) !void {
|
|
|
|
|
\\ const this: *@This() = @ptrCast(@alignCast(ctx));
|
|
|
|
|
\\ switch (event) {
|
|
|
|
|
\\@@ -78,7 +92,17 @@ pub fn Tree(App: type) type {
|
|
|
|
|
\\ this.scrollback = 0;
|
|
|
|
|
\\ this.idx = 0;
|
|
|
|
|
\\ },
|
|
|
|
|
\\- // TODO also support key inputs to change the current file?
|
|
|
|
|
\\+ .key => |key| {
|
|
|
|
|
\\+ if (key.eql(.{ .cp = 'J' }) and this.idx < this.len - 1) {
|
|
|
|
|
\\+ this.idx += 1;
|
|
|
|
|
\\+ this.queue.push(.{ .file = this.idx });
|
|
|
|
|
\\+ }
|
|
|
|
|
\\+
|
|
|
|
|
\\+ if (key.eql(.{ .cp = 'K' }) and this.idx > 0) {
|
|
|
|
|
\\+ this.idx -= 1;
|
|
|
|
|
\\+ this.queue.push(.{ .file = this.idx });
|
|
|
|
|
\\+ }
|
|
|
|
|
\\+ },
|
|
|
|
|
\\ .mouse => |mouse| if (this.len > 0) switch (mouse.button) {
|
|
|
|
|
\\ .left => if (mouse.y + this.scrollback < this.len) {
|
|
|
|
|
\\ this.idx = mouse.y + this.scrollback;
|
|
|
|
|
\\@@ -112,7 +136,6 @@ pub fn Tree(App: type) type {
|
|
|
|
|
\\ const row_color: zterm.Color = if (this.idx == idx) .blue else .default;
|
|
|
|
|
\\ cell_idx = row * size.x;
|
|
|
|
|
\\ for (0..size.x) |_| {
|
|
|
|
|
\\- cell_idx += 1;
|
|
|
|
|
\\ if (value_idx >= value.len) break;
|
|
|
|
|
\\ const cp = value[value_idx];
|
|
|
|
|
\\ defer value_idx += 1;
|
|
|
|
|
\\@@ -128,6 +151,7 @@ pub fn Tree(App: type) type {
|
|
|
|
|
\\ else => cp,
|
|
|
|
|
\\ };
|
|
|
|
|
\\ cells[cell_idx].style.fg = row_color;
|
|
|
|
|
\\+ cell_idx += 1;
|
|
|
|
|
\\ }
|
|
|
|
|
\\ }
|
|
|
|
|
\\ }
|
|
|
|
|
\\diff --git a/src/root.zig b/src/root.zig
|
|
|
|
|
\\index 29aa832..54d3631 100644
|
|
|
|
|
\\--- a/src/root.zig
|
|
|
|
|
\\+++ b/src/root.zig
|
|
|
|
|
\\@@ -17,7 +17,11 @@ pub fn Container(App: type, gpa: Allocator, element: *elements.Root(App), tree:
|
|
|
|
|
\\ },
|
|
|
|
|
\\ },
|
|
|
|
|
\\ }, element.element());
|
|
|
|
|
\\- try root.append(try .init(gpa, .{}, tree.element()));
|
|
|
|
|
\\+ try root.append(try .init(gpa, .{
|
|
|
|
|
\\+ .size = .{
|
|
|
|
|
\\+ .grow = .vertical,
|
|
|
|
|
\\+ },
|
|
|
|
|
\\+ }, tree.element()));
|
|
|
|
|
\\ try root.append(try .init(gpa, .{}, .{})); // empty container holding the scrollable element for the diff of each file
|
|
|
|
|
\\ return root;
|
|
|
|
|
\\ }
|
|
|
|
|
\\
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
pub fn main() !void {
|
|
|
|
|
@@ -120,6 +120,13 @@ pub fn main() !void {
|
|
|
|
|
else => area.allocator(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var threaded_io: std.Io.Threaded = .init(allocator, .{});
|
|
|
|
|
errdefer threaded_io.deinit();
|
|
|
|
|
const io = threaded_io.io();
|
|
|
|
|
|
|
|
|
|
var diff_input: [:0]u8 = undefined;
|
|
|
|
|
defer allocator.free(diff_input);
|
|
|
|
|
|
|
|
|
|
// argument handling
|
|
|
|
|
{
|
|
|
|
|
var arg_it = try std.process.argsWithAllocator(allocator);
|
|
|
|
|
@@ -127,21 +134,39 @@ pub fn main() !void {
|
|
|
|
|
|
|
|
|
|
// TODO may there be other options?
|
|
|
|
|
// usage: tui-diff
|
|
|
|
|
|
|
|
|
|
// skip own executable name
|
|
|
|
|
_ = arg_it.skip();
|
|
|
|
|
|
|
|
|
|
// only handle the first argument otherwise ignore!
|
|
|
|
|
if (arg_it.next()) |file| {
|
|
|
|
|
var buffer: [4096]u8 = undefined;
|
|
|
|
|
var stream = std.Io.File.readerStreaming(
|
|
|
|
|
try std.Io.Dir.openFileAbsolute(
|
|
|
|
|
io,
|
|
|
|
|
file,
|
|
|
|
|
.{ .mode = .read_only },
|
|
|
|
|
),
|
|
|
|
|
io,
|
|
|
|
|
&buffer,
|
|
|
|
|
);
|
|
|
|
|
const reader = &stream.interface;
|
|
|
|
|
diff_input = try reader.allocRemainingAlignedSentinel(
|
|
|
|
|
allocator,
|
|
|
|
|
.unlimited,
|
|
|
|
|
.of(u8),
|
|
|
|
|
0,
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
// TODO detect VCS in the current working directory (and traversing upwards if none found at point?)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// tui creation
|
|
|
|
|
errdefer |err| log.err("Application Error: {any}", .{err});
|
|
|
|
|
|
|
|
|
|
var threaded_io: std.Io.Threaded = .init(allocator, .{});
|
|
|
|
|
errdefer threaded_io.deinit();
|
|
|
|
|
const io = threaded_io.io();
|
|
|
|
|
|
|
|
|
|
var renderer = zterm.Renderer.Buffered.init(allocator);
|
|
|
|
|
defer renderer.deinit();
|
|
|
|
|
|
|
|
|
|
var app: App = .init(io, try .init(allocator, diff));
|
|
|
|
|
var app: App = .init(io, try .init(allocator, diff_input));
|
|
|
|
|
defer app.model.deinit(allocator);
|
|
|
|
|
|
|
|
|
|
var element_root: tui_diff.elements.Root(App) = .init(allocator);
|
|
|
|
|
|