Compare commits

...

4 commits

4 changed files with 88 additions and 36 deletions

View file

@ -2,7 +2,7 @@ const std = @import("std");
const builtin = @import("builtin");
const cbor = @import("cbor");
const TypedInt = @import("TypedInt");
const VcsBlame = @import("VcsBlame");
pub const VcsBlame = @import("VcsBlame");
const file_type_config = @import("file_type_config");
const Allocator = std.mem.Allocator;
const ArrayList = std.ArrayList;

View file

@ -1538,13 +1538,16 @@ pub const Editor = struct {
return if (head_line < 0) null else @intCast(head_line);
}
pub fn get_vcs_blame(self: *const Self, row: usize) ?*const Buffer.VcsBlame.Commit {
const buffer = self.buffer orelse return null;
const blame_row = self.get_delta_lines_until_row(row) orelse return null;
return buffer.get_vcs_blame(blame_row) orelse return null;
}
fn render_blame(self: *Self, theme: *const Widget.Theme, hl_row: ?usize, cell_map: CellMap) !void {
const cursor = self.get_primary().cursor;
const pos = self.screen_cursor(&cursor) orelse return;
const buffer = self.buffer orelse return;
const blame_row = self.get_delta_lines_until_row(cursor.row) orelse return;
const commit = buffer.get_vcs_blame(blame_row) orelse return;
const commit = self.get_vcs_blame(cursor.row) orelse return;
const screen_width = self.view.cols;
var space_begin = screen_width;

View file

@ -9,6 +9,7 @@
/// {{selections*}} - All current selections expanded to multiple quoted arguments
/// {{indent_mode}} - The current indent mode ("tabs" or "spaces")
/// {{indent_size}} - The current indent size (in columns)
/// {{blame_commit}} - The blame commit ID at the line number of the primary cursor
pub fn expand(allocator: Allocator, arg: []const u8) Error![]const u8 {
var result: std.Io.Writer.Allocating = .init(allocator);
defer result.deinit();
@ -160,6 +161,18 @@ const functions = struct {
try stream.writer.print("{d}", .{ed.indent_size});
return stream.toOwnedSlice();
}
/// {{blame_commit}} - The blame commit ID at the line number of the primary cursor
pub fn blame_commit(allocator: Allocator) Error![]const u8 {
const mv = tui.mainview() orelse return &.{};
const ed = mv.get_active_editor() orelse return &.{};
const row = ed.get_primary().cursor.row;
const commit = ed.get_vcs_blame(row);
const id = if (commit) |c| c.id else "";
var stream: std.Io.Writer.Allocating = .init(allocator);
try stream.writer.print("{s}", .{id});
return stream.toOwnedSlice();
}
};
fn get_functions() []struct { []const u8, Function } {

View file

@ -30,6 +30,7 @@ const @"style.config" = struct {
close_icon_fg: colors = .Error,
save_icon: []const u8 = "󰆓",
save_icon_fg: ?colors = null,
clipping_indicator: []const u8 = "»",
spacer: []const u8 = "|",
spacer_fg: colors = .active_bg,
@ -100,8 +101,8 @@ pub fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?Event
pub const TabBar = struct {
allocator: std.mem.Allocator,
plane: Plane,
widget_list: *WidgetList,
widget_list_widget: Widget,
splits_list: *WidgetList,
splits_list_widget: Widget,
event_handler: ?EventHandler,
tabs: []TabBarTab = &[_]TabBarTab{},
active_focused_buffer_ref: ?Buffer.Ref = null,
@ -133,8 +134,8 @@ pub const TabBar = struct {
return .{
.allocator = allocator,
.plane = w.plane,
.widget_list = w,
.widget_list_widget = w.widget(),
.splits_list = w,
.splits_list_widget = w.widget(),
.event_handler = event_handler,
.tab_style = tab_style,
.tab_style_bufs = tab_style_bufs,
@ -145,13 +146,13 @@ pub const TabBar = struct {
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
root.free_config(self.allocator, self.tab_style_bufs);
self.allocator.free(self.tabs);
self.widget_list_widget.deinit(allocator);
self.splits_list_widget.deinit(allocator);
allocator.destroy(self);
}
pub fn layout(self: *Self) Widget.Layout {
return if (self.tabs.len >= self.minimum_tabs_shown)
self.widget_list_widget.layout()
self.splits_list_widget.layout()
else
.{ .static = 0 };
}
@ -159,9 +160,9 @@ pub const TabBar = struct {
pub fn update(self: *Self) void {
const drag_source, const drag_btn = tui.get_drag_source();
self.update_tabs(drag_source) catch {};
self.widget_list_widget.resize(Widget.Box.from(self.plane));
self.widget_list_widget.update();
for (self.widget_list.widgets.items) |*split_widgetstate| if (split_widgetstate.widget.dynamic_cast(WidgetList)) |split|
self.splits_list_widget.resize(Widget.Box.from(self.plane));
self.splits_list_widget.update();
for (self.splits_list.widgets.items) |*split_widgetstate| if (split_widgetstate.widget.dynamic_cast(WidgetList)) |split|
for (split.widgets.items) |*widgetstate| if (widgetstate.widget.dynamic_cast(Tab.ButtonType)) |btn| if (btn.drag_pos) |_|
tui.update_drag_source(&widgetstate.widget, drag_btn);
tui.refresh_hover(@src());
@ -177,10 +178,40 @@ pub const TabBar = struct {
});
self.plane.fill(" ");
self.plane.home();
for (self.tabs) |*tab| _ = tab.widget.render(theme);
for (self.tabs) |*tab| {
const clipped, const clip_box = self.is_tab_clipped(tab);
if (clipped) {
if (clip_box) |box| self.render_clipping_indicator(box, theme);
continue;
}
_ = tab.widget.render(theme);
}
return false;
}
fn is_tab_clipped(self: *const Self, tab: *const TabBarTab) struct { bool, ?Widget.Box } {
const view = tab.view orelse return .{ true, null };
const split_idx = if (view < self.splits_list.widgets.items.len) view else return .{ true, null };
const split = self.splits_list.widgets.items[split_idx];
const split_box = Widget.Box.from(split.widget.plane.*);
const widget_box = tab.widget.box();
const dragging = if (tab.widget.dynamic_cast(Tab.ButtonType)) |btn| if (btn.drag_pos) |_| true else false else false;
if (dragging) return .{ false, split_box };
if (split_box.y + split_box.h < widget_box.y + widget_box.h or
split_box.x + split_box.w < widget_box.x + widget_box.w)
return .{ true, split_box };
return .{ false, split_box };
}
fn render_clipping_indicator(self: *@This(), box: Widget.Box, theme: *const Widget.Theme) void {
self.plane.set_style(.{
.fg = self.tab_style.bar_fg.from_theme(theme),
.bg = self.tab_style.bar_bg.from_theme(theme),
});
self.plane.cursor_move_yx(0, @intCast(box.x + box.w -| 1));
self.plane.putchar(self.tab_style.clipping_indicator);
}
pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
var buffer_ref: Buffer.Ref = undefined;
if (try m.match(.{"next_tab"})) {
@ -236,7 +267,7 @@ pub const TabBar = struct {
fn handle_event_drop_target(self: *Self, dragging: usize) tp.result {
var hover_view: ?usize = null;
for (self.widget_list.widgets.items, 0..) |*split_widgetstate, idx|
for (self.splits_list.widgets.items, 0..) |*split_widgetstate, idx|
if (split_widgetstate.widget.dynamic_cast(WidgetList)) |split| {
for (split.widgets.items) |*widgetstate|
if (widgetstate.widget.dynamic_cast(drop_target.ButtonType)) |btn| {
@ -251,20 +282,25 @@ pub const TabBar = struct {
}
pub fn handle_resize(self: *Self, pos: Widget.Box) void {
self.widget_list_widget.resize(pos);
self.plane = self.widget_list.plane;
self.splits_list_widget.resize(pos);
self.plane = self.splits_list.plane;
}
pub fn get(self: *const Self, name: []const u8) ?*const Widget {
return self.widget_list_widget.get(name);
return self.splits_list_widget.get(name);
}
pub fn walk(self: *Self, ctx: *anyopaque, f: Widget.WalkFn, _: *Widget) bool {
return self.widget_list_widget.walk(ctx, f);
pub fn walk(self: *Self, ctx: *anyopaque, f: Widget.WalkFn, self_w: *Widget) bool {
for (self.tabs) |*tab| {
const clipped, _ = self.is_tab_clipped(tab);
if (!clipped)
if (tab.widget.walk(ctx, f)) return true;
}
return f(ctx, self_w);
}
pub fn hover(self: *Self) bool {
return self.widget_list_widget.hover();
return self.splits_list_widget.hover();
}
fn update_tabs(self: *Self, drag_source: ?*Widget) !void {
@ -273,7 +309,7 @@ pub const TabBar = struct {
if (tab.widget.dynamic_cast(Tab.ButtonType)) |btn|
if (btn.drag_pos) |_| break true;
} else false;
if (!dragging and !buffers_changed and self.widget_list.widgets.items.len > 0) return;
if (!dragging and !buffers_changed and self.splits_list.widgets.items.len > 0) return;
try self.update_tab_widgets(drag_source);
}
@ -282,19 +318,19 @@ pub const TabBar = struct {
const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager");
var prev_widget_count: usize = 0;
for (self.widget_list.widgets.items) |*split_widgetstate| if (split_widgetstate.widget.dynamic_cast(WidgetList)) |split| {
for (self.splits_list.widgets.items) |*split_widgetstate| if (split_widgetstate.widget.dynamic_cast(WidgetList)) |split| {
prev_widget_count += 1;
for (split.widgets.items) |_| prev_widget_count += 1;
};
for (self.widget_list.widgets.items) |*split_widget| if (split_widget.widget.dynamic_cast(WidgetList)) |split| {
for (self.splits_list.widgets.items) |*split_widget| if (split_widget.widget.dynamic_cast(WidgetList)) |split| {
for (split.widgets.items) |*widget|
if (&widget.widget == drag_source) tui.reset_drag_context();
};
while (self.widget_list.pop()) |split_widget| if (split_widget.dynamic_cast(WidgetList)) |split| {
while (self.splits_list.pop()) |split_widget| if (split_widget.dynamic_cast(WidgetList)) |split| {
while (split.pop()) |widget| if (widget.dynamic_cast(Tab.ButtonType) == null)
widget.deinit(self.widget_list.allocator);
split.deinit(self.widget_list.allocator);
widget.deinit(self.splits_list.allocator);
split.deinit(self.splits_list.allocator);
};
for (self.tabs) |*tab| if (buffer_manager.buffer_from_ref(tab.buffer_ref)) |buffer| {
@ -306,8 +342,8 @@ pub const TabBar = struct {
var widget_count: usize = 0;
for (0..views) |view| {
var first = true;
var view_widget_list = try WidgetList.createH(self.allocator, self.widget_list.plane, "split", .dynamic);
try self.widget_list.add(view_widget_list.widget());
var view_widget_list = try WidgetList.createH(self.allocator, self.splits_list.plane, "split", .dynamic);
try self.splits_list.add(view_widget_list.widget());
widget_count += 1;
for (self.tabs) |tab| {
const tab_view = tab.view orelse 0;
@ -413,7 +449,7 @@ pub const TabBar = struct {
}
fn find_first_tab_buffer(self: *Self) ?Buffer.Ref {
for (self.widget_list.widgets.items) |*split_widget| if (split_widget.widget.dynamic_cast(WidgetList)) |split|
for (self.splits_list.widgets.items) |*split_widget| if (split_widget.widget.dynamic_cast(WidgetList)) |split|
for (split.widgets.items) |*widget_state| if (widget_state.widget.dynamic_cast(Tab.ButtonType)) |btn|
return btn.opts.ctx.buffer_ref;
return null;
@ -421,7 +457,7 @@ pub const TabBar = struct {
fn find_last_tab_buffer(self: *Self) ?Buffer.Ref {
var last: ?Buffer.Ref = null;
for (self.widget_list.widgets.items) |*split_widget| if (split_widget.widget.dynamic_cast(WidgetList)) |split|
for (self.splits_list.widgets.items) |*split_widget| if (split_widget.widget.dynamic_cast(WidgetList)) |split|
for (split.widgets.items) |*widget_state| if (widget_state.widget.dynamic_cast(Tab.ButtonType)) |btn| {
last = btn.opts.ctx.buffer_ref;
};
@ -430,7 +466,7 @@ pub const TabBar = struct {
fn find_next_tab_buffer(self: *Self) struct { ?Buffer.Ref, usize } {
var found_active: bool = false;
for (self.widget_list.widgets.items) |*split_widget| if (split_widget.widget.dynamic_cast(WidgetList)) |split|
for (self.splits_list.widgets.items) |*split_widget| if (split_widget.widget.dynamic_cast(WidgetList)) |split|
for (split.widgets.items) |*widget_state| if (widget_state.widget.dynamic_cast(Tab.ButtonType)) |btn| {
if (found_active)
return .{ btn.opts.ctx.buffer_ref, btn.opts.ctx.view };
@ -443,7 +479,7 @@ pub const TabBar = struct {
fn find_previous_tab_buffer(self: *Self) struct { ?Buffer.Ref, usize } {
var previous: ?Buffer.Ref = null;
var previous_view: usize = 0;
for (self.widget_list.widgets.items) |*split_widget| if (split_widget.widget.dynamic_cast(WidgetList)) |split|
for (self.splits_list.widgets.items) |*split_widget| if (split_widget.widget.dynamic_cast(WidgetList)) |split|
for (split.widgets.items) |*widget_state| if (widget_state.widget.dynamic_cast(Tab.ButtonType)) |btn| {
if (btn.opts.ctx.buffer_ref == self.active_focused_buffer_ref)
return .{ previous, previous_view };
@ -663,7 +699,7 @@ const Tab = struct {
) !Widget {
const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager");
const buffer = buffer_manager.buffer_from_ref(buffer_ref);
return Button.create_widget(Tab, tabbar.allocator, tabbar.widget_list.plane, .{
return Button.create_widget(Tab, tabbar.allocator, tabbar.splits_list.plane, .{
.ctx = .{ .tabbar = tabbar, .buffer_ref = buffer_ref, .view = if (buffer) |buf| buf.get_last_view() orelse 0 else 0, .tab_style = tab_style },
.label = if (buffer) |buf| name_from_buffer(buf) else "???",
.on_click = Tab.on_click,
@ -1026,7 +1062,7 @@ const drop_target = struct {
tabbar: *TabBar,
view: usize,
) !Widget {
return Button.create_widget(@This(), tabbar.allocator, tabbar.widget_list.plane, .{
return Button.create_widget(@This(), tabbar.allocator, tabbar.splits_list.plane, .{
.ctx = .{ .tabbar = tabbar, .view = view },
.label = &.{},
.on_layout = @This().layout,