diff --git a/build.zig b/build.zig index 1656e8a..b135612 100644 --- a/build.zig +++ b/build.zig @@ -595,7 +595,6 @@ fn gen_version_info( var code: u8 = 0; const describe = try b.runAllowFail(&[_][]const u8{ "git", "describe", "--always", "--tags" }, &code, .Ignore); - const date_ = try b.runAllowFail(&[_][]const u8{ "git", "show", "-s", "--format=%ci", "HEAD" }, &code, .Ignore); const branch_ = try b.runAllowFail(&[_][]const u8{ "git", "rev-parse", "--abbrev-ref", "HEAD" }, &code, .Ignore); const branch = std.mem.trimRight(u8, branch_, "\r\n "); const tracking_branch_ = blk: { @@ -616,7 +615,6 @@ fn gen_version_info( const log_ = b.runAllowFail(&[_][]const u8{ "git", "log", "--pretty=oneline", "@{u}..." }, &code, .Ignore) catch ""; const diff_ = b.runAllowFail(&[_][]const u8{ "git", "diff", "--stat", "--patch", "HEAD" }, &code, .Ignore) catch "(git diff failed)"; const version = std.mem.trimRight(u8, describe, "\r\n "); - const date = std.mem.trimRight(u8, date_, "\r\n "); const tracking_branch = std.mem.trimRight(u8, tracking_branch_, "\r\n "); const tracking_remote = std.mem.trimRight(u8, tracking_remote_, "\r\n "); const remote = std.mem.trimRight(u8, remote_, "\r\n "); @@ -624,10 +622,9 @@ fn gen_version_info( const diff = std.mem.trimRight(u8, diff_, "\r\n "); const target_triple = try target.result.zigTriple(b.allocator); - try writer.print("Flow Control: a programmer's text editor\n\nversion: {s}{s}\ncommitted: {s}\ntarget: {s}\n", .{ + try writer.print("Flow Control: a programmer's text editor\n\nversion: {s}{s}\ntarget: {s}\n", .{ version, if (diff.len > 0) "-dirty" else "", - date, target_triple, }); diff --git a/src/buffer/Buffer.zig b/src/buffer/Buffer.zig index cb1de28..f4aa39a 100644 --- a/src/buffer/Buffer.zig +++ b/src/buffer/Buffer.zig @@ -524,7 +524,7 @@ const Node = union(enum) { if (!found) return error.NotFound; } - pub fn egc_at(self: *const Node, line: usize, col: usize, metrics: Metrics) error{NotFound}!struct { []const u8, usize, usize } { + pub fn ecg_at(self: *const Node, line: usize, col: usize, metrics: Metrics) error{NotFound}!struct { []const u8, usize, usize } { const ctx_ = struct { col: usize, at: ?[]const u8 = null, @@ -545,8 +545,8 @@ const Node = union(enum) { } pub fn test_at(self: *const Node, pred: *const fn (c: []const u8) bool, line: usize, col: usize, metrics: Metrics) bool { - const egc, _, _ = self.egc_at(line, col, metrics) catch return false; - return pred(egc); + const ecg, _, _ = self.ecg_at(line, col, metrics) catch return false; + return pred(ecg); } pub fn get_line_width_map(self: *const Node, line: usize, map: *ArrayList(usize), metrics: Metrics) error{ Stop, NoSpaceLeft }!void { diff --git a/src/buffer/Cursor.zig b/src/buffer/Cursor.zig index 23e562b..43df3d2 100644 --- a/src/buffer/Cursor.zig +++ b/src/buffer/Cursor.zig @@ -40,7 +40,7 @@ fn move_right_no_target(self: *Self, root: Buffer.Root, metrics: Metrics) !void const lines = root.lines(); if (lines <= self.row) return error.Stop; if (self.col < root.line_width(self.row, metrics) catch 0) { - _, const wcwidth, const offset = root.egc_at(self.row, self.col, metrics) catch return error.Stop; + _, const wcwidth, const offset = root.ecg_at(self.row, self.col, metrics) catch return error.Stop; self.col += wcwidth - offset; } else if (self.row < lines - 1) { self.col = 0; @@ -59,7 +59,7 @@ fn move_left_no_target(self: *Self, root: Buffer.Root, metrics: Metrics) !void { self.row -= 1; self.col = root.line_width(self.row, metrics) catch 0; } else { - _, const wcwidth, _ = root.egc_at(self.row, self.col - 1, metrics) catch return error.Stop; + _, const wcwidth, _ = root.ecg_at(self.row, self.col - 1, metrics) catch return error.Stop; if (self.col > wcwidth) self.col -= wcwidth else self.col = 0; } } @@ -176,10 +176,6 @@ fn is_at_end(self: *const Self, root: Buffer.Root, metrics: Metrics) bool { return if (self.row < root.lines()) self.col == root.line_width(self.row, metrics) catch 0 else true; } -pub fn egc_at(self: *const Self, root: Buffer.Root, metrics: Metrics) error{NotFound}!struct { []const u8, usize, usize } { - return root.egc_at(self.row, self.col, metrics); -} - pub fn test_at(self: *const Self, root: Buffer.Root, pred: *const fn (c: []const u8) bool, metrics: Metrics) bool { return root.test_at(pred, self.row, self.col, metrics); } diff --git a/src/buffer/unicode.zig b/src/buffer/unicode.zig index a9a3848..60bf389 100644 --- a/src/buffer/unicode.zig +++ b/src/buffer/unicode.zig @@ -38,25 +38,6 @@ pub fn control_code_to_unicode(code: u8) [:0]const u8 { }; } -pub const char_pairs = [_]struct { []const u8, []const u8 }{ - .{ "\"", "\"" }, - .{ "'", "'" }, - .{ "(", ")" }, - .{ "(", ")" }, - .{ "[", "]" }, - .{ "[", "]" }, - .{ "{", "}" }, - .{ "{", "}" }, - .{ "‘", "’" }, - .{ "‘", "’" }, - .{ "“", "”" }, - .{ "“", "”" }, - .{ "‚", "‘" }, - .{ "‚", "‘" }, - .{ "«", "»" }, - .{ "«", "»" }, -}; - fn raw_byte_to_utf8(cp: u8, buf: []u8) ![]const u8 { var utf16le: [1]u16 = undefined; const utf16le_as_bytes = std.mem.sliceAsBytes(utf16le[0..]); diff --git a/src/file_link.zig b/src/file_link.zig deleted file mode 100644 index aac4cfa..0000000 --- a/src/file_link.zig +++ /dev/null @@ -1,65 +0,0 @@ -const std = @import("std"); -const tp = @import("thespian"); -const root = @import("root"); - -pub const Dest = union(enum) { - file: FileDest, - dir: DirDest, -}; - -pub const FileDest = struct { - path: []const u8, - line: ?usize = null, - column: ?usize = null, - end_column: ?usize = null, - exists: bool = false, -}; - -pub const DirDest = struct { - path: []const u8, -}; - -pub fn parse(link: []const u8) error{InvalidFileLink}!Dest { - if (link.len == 0) return error.InvalidFileLink; - var it = std.mem.splitScalar(u8, link, ':'); - var dest: Dest = if (root.is_directory(link)) - .{ .dir = .{ .path = link } } - else - .{ .file = .{ .path = it.first() } }; - switch (dest) { - .file => |*file| { - if (it.next()) |line_| - file.line = std.fmt.parseInt(usize, line_, 10) catch blk: { - file.path = link; - break :blk null; - }; - if (file.line) |_| if (it.next()) |col_| { - file.column = std.fmt.parseInt(usize, col_, 10) catch null; - }; - if (file.column) |_| if (it.next()) |col_| { - file.end_column = std.fmt.parseInt(usize, col_, 10) catch null; - }; - file.exists = root.is_file(file.path); - }, - .dir => {}, - } - return dest; -} - -pub fn navigate(to: tp.pid_ref, link: *const Dest) anyerror!void { - switch (link.*) { - .file => |file| { - if (file.line) |l| { - if (file.column) |col| { - try to.send(.{ "cmd", "navigate", .{ .file = file.path, .line = l, .column = col } }); - if (file.end_column) |end| - try to.send(.{ "A", l, col - 1, end - 1 }); - return; - } - return to.send(.{ "cmd", "navigate", .{ .file = file.path, .line = l } }); - } - return to.send(.{ "cmd", "navigate", .{ .file = file.path } }); - }, - else => {}, - } -} diff --git a/src/keybind/builtin/flow.json b/src/keybind/builtin/flow.json index 35cdc55..e109961 100644 --- a/src/keybind/builtin/flow.json +++ b/src/keybind/builtin/flow.json @@ -149,7 +149,7 @@ ["escape", "cancel"], ["enter", "smart_insert_line"], ["delete", "delete_forward"], - ["backspace", "smart_delete_backward"], + ["backspace", "delete_backward"], ["left", "move_left"], ["right", "move_right"], ["up", "move_up"], @@ -164,19 +164,12 @@ ["\"", "smart_insert_pair", "\"", "\""], ["'", "smart_insert_pair", "'", "'"], ["(", "smart_insert_pair", "(", ")"], - [")", "smart_insert_pair_close", "(", ")"], ["[", "smart_insert_pair", "[", "]"], - ["]", "smart_insert_pair_close", "[", "]"], ["{", "smart_insert_pair", "{", "}"], - ["}", "smart_insert_pair_close", "{", "}"], ["‘", "smart_insert_pair", "‘", "’"], - ["’", "smart_insert_pair_close", "‘", "’"], ["“", "smart_insert_pair", "“", "”"], - ["”", "smart_insert_pair_close", "“", "”"], ["‚", "smart_insert_pair", "‚", "‘"], - ["‘", "smart_insert_pair_close", "‚", "‘"], ["«", "smart_insert_pair", "«", "»"], - ["»", "smart_insert_pair_close", "«", "»"], ["left_control", "enable_jump_mode"], ["right_control", "enable_jump_mode"], diff --git a/src/main.zig b/src/main.zig index 1d5a1dc..1afdc47 100644 --- a/src/main.zig +++ b/src/main.zig @@ -6,7 +6,6 @@ const builtin = @import("builtin"); const bin_path = @import("bin_path.zig"); const list_languages = @import("list_languages.zig"); -pub const file_link = @import("file_link.zig"); const c = @cImport({ @cInclude("locale.h"); @@ -235,60 +234,92 @@ pub fn main() anyerror!void { const tui_proc = try tui.spawn(a, &ctx, &eh, &env); defer tui_proc.deinit(); - var links = std.ArrayList(file_link.Dest).init(a); - defer links.deinit(); - var prev: ?*file_link.Dest = null; + const Dest = struct { + file: []const u8 = "", + line: ?usize = null, + column: ?usize = null, + end_column: ?usize = null, + }; + var dests = std.ArrayList(Dest).init(a); + defer dests.deinit(); + var prev: ?*Dest = null; var line_next: ?usize = null; for (positional_args.items) |arg| { if (arg.len == 0) continue; if (!args.literal and arg[0] == '+') { const line = try std.fmt.parseInt(usize, arg[1..], 10); - if (prev) |p| switch (p.*) { - .file => |*file| { - file.line = line; - continue; - }, - else => {}, - }; - line_next = line; + if (prev) |p| { + p.line = line; + } else { + line_next = line; + } continue; } - const curr = try links.addOne(); - curr.* = if (!args.literal) try file_link.parse(arg) else .{ .file = .{ .path = arg } }; + const curr = try dests.addOne(); + curr.* = .{}; prev = curr; - if (line_next) |line| { - switch (curr.*) { - .file => |*file| { - file.line = line; - line_next = null; - }, - else => {}, + curr.line = line; + line_next = null; + } + if (!args.literal) { + var it = std.mem.splitScalar(u8, arg, ':'); + curr.file = it.first(); + if (it.next()) |line_| + curr.line = std.fmt.parseInt(usize, line_, 10) catch blk: { + curr.file = arg; + break :blk null; + }; + if (curr.line) |_| { + if (it.next()) |col_| + curr.column = std.fmt.parseInt(usize, col_, 10) catch null; + if (it.next()) |col_| + curr.end_column = std.fmt.parseInt(usize, col_, 10) catch null; } + } else { + curr.file = arg; } } var have_project = false; + var files = std.ArrayList(Dest).init(a); + defer files.deinit(); if (args.project) |project| { try tui_proc.send(.{ "cmd", "open_project_dir", .{project} }); have_project = true; } - for (links.items) |link| switch (link) { - .dir => |dir| { + for (dests.items) |dest| { + if (dest.file.len == 0) continue; + if (is_directory(dest.file)) { if (have_project) { std.debug.print("more than one project directory is not allowed\n", .{}); exit(1); } - try tui_proc.send(.{ "cmd", "open_project_dir", .{dir.path} }); - have_project = true; - }, - else => {}, - }; + try tui_proc.send(.{ "cmd", "open_project_dir", .{dest.file} }); - for (links.items) |link| { - try file_link.navigate(tui_proc.ref(), &link); + have_project = true; + } else { + const curr = try files.addOne(); + curr.* = dest; + } + } + + for (files.items) |dest| { + if (dest.file.len == 0) continue; + + if (dest.line) |l| { + if (dest.column) |col| { + try tui_proc.send(.{ "cmd", "navigate", .{ .file = dest.file, .line = l, .column = col } }); + if (dest.end_column) |end| + try tui_proc.send(.{ "A", l, col - 1, end - 1 }); + } else { + try tui_proc.send(.{ "cmd", "navigate", .{ .file = dest.file, .line = l } }); + } + } else { + try tui_proc.send(.{ "cmd", "navigate", .{ .file = dest.file } }); + } } else { if (!have_project) try tui_proc.send(.{ "cmd", "open_project_cwd" }); @@ -863,14 +894,6 @@ pub fn is_directory(rel_path: []const u8) bool { return true; } -pub fn is_file(rel_path: []const u8) bool { - var path_buf: [std.fs.max_path_bytes]u8 = undefined; - const abs_path = std.fs.cwd().realpath(rel_path, &path_buf) catch return false; - var file = std.fs.openFileAbsolute(abs_path, .{ .mode = .read_only }) catch return false; - defer file.close(); - return true; -} - pub fn shorten_path(buf: []u8, path: []const u8, removed_prefix: *usize, max_len: usize) []const u8 { removed_prefix.* = 0; if (path.len <= max_len) return path; diff --git a/src/project_manager.zig b/src/project_manager.zig index cdf536f..362045e 100644 --- a/src/project_manager.zig +++ b/src/project_manager.zig @@ -45,10 +45,6 @@ fn create() SpawnError!Self { return .{ .pid = tp.env.get().proc(module_name) }; } -pub fn start() SpawnError!void { - _ = try get(); -} - pub fn shutdown() void { const pid = tp.env.get().proc(module_name); if (pid.expired()) { diff --git a/src/tui/editor.zig b/src/tui/editor.zig index cca00a8..ecc1324 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -2774,49 +2774,6 @@ pub const Editor = struct { } pub const delete_backward_meta: Meta = .{ .description = "Delete previous character" }; - pub fn smart_delete_backward(self: *Self, _: Context) Result { - const b = try self.buf_for_update(); - var all_stop = true; - var root = b.root; - - for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { - if (cursel.selection) |_| { - root = self.delete_selection(root, cursel, b.allocator) catch continue; - all_stop = false; - continue; - } - with_selection_const(root, move_cursor_left, cursel, self.metrics) catch continue; - - if (cursel.selection) |*sel| { - const egc_left, _, _ = sel.end.egc_at(root, self.metrics) catch { - root = self.delete_selection(root, cursel, b.allocator) catch continue; - all_stop = false; - continue; - }; - const egc_right, _, _ = sel.begin.egc_at(root, self.metrics) catch { - root = self.delete_selection(root, cursel, b.allocator) catch continue; - all_stop = false; - continue; - }; - - for (Buffer.unicode.char_pairs) |pair| if (std.mem.eql(u8, egc_left, pair[0]) and std.mem.eql(u8, egc_right, pair[1])) { - sel.begin.move_right(root, self.metrics) catch {}; - break; - }; - } - - root = self.delete_selection(root, cursel, b.allocator) catch continue; - all_stop = false; - }; - - if (all_stop) - return error.Stop; - - try self.update_buf(root); - self.clamp(); - } - pub const smart_delete_backward_meta: Meta = .{ .description = "Delete previous character (smart)" }; - pub fn delete_word_left(self: *Self, _: Context) Result { const b = try self.buf_for_update(); const root = try self.delete_to(move_cursor_word_left_space, b.root, b.allocator); @@ -3038,7 +2995,7 @@ pub const Editor = struct { return error.Stop; try move_cursor_left(root, cursor, metrics); while (true) { - const curr_egc, _, _ = root.egc_at(cursor.row, cursor.col, metrics) catch return error.Stop; + const curr_egc, _, _ = root.ecg_at(cursor.row, cursor.col, metrics) catch return error.Stop; if (std.mem.eql(u8, curr_egc, egc)) return; if (is_eol_left(root, cursor, metrics)) @@ -3053,7 +3010,7 @@ pub const Editor = struct { return error.Stop; try move_cursor_right(root, cursor, metrics); while (true) { - const curr_egc, _, _ = root.egc_at(cursor.row, cursor.col, metrics) catch return error.Stop; + const curr_egc, _, _ = root.ecg_at(cursor.row, cursor.col, metrics) catch return error.Stop; if (std.mem.eql(u8, curr_egc, egc)) return; if (is_eol_right(root, cursor, metrics)) @@ -4113,7 +4070,6 @@ pub const Editor = struct { if (!try ctx.args.match(.{ tp.extract(&chars_left), tp.extract(&chars_right) })) return error.InvalidSmartInsertPairArguments; const b = try self.buf_for_update(); - var move: enum { left, right } = .left; var root = b.root; for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { if (cursel.selection) |*sel| { @@ -4122,60 +4078,16 @@ pub const Editor = struct { var end: CurSel = .{ .cursor = sel.end }; root = try self.insert(root, &end, chars_right, b.allocator); sel.end.move_left(root, self.metrics) catch {}; - } else blk: { - const egc, _, _ = cursel.cursor.egc_at(root, self.metrics) catch { - root = try self.insert(root, cursel, chars_left, b.allocator); - root = try self.insert(root, cursel, chars_right, b.allocator); - break :blk; - }; - if (std.mem.eql(u8, egc, chars_left)) { - move = .right; - } else { - root = try self.insert(root, cursel, chars_left, b.allocator); - root = try self.insert(root, cursel, chars_right, b.allocator); - } - } - switch (move) { - .left => cursel.cursor.move_left(root, self.metrics) catch {}, - .right => cursel.cursor.move_right(root, self.metrics) catch {}, + } else { + root = try self.insert(root, cursel, chars_left, b.allocator); + root = try self.insert(root, cursel, chars_right, b.allocator); } + cursel.cursor.move_left(root, self.metrics) catch {}; }; try self.update_buf(root); self.clamp(); } - pub const smart_insert_pair_meta: Meta = .{ .arguments = &.{ .string, .string } }; - - pub fn smart_insert_pair_close(self: *Self, ctx: Context) Result { - var chars_left: []const u8 = undefined; - var chars_right: []const u8 = undefined; - if (!try ctx.args.match(.{ tp.extract(&chars_left), tp.extract(&chars_right) })) - return error.InvalidSmartInsertPairArguments; - const b = try self.buf_for_update(); - var root = b.root; - for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { - if (cursel.selection) |*sel| { - var begin: CurSel = .{ .cursor = sel.begin }; - root = try self.insert(root, &begin, chars_left, b.allocator); - var end: CurSel = .{ .cursor = sel.end }; - root = try self.insert(root, &end, chars_right, b.allocator); - sel.begin.move_left(root, self.metrics) catch {}; - cursel.disable_selection(root, self.metrics); - } else blk: { - const egc, _, _ = cursel.cursor.egc_at(root, self.metrics) catch { - root = try self.insert(root, cursel, chars_right, b.allocator); - break :blk; - }; - if (std.mem.eql(u8, egc, chars_right)) { - cursel.cursor.move_right(root, self.metrics) catch {}; - } else { - root = try self.insert(root, cursel, chars_right, b.allocator); - } - } - }; - try self.update_buf(root); - self.clamp(); - } - pub const smart_insert_pair_close_meta: Meta = .{ .arguments = &.{ .string, .string } }; + pub const smart_insert_pair_meta: Meta = .{ .arguments = &.{.string} }; pub fn enable_fast_scroll(self: *Self, _: Context) Result { self.fast_scroll = true; @@ -4420,12 +4332,12 @@ pub const Editor = struct { pub fn save_file_with_formatting(self: *Self, _: Context) Result { return self.save_file(Context.fmt(.{"format"})); } - pub const save_file_with_formatting_meta: Meta = .{ .description = "Save file with formatting" }; + pub const save_file_with_formatting_meta = .{ .description = "Save file with formatting" }; pub fn save_file_without_formatting(self: *Self, _: Context) Result { return self.save_file(Context.fmt(.{"no_format"})); } - pub const save_file_without_formatting_meta: Meta = .{ .description = "Save file without formatting" }; + pub const save_file_without_formatting_meta = .{ .description = "Save file without formatting" }; pub fn save_file_as(self: *Self, ctx: Context) Result { var file_path: []const u8 = undefined; diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index c56ee05..1b30744 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -359,7 +359,7 @@ const cmds = struct { const same_file = if (self.get_active_file_path()) |fp| std.mem.eql(u8, fp, f) else false; const have_editor_metadata = if (self.buffer_manager.get_buffer_for_file(f)) |_| true else false; - if (!same_file and !have_editor_metadata and line == null) + if (!same_file and !have_editor_metadata) if (try project_manager.get_mru_position(self.allocator, f)) |pos| { line = @intCast(pos.row); column = @intCast(pos.col); diff --git a/src/tui/tui.zig b/src/tui/tui.zig index 1c9e3f4..28ade15 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -133,8 +133,6 @@ fn init(allocator: Allocator) !*Self { self.rdr_.dispatch_event = dispatch_event; try self.rdr_.run(); - try project_manager.start(); - try frame_clock.start(); try self.commands.init(self); errdefer self.deinit(); @@ -897,15 +895,6 @@ const cmds = struct { pub const move_to_char_meta: Meta = .{ .description = "Move cursor to matching character" }; pub fn open_file(self: *Self, ctx: Ctx) Result { - if (get_active_selection(self.allocator)) |text| { - defer self.allocator.free(text); - const link = try root.file_link.parse(text); - switch (link) { - .file => |file| if (file.exists) - return root.file_link.navigate(tp.self_pid(), &link), - else => {}, - } - } return enter_mini_mode(self, @import("mode/mini/open_file.zig"), ctx); } pub const open_file_meta: Meta = .{ .description = "Open file" };