Compare commits
4 commits
86856f6631
...
0df97f5ad5
| Author | SHA1 | Date | |
|---|---|---|---|
| 0df97f5ad5 | |||
| 414668c4cd | |||
| ac12252ce1 | |||
| 645e0d4e3b |
4 changed files with 88 additions and 36 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 } {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue