Compare commits

..

No commits in common. "3dfb93fbd2311e2df91d83f88a130ece97c1963e" and "764a8cce41e6264d3b98d458bab406bd592a5fd5" have entirely different histories.

7 changed files with 102 additions and 76 deletions

View file

@ -502,6 +502,7 @@ pub fn build_exe(
.{ .name = "EventHandler", .module = EventHandler_mod },
.{ .name = "input", .module = input_mod },
.{ .name = "thespian", .module = thespian_mod },
.{ .name = "log", .module = log_mod },
.{ .name = "Buffer", .module = Buffer_mod },
.{ .name = "config", .module = config_mod },
},

View file

@ -6,6 +6,7 @@ const std = @import("std");
const tp = @import("thespian");
const cbor = @import("cbor");
const builtin = @import("builtin");
const log = @import("log");
const root = @import("soft_root").root;
const input = @import("input");
@ -15,8 +16,6 @@ const KeyEvent = input.KeyEvent;
const SelectionStyle = @import("Buffer").Selection.Style;
pub const CursorShape = @import("config").CursorShape;
const log = std.log.scoped(.keybind);
const parse_flow = @import("parse_flow.zig");
const parse_vim = @import("parse_vim.zig");
@ -246,9 +245,11 @@ pub fn set_namespace(namespace_name: []const u8) LoadError!void {
fn get_mode_binding_set(mode_name: []const u8, insert_command: []const u8) LoadError!*const BindingSet {
const namespace = current_namespace();
var binding_set = namespace.get_mode(mode_name) orelse {
log.err("ERROR: mode not found: {s}", .{mode_name});
const logger = log.logger("keybind");
logger.print_err("get_namespace_mode", "ERROR: mode not found: {s}", .{mode_name});
var iter = namespace.modes.iterator();
while (iter.next()) |entry| log.info("available modes: {s}", .{entry.key_ptr.*});
while (iter.next()) |entry| logger.print("available modes: {s}", .{entry.key_ptr.*});
logger.deinit();
return error.NotFound;
};
binding_set.set_insert_command(insert_command);
@ -366,8 +367,11 @@ const Command = struct {
fn execute_const(self: *const @This()) void {
var buf: [2048]u8 = undefined;
@memcpy(buf[0..self.args.len], self.args);
command.executeName(self.command, .{ .args = .{ .buf = buf[0..self.args.len] } }) catch |e|
log.err("ERROR: {s} {s}", .{ self.command, @errorName(e) });
command.executeName(self.command, .{ .args = .{ .buf = buf[0..self.args.len] } }) catch |e| {
const logger = log.logger("keybind");
logger.print_err("init/deinit_command", "ERROR: {s} {s}", .{ self.command, @errorName(e) });
logger.deinit();
};
}
fn has_integer_argument(id: command.ID) bool {
@ -386,7 +390,9 @@ const Command = struct {
switch (state) {
.command => {
if (token != .string) {
log.err("ERROR: invalid command token {any}", .{token});
const logger = log.logger("keybind");
logger.print_err("keybind.load", "ERROR: invalid command token {any}", .{token});
logger.deinit();
return error.InvalidFormat;
}
command_ = try allocator.dupe(u8, token.string);
@ -398,7 +404,9 @@ const Command = struct {
else => {
const json = try std.json.Stringify.valueAlloc(allocator, token, .{});
defer allocator.free(json);
log.err("ERROR: invalid command argument '{s}'", .{json});
const logger = log.logger("keybind");
logger.print_err("keybind.load", "ERROR: invalid command argument '{s}'", .{json});
logger.deinit();
return error.InvalidFormat;
},
}
@ -530,22 +538,30 @@ const BindingSet = struct {
_ = event;
bindings: for (bindings) |entry| {
if (entry.len < 2) {
log.err("ERROR: invalid binding definition {any}", .{entry});
const logger = log.logger("keybind");
logger.print_err("keybind.load", "ERROR: invalid binding definition {any}", .{entry});
logger.deinit();
continue :bindings;
}
const keys = entry[0];
if (keys != .string) {
log.err("ERROR: invalid binding key definition {any}", .{keys});
const logger = log.logger("keybind");
logger.print_err("keybind.load", "ERROR: invalid binding key definition {any}", .{keys});
logger.deinit();
continue :bindings;
}
const key_events = switch (self.syntax) {
.flow => parse_flow.parse_key_events(allocator, keys.string) catch |e| {
log.err("ERROR: {s} {s}", .{ @errorName(e), parse_flow.parse_error_message });
const logger = log.logger("keybind");
logger.print_err("keybind.load", "ERROR: {s} {s}", .{ @errorName(e), parse_flow.parse_error_message });
logger.deinit();
break;
},
.vim => parse_vim.parse_key_events(allocator, keys.string) catch |e| {
log.err("ERROR: {s} {s}", .{ @errorName(e), parse_vim.parse_error_message });
const logger = log.logger("keybind");
logger.print_err("keybind.load.vim", "ERROR: {s} {s}", .{ @errorName(e), parse_vim.parse_error_message });
logger.deinit();
break;
},
};
@ -561,7 +577,9 @@ const BindingSet = struct {
if (cmd_entry != .array) {
const json = try std.json.Stringify.valueAlloc(allocator, cmd_entry, .{});
defer allocator.free(json);
log.err("ERROR: invalid command definition {s}", .{json});
const logger = log.logger("keybind");
logger.print_err("keybind.load", "ERROR: invalid command definition {s}", .{json});
logger.deinit();
continue :bindings;
}
try cmds.append(allocator, try Command.load(allocator, cmd_entry.array.items));
@ -775,7 +793,10 @@ const BindingSet = struct {
input.key.left_shift, input.key.right_shift => return,
else => {},
};
log.info("{f} is unbound, press C-? for key hints", .{current_key_event_sequence_fmt()});
const logger = log.logger("keybind");
defer logger.deinit();
logger.print("C-? for key hints", .{});
}
/// Retrieve bindings that will match a key event sequence
@ -879,16 +900,9 @@ const KeyEventSequenceFmt = struct {
key_events: []const KeyEvent,
pub fn format(self: @This(), writer: anytype) !void {
var first = true;
for (self.key_events) |key_event| {
if (first) {
first = false;
} else {
try writer.print(" ", .{});
}
for (self.key_events) |key_event|
try writer.print(" {f}", .{input.key_event_short_fmt(key_event)});
}
}
};
pub fn key_event_sequence_fmt(key_events: []const KeyEvent) KeyEventSequenceFmt {
@ -971,7 +985,7 @@ test "match" {
}
test "json" {
var bindings: BindingSet = .{ .name = "test", .config_section = "test_section", .selection_style = .normal };
var bindings: BindingSet = .{ .name = "test", .selection_style = .normal };
_ = try bindings.process_key_event(input.KeyEvent.from_key('j'));
_ = try bindings.process_key_event(input.KeyEvent.from_key('k'));
_ = try bindings.process_key_event(input.KeyEvent.from_key('g'));

View file

@ -260,11 +260,7 @@ pub fn std_log_function(
const log_pid = std_log_pid orelse return;
const prefix = "[" ++ comptime level.asText() ++ "] ";
var buf: [max_log_message]u8 = undefined;
const fmt = switch (level) {
.warn, .debug => prefix ++ format,
.err, .info => format,
};
const output = std.fmt.bufPrint(&buf, fmt, args) catch "MESSAGE TOO LARGE";
const output = std.fmt.bufPrint(&buf, prefix ++ format, args) catch "MESSAGE TOO LARGE";
if (level == .err) {
log_pid.send(.{ "log", "error", @tagName(scope), "std.log", "->", output }) catch {};
} else {

View file

@ -30,7 +30,8 @@ pub const application_subtext = "a programmer's text editor";
pub const application_description = application_title ++ ": " ++ application_subtext;
pub const std_options: std.Options = .{
.log_level = if (builtin.mode == .Debug) .debug else .info,
// .log_level = if (builtin.mode == .Debug) .debug else .warn,
.log_level = if (builtin.mode == .Debug) .info else .warn,
.logFn = log.std_log_function,
};

View file

@ -206,7 +206,7 @@ const Process = struct {
var buf: [1024]u8 = undefined;
const json = self.argv.to_json(&buf) catch |e| return tp.exit_error(e, @errorReturnTrace());
if (self.handlers.log_execute)
self.logger.print("execute {s}", .{json});
self.logger.print("shell: execute {s}", .{json});
self.sp = tp.subprocess.init(self.allocator, self.argv, module_name, self.stdin_behavior) catch |e| return tp.exit_error(e, @errorReturnTrace());
tp.receive(&self.receiver);
}

View file

@ -12,13 +12,12 @@ var show_page: usize = 0;
pub fn render_current_input_mode(allocator: std.mem.Allocator, select_mode: keybind.SelectMode, theme: *const Widget.Theme) void {
const mode = tui.input_mode() orelse return;
const key_events = mode.current_key_event_sequence_bindings(allocator, select_mode) catch return;
const bindings = if (key_events.len > 0)
key_events
else
mode.current_bindings(allocator, select_mode) catch return;
const bindings = blk: {
const b = mode.current_key_event_sequence_bindings(allocator, select_mode) catch return;
break :blk if (b.len > 0) b else mode.current_bindings(allocator, select_mode) catch return;
};
defer allocator.free(bindings);
return render(mode, bindings, theme, if (key_events.len > 0) .no_key_event_prefix else .full);
return render(mode, bindings, theme, .full);
}
pub fn render_current_key_event_sequence(allocator: std.mem.Allocator, select_mode: keybind.SelectMode, theme: *const Widget.Theme) void {
@ -54,7 +53,7 @@ fn render(mode: *keybind.Mode, bindings: []const keybind.Binding, theme: *const
const max_len = max_prefix_len + max_description_len + 2 + 2;
const widget_style = tui.get_widget_style(widget_type);
const scr = tui.screen();
const max_screen_height = scr.h -| widget_style.padding.top -| widget_style.padding.bottom -| 3;
const max_screen_height = scr.h -| widget_style.padding.top -| widget_style.padding.bottom -| 1;
const max_items = @min(bindings.len, max_screen_height);
const page_size = max_screen_height;
var top = show_page * page_size;
@ -91,15 +90,6 @@ fn render(mode: *keybind.Mode, bindings: []const keybind.Binding, theme: *const
}
if (widget_style.padding.top > 0) {
top_layer_.cursor_move_yx(@intCast(0), @intCast(3)) catch return;
if (key_events.len > 0) {
_ = top_layer_.print("{s} {s}/{s} prefix: {s} {s}", .{
widget_style.border.nib,
keybind.get_namespace(),
mode.bindings.config_section,
key_events,
widget_style.border.nie,
}) catch {};
} else {
_ = top_layer_.print("{s} {s}/{s} {s}", .{
widget_style.border.nib,
keybind.get_namespace(),
@ -107,7 +97,6 @@ fn render(mode: *keybind.Mode, bindings: []const keybind.Binding, theme: *const
widget_style.border.nie,
}) catch {};
}
}
// workaround vaxis.Layer issue
const top_layer_window = top_layer_.window;
@ -144,17 +133,14 @@ fn render(mode: *keybind.Mode, bindings: []const keybind.Binding, theme: *const
break :blk writer.buffered();
};
plane.cursor_move_yx(@intCast(y), 0) catch break;
switch (render_mode) {
.no_key_event_prefix => _ = plane.print("{s}", .{keybind_txt[key_events.len..]}) catch {},
.full => _ = plane.print(" {s}", .{keybind_txt}) catch {},
}
_ = plane.print("{s}", .{keybind_txt[key_events.len..]}) catch {};
}
plane.set_style(style_label);
for (bindings[top..], 0..) |binding, y| {
if (y >= max_items) break;
const padding = max_prefix_len + 3;
const padding = max_prefix_len + 2;
const description = blk: {
const id = binding.commands[0].command_id orelse

View file

@ -13,6 +13,8 @@ const Buffer = @import("Buffer");
const Cursor = Buffer.Cursor;
const Selection = Buffer.Selection;
const Direction = enum { backwards, forwards };
var commands: Commands = undefined;
pub fn init() !void {
@ -242,62 +244,62 @@ const cmds_ = struct {
pub const extend_line_below_meta: Meta = .{ .arguments = &.{.integer}, .description = "Select current line, if already selected, extend to next line" };
pub fn move_next_word_start(_: *void, ctx: Ctx) Result {
try move_to_word(ctx, Editor.move_cursor_word_right_vim);
try move_to_word(ctx, Editor.move_cursor_word_right_vim, .forwards);
}
pub const move_next_word_start_meta: Meta = .{ .description = "Move next word start", .arguments = &.{.integer} };
pub fn extend_next_word_start(_: *void, ctx: Ctx) Result {
try extend_to_word(ctx, Editor.move_cursor_word_right_vim);
try extend_to_word(ctx, Editor.move_cursor_word_right_vim, .forwards);
}
pub const extend_next_word_start_meta: Meta = .{ .description = "Extend next word start", .arguments = &.{.integer} };
pub fn move_next_long_word_start(_: *void, ctx: Ctx) Result {
try move_to_word(ctx, move_cursor_long_word_right);
try move_to_word(ctx, move_cursor_long_word_right, .forwards);
}
pub const move_next_long_word_start_meta: Meta = .{ .description = "Move next long word start", .arguments = &.{.integer} };
pub fn extend_next_long_word_start(_: *void, ctx: Ctx) Result {
try extend_to_word(ctx, move_cursor_long_word_right);
try extend_to_word(ctx, move_cursor_long_word_right, .forwards);
}
pub const extend_next_long_word_start_meta: Meta = .{ .description = "Extend next long word start", .arguments = &.{.integer} };
pub fn move_prev_word_start(_: *void, ctx: Ctx) Result {
try move_to_word(ctx, move_cursor_word_left_helix);
try move_to_word(ctx, move_cursor_word_left_helix, .backwards);
}
pub const move_prev_word_start_meta: Meta = .{ .description = "Move previous word start", .arguments = &.{.integer} };
pub fn extend_prev_word_start(_: *void, ctx: Ctx) Result {
try extend_to_word(ctx, move_cursor_word_left_helix);
try extend_to_word(ctx, move_cursor_word_left_helix, .backwards);
}
pub const extend_prev_word_start_meta: Meta = .{ .description = "Extend previous word start", .arguments = &.{.integer} };
pub fn move_prev_long_word_start(_: *void, ctx: Ctx) Result {
try move_to_word(ctx, move_cursor_long_word_left);
try move_to_word(ctx, move_cursor_long_word_left, .backwards);
}
pub const move_prev_long_word_start_meta: Meta = .{ .description = "Move previous long word start", .arguments = &.{.integer} };
pub fn extend_prev_long_word_start(_: *void, ctx: Ctx) Result {
try extend_to_word(ctx, move_cursor_long_word_left);
try extend_to_word(ctx, move_cursor_long_word_left, .backwards);
}
pub const extend_prev_long_word_start_meta: Meta = .{ .description = "Extend previous word start", .arguments = &.{.integer} };
pub fn move_next_word_end(_: *void, ctx: Ctx) Result {
try move_to_word(ctx, move_cursor_word_right_end_helix);
try move_to_word(ctx, move_cursor_word_right_end_helix, .forwards);
}
pub const move_next_word_end_meta: Meta = .{ .description = "Move next word end", .arguments = &.{.integer} };
pub fn extend_next_word_end(_: *void, ctx: Ctx) Result {
try extend_to_word(ctx, move_cursor_word_right_end_helix);
try extend_to_word(ctx, move_cursor_word_right_end_helix, .forwards);
}
pub const extend_next_word_end_meta: Meta = .{ .description = "Extend next word end", .arguments = &.{.integer} };
pub fn move_next_long_word_end(_: *void, ctx: Ctx) Result {
try move_to_word(ctx, move_cursor_long_word_right_end);
try move_to_word(ctx, move_cursor_long_word_right_end, .forwards);
}
pub const move_next_long_word_end_meta: Meta = .{ .description = "Move next long word end", .arguments = &.{.integer} };
pub fn extend_next_long_word_end(_: *void, ctx: Ctx) Result {
try extend_to_word(ctx, move_cursor_long_word_right_end);
try extend_to_word(ctx, move_cursor_long_word_right_end, .forwards);
}
pub const extend_next_long_word_end_meta: Meta = .{ .description = "Extend next long word end", .arguments = &.{.integer} };
@ -526,7 +528,7 @@ fn match_bracket(root: Buffer.Root, cursel: *CurSel, ctx: command.Context, metri
}
}
fn move_to_word(ctx: command.Context, move: Editor.cursor_operator_const) command.Result {
fn move_to_word(ctx: command.Context, move: Editor.cursor_operator_const, direction: Direction) command.Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = try ed.buf_root();
@ -537,18 +539,45 @@ fn move_to_word(ctx: command.Context, move: Editor.cursor_operator_const) comman
if (repeat > 1) ed.with_cursors_const_repeat(root, move, command.fmt(.{repeat - 1})) catch {};
for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
cursel.selection = null;
var sel = Selection.from_cursor(&cursel.cursor);
const cur = sel.begin.test_at(root, is_not_whitespace_or_eol, ed.metrics);
if (direction == .backwards) {
sel.begin.move_left(root, ed.metrics) catch continue;
const prev = sel.begin.test_at(root, Editor.is_not_word_char, ed.metrics);
sel.begin = sel.end;
if (!cur or cur != prev)
sel.begin.move_right(root, ed.metrics) catch continue;
} else {
sel.end.move_right(root, ed.metrics) catch continue;
const next = sel.end.test_at(root, Editor.is_not_word_char, ed.metrics);
if (!cur and cur != next)
sel.begin = sel.end;
}
cursel.cursor = sel.end;
cursel.selection = sel;
};
ed.with_selections_const_repeat(root, move, command.fmt(.{1})) catch {};
ed.clamp();
}
fn extend_to_word(ctx: command.Context, move: Editor.cursor_operator_const) command.Result {
fn extend_to_word(ctx: command.Context, move: Editor.cursor_operator_const, _: Direction) command.Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = try ed.buf_root();
ed.with_selections_const_repeat(root, move, ctx) catch {};
var repeat: usize = 1;
_ = ctx.args.match(.{tp.extract(&repeat)}) catch false;
for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
const sel = cursel.enable_selection(root, ed.metrics);
const pivot: usize = if (sel.is_reversed()) sel.begin.col -| 1 else sel.begin.col;
var i: usize = repeat;
while (i > 0) : (i -= 1) {
try move(root, &sel.end, ed.metrics);
}
sel.begin.col = if (sel.is_reversed()) pivot +| 1 else pivot;
cursel.cursor = sel.end;
};
ed.clamp();
}
@ -830,7 +859,6 @@ fn move_noop(_: Buffer.Root, _: *Cursor, _: Buffer.Metrics) error{Stop}!void {}
fn move_cursor_word_right_end_helix(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void {
try Editor.move_cursor_right(root, cursor, metrics);
Editor.move_cursor_right_until(root, cursor, Editor.is_word_boundary_right_vim, metrics);
try cursor.move_right(root, metrics);
}
fn move_cursor_to_char_left_beyond_eol(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics, ctx: command.Context) error{Stop}!void {