feat(buffers): move buffer lifetime management to new Buffer.Manager module
This commit is contained in:
		
							parent
							
								
									fbeaefe7ff
								
							
						
					
					
						commit
						aa1e0674cc
					
				
					 6 changed files with 109 additions and 48 deletions
				
			
		| 
						 | 
				
			
			@ -10,6 +10,7 @@ const max_imbalance = 7;
 | 
			
		|||
pub const Root = *const Node;
 | 
			
		||||
pub const unicode = @import("unicode.zig");
 | 
			
		||||
 | 
			
		||||
pub const Manager = @import("Manager.zig");
 | 
			
		||||
pub const Cursor = @import("Cursor.zig");
 | 
			
		||||
pub const View = @import("View.zig");
 | 
			
		||||
pub const Selection = @import("Selection.zig");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										79
									
								
								src/buffer/Manager.zig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/buffer/Manager.zig
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,79 @@
 | 
			
		|||
const std = @import("std");
 | 
			
		||||
const Buffer = @import("Buffer.zig");
 | 
			
		||||
 | 
			
		||||
const Self = @This();
 | 
			
		||||
 | 
			
		||||
allocator: std.mem.Allocator,
 | 
			
		||||
buffers: std.StringHashMapUnmanaged(*Buffer),
 | 
			
		||||
 | 
			
		||||
pub fn init(allocator: std.mem.Allocator) Self {
 | 
			
		||||
    return .{
 | 
			
		||||
        .allocator = allocator,
 | 
			
		||||
        .buffers = .{},
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn deinit(self: *Self) void {
 | 
			
		||||
    var i = self.buffers.iterator();
 | 
			
		||||
    while (i.next()) |p| {
 | 
			
		||||
        self.allocator.free(p.key_ptr.*);
 | 
			
		||||
        p.value_ptr.*.deinit();
 | 
			
		||||
    }
 | 
			
		||||
    self.buffers.deinit(self.allocator);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn open_file(self: *Self, file_path: []const u8) Buffer.LoadFromFileError!*Buffer {
 | 
			
		||||
    if (self.buffers.get(file_path)) |buffer| {
 | 
			
		||||
        return buffer;
 | 
			
		||||
    } else {
 | 
			
		||||
        var buffer = try Buffer.create(self.allocator);
 | 
			
		||||
        errdefer buffer.deinit();
 | 
			
		||||
        try buffer.load_from_file_and_update(file_path);
 | 
			
		||||
        try self.buffers.put(self.allocator, try self.allocator.dupe(u8, file_path), buffer);
 | 
			
		||||
        return buffer;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn open_scratch(self: *Self, file_path: []const u8, content: []const u8) Buffer.LoadFromStringError!*Buffer {
 | 
			
		||||
    if (self.buffers.get(file_path)) |buffer| {
 | 
			
		||||
        return buffer;
 | 
			
		||||
    } else {
 | 
			
		||||
        var buffer = try Buffer.create(self.allocator);
 | 
			
		||||
        errdefer buffer.deinit();
 | 
			
		||||
        try buffer.load_from_string_and_update(file_path, content);
 | 
			
		||||
        buffer.file_exists = true;
 | 
			
		||||
        try self.buffers.put(self.allocator, try self.allocator.dupe(u8, file_path), buffer);
 | 
			
		||||
        return buffer;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn retire(self: *Self, buffer: *Buffer) void {
 | 
			
		||||
    _ = self;
 | 
			
		||||
    _ = buffer;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn list(self: *Self, allocator: std.mem.Allocator) []*const Buffer {
 | 
			
		||||
    _ = self;
 | 
			
		||||
    _ = allocator;
 | 
			
		||||
    unreachable;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn is_dirty(self: *const Self) bool {
 | 
			
		||||
    var i = self.buffers.iterator();
 | 
			
		||||
    while (i.next()) |kv|
 | 
			
		||||
        if (kv.value_ptr.*.is_dirty())
 | 
			
		||||
            return true;
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn is_buffer_dirty(self: *const Self, file_path: []const u8) bool {
 | 
			
		||||
    return if (self.buffers.get(file_path)) |buffer| buffer.is_dirty() else false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn save_all(self: *const Self) Buffer.StoreToFileError!void {
 | 
			
		||||
    var i = self.buffers.iterator();
 | 
			
		||||
    while (i.next()) |kv| {
 | 
			
		||||
        const buffer = kv.value_ptr.*;
 | 
			
		||||
        try buffer.store_to_file_and_clean(buffer.file_path);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -218,6 +218,7 @@ pub const Editor = struct {
 | 
			
		|||
 | 
			
		||||
    file_path: ?[]const u8,
 | 
			
		||||
    buffer: ?*Buffer,
 | 
			
		||||
    buffer_manager: *Buffer.Manager,
 | 
			
		||||
    lsp_version: usize = 1,
 | 
			
		||||
    pause_undo: bool = false,
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -358,7 +359,7 @@ pub const Editor = struct {
 | 
			
		|||
        self.clamp();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn init(self: *Self, allocator: Allocator, n: Plane) void {
 | 
			
		||||
    fn init(self: *Self, allocator: Allocator, n: Plane, buffer_manager: *Buffer.Manager) void {
 | 
			
		||||
        const logger = log.logger("editor");
 | 
			
		||||
        const frame_rate = tp.env.get().num("frame-rate");
 | 
			
		||||
        const indent_size = tui.current().config.indent_size;
 | 
			
		||||
| 
						 | 
				
			
			@ -372,6 +373,7 @@ pub const Editor = struct {
 | 
			
		|||
            .logger = logger,
 | 
			
		||||
            .file_path = null,
 | 
			
		||||
            .buffer = null,
 | 
			
		||||
            .buffer_manager = buffer_manager,
 | 
			
		||||
            .handlers = EventHandler.List.init(allocator),
 | 
			
		||||
            .animation_lag = get_animation_max_lag(),
 | 
			
		||||
            .animation_frame_rate = frame_rate,
 | 
			
		||||
| 
						 | 
				
			
			@ -393,7 +395,7 @@ pub const Editor = struct {
 | 
			
		|||
        self.matches.deinit();
 | 
			
		||||
        self.handlers.deinit();
 | 
			
		||||
        self.logger.deinit();
 | 
			
		||||
        if (self.buffer) |p| p.deinit();
 | 
			
		||||
        if (self.buffer) |p| self.buffer_manager.retire(p);
 | 
			
		||||
        if (self.case_data) |cd| cd.deinit();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -459,28 +461,16 @@ pub const Editor = struct {
 | 
			
		|||
        self.view.cols = pos.w;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn is_dirty(self: *Self) bool {
 | 
			
		||||
        const b = self.buffer orelse return false;
 | 
			
		||||
        return b.is_dirty();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn open(self: *Self, file_path: []const u8) !void {
 | 
			
		||||
        var new_buf = try Buffer.create(self.allocator);
 | 
			
		||||
        errdefer new_buf.deinit();
 | 
			
		||||
        try new_buf.load_from_file_and_update(file_path);
 | 
			
		||||
        return self.open_buffer(file_path, new_buf);
 | 
			
		||||
        return self.open_buffer(file_path, try self.buffer_manager.open_file(file_path));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn open_scratch(self: *Self, file_path: []const u8, content: []const u8) !void {
 | 
			
		||||
        var new_buf = try Buffer.create(self.allocator);
 | 
			
		||||
        errdefer new_buf.deinit();
 | 
			
		||||
        try new_buf.load_from_string_and_update(file_path, content);
 | 
			
		||||
        new_buf.file_exists = true;
 | 
			
		||||
        return self.open_buffer(file_path, new_buf);
 | 
			
		||||
        return self.open_buffer(file_path, try self.buffer_manager.open_scratch(file_path, content));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn open_buffer(self: *Self, file_path: []const u8, new_buf: *Buffer) !void {
 | 
			
		||||
        errdefer new_buf.deinit();
 | 
			
		||||
        errdefer self.buffer_manager.retire(new_buf);
 | 
			
		||||
        self.cancel_all_selections();
 | 
			
		||||
        self.get_primary().reset();
 | 
			
		||||
        self.file_path = try self.allocator.dupe(u8, file_path);
 | 
			
		||||
| 
						 | 
				
			
			@ -519,17 +509,7 @@ pub const Editor = struct {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    fn close(self: *Self) !void {
 | 
			
		||||
        return self.close_internal(false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn close_dirty(self: *Self) !void {
 | 
			
		||||
        return self.close_internal(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn close_internal(self: *Self, allow_dirty_close: bool) !void {
 | 
			
		||||
        const b = self.buffer orelse return error.Stop;
 | 
			
		||||
        if (!allow_dirty_close and b.is_dirty()) return tp.exit("unsaved changes");
 | 
			
		||||
        if (self.buffer) |b_mut| b_mut.deinit();
 | 
			
		||||
        if (self.buffer) |b_mut| self.buffer_manager.retire(b_mut);
 | 
			
		||||
        self.buffer = null;
 | 
			
		||||
        self.plane.erase();
 | 
			
		||||
        self.plane.home();
 | 
			
		||||
| 
						 | 
				
			
			@ -3770,7 +3750,8 @@ pub const Editor = struct {
 | 
			
		|||
 | 
			
		||||
    pub fn close_file_without_saving(self: *Self, _: Context) Result {
 | 
			
		||||
        self.cancel_all_selections();
 | 
			
		||||
        try self.close_dirty();
 | 
			
		||||
        if (self.buffer) |buffer| buffer.reset_to_last_saved();
 | 
			
		||||
        try self.close();
 | 
			
		||||
    }
 | 
			
		||||
    pub const close_file_without_saving_meta = .{ .description = "Close file without saving" };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -4759,8 +4740,8 @@ pub const Editor = struct {
 | 
			
		|||
    pub const set_file_type_meta = .{ .arguments = &.{.string} };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub fn create(allocator: Allocator, parent: Widget) !Widget {
 | 
			
		||||
    return EditorWidget.create(allocator, parent);
 | 
			
		||||
pub fn create(allocator: Allocator, parent: Widget, buffer_manager: *Buffer.Manager) !Widget {
 | 
			
		||||
    return EditorWidget.create(allocator, parent, buffer_manager);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub const EditorWidget = struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -4782,10 +4763,10 @@ pub const EditorWidget = struct {
 | 
			
		|||
    const Self = @This();
 | 
			
		||||
    const Commands = command.Collection(Editor);
 | 
			
		||||
 | 
			
		||||
    fn create(allocator: Allocator, parent: Widget) !Widget {
 | 
			
		||||
    fn create(allocator: Allocator, parent: Widget, buffer_manager: *Buffer.Manager) !Widget {
 | 
			
		||||
        const container = try WidgetList.createH(allocator, parent, "editor.container", .dynamic);
 | 
			
		||||
        const self: *Self = try allocator.create(Self);
 | 
			
		||||
        try self.init(allocator, container.widget());
 | 
			
		||||
        try self.init(allocator, container.widget(), buffer_manager);
 | 
			
		||||
        try self.commands.init(&self.editor);
 | 
			
		||||
        const editorWidget = Widget.to(self);
 | 
			
		||||
        try container.add(try editor_gutter.create(allocator, container.widget(), editorWidget, &self.editor));
 | 
			
		||||
| 
						 | 
				
			
			@ -4794,7 +4775,7 @@ pub const EditorWidget = struct {
 | 
			
		|||
        return container.widget();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn init(self: *Self, allocator: Allocator, parent: Widget) !void {
 | 
			
		||||
    fn init(self: *Self, allocator: Allocator, parent: Widget, buffer_manager: *Buffer.Manager) !void {
 | 
			
		||||
        var n = try Plane.init(&(Widget.Box{}).opts("editor"), parent.plane.*);
 | 
			
		||||
        errdefer n.deinit();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -4803,7 +4784,7 @@ pub const EditorWidget = struct {
 | 
			
		|||
            .plane = n,
 | 
			
		||||
            .editor = undefined,
 | 
			
		||||
        };
 | 
			
		||||
        self.editor.init(allocator, n);
 | 
			
		||||
        self.editor.init(allocator, n, buffer_manager);
 | 
			
		||||
        errdefer self.editor.deinit();
 | 
			
		||||
        try self.editor.push_cursor();
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,6 +14,7 @@ const build_options = @import("build_options");
 | 
			
		|||
const Plane = @import("renderer").Plane;
 | 
			
		||||
const input = @import("input");
 | 
			
		||||
const command = @import("command");
 | 
			
		||||
const BufferManager = @import("Buffer").Manager;
 | 
			
		||||
 | 
			
		||||
const tui = @import("tui.zig");
 | 
			
		||||
const Box = @import("Box.zig");
 | 
			
		||||
| 
						 | 
				
			
			@ -47,6 +48,7 @@ active_view: ?usize = 0,
 | 
			
		|||
panels: ?*WidgetList = null,
 | 
			
		||||
last_match_text: ?[]const u8 = null,
 | 
			
		||||
location_history: location_history,
 | 
			
		||||
buffer_manager: BufferManager,
 | 
			
		||||
file_stack: std.ArrayList([]const u8),
 | 
			
		||||
find_in_files_state: enum { init, adding, done } = .done,
 | 
			
		||||
file_list_type: FileListType = .find_in_files,
 | 
			
		||||
| 
						 | 
				
			
			@ -70,6 +72,7 @@ pub fn create(allocator: std.mem.Allocator) !Widget {
 | 
			
		|||
        .file_stack = std.ArrayList([]const u8).init(allocator),
 | 
			
		||||
        .views = undefined,
 | 
			
		||||
        .views_widget = undefined,
 | 
			
		||||
        .buffer_manager = BufferManager.init(allocator),
 | 
			
		||||
    };
 | 
			
		||||
    try self.commands.init(self);
 | 
			
		||||
    const w = Widget.to(self);
 | 
			
		||||
| 
						 | 
				
			
			@ -104,6 +107,7 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
 | 
			
		|||
    self.commands.deinit();
 | 
			
		||||
    self.widgets.deinit(allocator);
 | 
			
		||||
    self.floating_views.deinit();
 | 
			
		||||
    self.buffer_manager.deinit();
 | 
			
		||||
    allocator.destroy(self);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -237,14 +241,7 @@ fn toggle_view(self: *Self, view: anytype) !void {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
fn check_all_not_dirty(self: *const Self) command.Result {
 | 
			
		||||
    for (self.editors.items) |editor|
 | 
			
		||||
        if (editor.is_dirty())
 | 
			
		||||
            return tp.exit("unsaved changes");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn check_active_not_dirty(self: *const Self) command.Result {
 | 
			
		||||
    if (self.active_editor) |idx|
 | 
			
		||||
        if (self.editors.items[idx].is_dirty())
 | 
			
		||||
    if (self.buffer_manager.is_dirty())
 | 
			
		||||
        return tp.exit("unsaved changes");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -296,6 +293,8 @@ const cmds = struct {
 | 
			
		|||
        self.clear_find_in_files_results(.diagnostics);
 | 
			
		||||
        if (self.file_list_type == .diagnostics and self.is_panel_view_showing(filelist_view))
 | 
			
		||||
            try self.toggle_panel_view(filelist_view, false);
 | 
			
		||||
        self.buffer_manager.deinit();
 | 
			
		||||
        self.buffer_manager = BufferManager.init(self.allocator);
 | 
			
		||||
        try project_manager.open(project_dir);
 | 
			
		||||
        const project = tp.env.get().str("project");
 | 
			
		||||
        tui.current().rdr.set_terminal_working_directory(project);
 | 
			
		||||
| 
						 | 
				
			
			@ -352,7 +351,6 @@ const cmds = struct {
 | 
			
		|||
 | 
			
		||||
        if (!same_file) {
 | 
			
		||||
            if (self.get_active_editor()) |editor| {
 | 
			
		||||
                try self.check_active_not_dirty();
 | 
			
		||||
                editor.send_editor_jump_source() catch {};
 | 
			
		||||
            }
 | 
			
		||||
            try self.create_editor();
 | 
			
		||||
| 
						 | 
				
			
			@ -395,7 +393,6 @@ const cmds = struct {
 | 
			
		|||
    pub const open_gui_config_meta = .{ .description = "Edit gui configuration file" };
 | 
			
		||||
 | 
			
		||||
    pub fn create_scratch_buffer(self: *Self, ctx: Ctx) Result {
 | 
			
		||||
        try self.check_all_not_dirty();
 | 
			
		||||
        tui.reset_drag_context();
 | 
			
		||||
        try self.create_editor();
 | 
			
		||||
        try command.executeName("open_scratch_buffer", ctx);
 | 
			
		||||
| 
						 | 
				
			
			@ -863,7 +860,7 @@ fn create_editor(self: *Self) !void {
 | 
			
		|||
    if (self.get_active_file_path()) |file_path| self.push_file_stack(file_path) catch {};
 | 
			
		||||
    try self.delete_active_view();
 | 
			
		||||
    command.executeName("enter_mode_default", .{}) catch {};
 | 
			
		||||
    var editor_widget = try ed.create(self.allocator, Widget.to(self));
 | 
			
		||||
    var editor_widget = try ed.create(self.allocator, Widget.to(self), &self.buffer_manager);
 | 
			
		||||
    errdefer editor_widget.deinit(self.allocator);
 | 
			
		||||
    const editor = editor_widget.get("editor") orelse @panic("mainview editor not found");
 | 
			
		||||
    if (self.top_bar) |bar| editor.subscribe(EventHandler.to_unowned(bar)) catch @panic("subscribe unsupported");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,6 @@ pub const create = Type.create;
 | 
			
		|||
 | 
			
		||||
pub fn load_entries(self: *Type) error{ Exit, OutOfMemory }!void {
 | 
			
		||||
    const editor = tui.get_active_editor() orelse return;
 | 
			
		||||
    if (editor.is_dirty()) return tp.exit("unsaved changes");
 | 
			
		||||
    if (editor.file_path) |old_path|
 | 
			
		||||
        if (std.mem.lastIndexOf(u8, old_path, "/")) |pos|
 | 
			
		||||
            try self.file_path.appendSlice(old_path[0 .. pos + 1]);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -991,6 +991,10 @@ pub fn get_active_selection(allocator: std.mem.Allocator) ?[]u8 {
 | 
			
		|||
    return editor.get_selection(sel, allocator) catch null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn get_buffer_manager() ?*@import("Buffer").Manager {
 | 
			
		||||
    return if (current().mainview.dynamic_cast(mainview)) |mv_| &mv_.buffer_manager else null;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn context_check() void {
 | 
			
		||||
    if (instance_ == null) @panic("tui call out of context");
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue