Compare commits
22 commits
8c9146a1c5
...
b5d137666f
| Author | SHA1 | Date | |
|---|---|---|---|
| b5d137666f | |||
| 4f39bbbd41 | |||
| 911fc3160a | |||
| b2996cddbd | |||
| 6fce29f876 | |||
| 96676c738a | |||
| ed027b5f6d | |||
| 7d2e6a3b45 | |||
| a5d7e76897 | |||
| 35d823d0b8 | |||
| d55b5b695b | |||
| c4301c40d9 | |||
| d59dc65e6b | |||
| 478f919051 | |||
| 0c92cd8b8f | |||
| f75cc9b845 | |||
| 6c60e5a0df | |||
| 1b9bb31a59 | |||
| 12159edfcf | |||
| 24afc12f4c | |||
| d711279938 | |||
| 10e9348901 |
9 changed files with 251 additions and 79 deletions
|
|
@ -669,6 +669,22 @@ const Node = union(enum) {
|
|||
return self.delete_bytes(sel.begin.row, pos, size, allocator, metrics) catch return error.Stop;
|
||||
}
|
||||
|
||||
pub fn delete_range_char(self: *const Node, sel: Selection, allocator: Allocator, size_: ?*usize, metrics: Metrics) error{Stop}!struct { Root, ?u8 } {
|
||||
var size: usize = 0;
|
||||
defer if (size_) |p| {
|
||||
p.* = size;
|
||||
};
|
||||
_ = self.get_range(sel, null, &size, null, metrics) catch return error.Stop;
|
||||
const char = if (size == 1) blk: {
|
||||
var result_buf: [6]u8 = undefined;
|
||||
const result = self.get_range(sel, &result_buf, null, null, metrics) catch break :blk null;
|
||||
break :blk (result orelse break :blk null)[0];
|
||||
} else null;
|
||||
const pos = try self.get_line_width_to_pos(sel.begin.row, sel.begin.col, metrics);
|
||||
const root = self.delete_bytes(sel.begin.row, pos, size, allocator, metrics) catch return error.Stop;
|
||||
return .{ root, char };
|
||||
}
|
||||
|
||||
pub fn delete_bytes(self: *const Node, line: usize, pos_: usize, bytes: usize, allocator: Allocator, metrics_: Metrics) !Root {
|
||||
const Ctx = struct {
|
||||
allocator: Allocator,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ const tp = @import("thespian");
|
|||
const log = @import("log");
|
||||
const cbor = @import("cbor");
|
||||
|
||||
pub var log_execute: bool = false;
|
||||
pub var context_check: ?*const fn () void = null;
|
||||
|
||||
pub const ID = usize;
|
||||
|
|
@ -69,15 +70,10 @@ pub fn Closure(comptime T: type) type {
|
|||
}
|
||||
|
||||
pub fn register(self: *Self) !void {
|
||||
if (command_names.get(self.vtbl.name)) |id| {
|
||||
self.vtbl.id = id;
|
||||
reAddCommand(&self.vtbl) catch |e| return log.err("cmd", "reAddCommand", e);
|
||||
// log.print("cmd", "reAddCommand({s}) => {d}", .{ self.vtbl.name, self.vtbl.id });
|
||||
} else {
|
||||
self.vtbl.id = try addCommand(&self.vtbl);
|
||||
command_names.put(self.vtbl.name, self.vtbl.id) catch |e| return log.err("cmd", "addCommand", e);
|
||||
// log.print("cmd", "addCommand({s}) => {d}", .{ self.vtbl.name, self.vtbl.id });
|
||||
}
|
||||
if (command_names.get(self.vtbl.name)) |id|
|
||||
reAddCommand(id, &self.vtbl) catch |e| return log.err("cmd", "reAddCommand", e)
|
||||
else
|
||||
addCommand(&self.vtbl);
|
||||
}
|
||||
|
||||
pub fn unregister(self: *Self) void {
|
||||
|
|
@ -100,21 +96,31 @@ pub var commands: CommandTable = .empty;
|
|||
var command_names: std.StringHashMap(ID) = std.StringHashMap(ID).init(command_table_allocator);
|
||||
const command_table_allocator = std.heap.c_allocator;
|
||||
|
||||
fn addCommand(cmd: *Vtable) !ID {
|
||||
try commands.append(command_table_allocator, cmd);
|
||||
return commands.items.len - 1;
|
||||
fn assignCommandId(name: []const u8) ID {
|
||||
commands.append(command_table_allocator, null) catch |e| std.debug.panic("assignCommandId: {t}", .{e});
|
||||
const id = commands.items.len - 1;
|
||||
command_names.put(name, id) catch |e| std.debug.panic("assignCommandId: {t}", .{e});
|
||||
return id;
|
||||
}
|
||||
|
||||
fn reAddCommand(cmd: *Vtable) !void {
|
||||
if (commands.items[cmd.id] != null) return error.DuplicateCommand;
|
||||
commands.items[cmd.id] = cmd;
|
||||
fn addCommand(cmd: *Vtable) void {
|
||||
commands.append(command_table_allocator, cmd) catch |e| std.debug.panic("addCommand: {t}", .{e});
|
||||
const id = commands.items.len - 1;
|
||||
cmd.id = id;
|
||||
command_names.put(cmd.name, id) catch |e| std.debug.panic("assignCommandId: {t}", .{e});
|
||||
}
|
||||
|
||||
fn reAddCommand(id: ID, cmd: *Vtable) !void {
|
||||
cmd.id = id;
|
||||
if (commands.items[id] != null) return error.DuplicateCommand;
|
||||
commands.items[id] = cmd;
|
||||
}
|
||||
|
||||
pub fn removeCommand(id: ID) void {
|
||||
commands.items[id] = null;
|
||||
}
|
||||
|
||||
pub fn execute(id: ID, ctx: Context) tp.result {
|
||||
pub fn execute(id: ID, name: []const u8, ctx: Context) tp.result {
|
||||
if (tp.env.get().enabled(tp.channel.debug)) trace: {
|
||||
var iter = ctx.args.buf;
|
||||
var len = cbor.decodeArrayHeader(&iter) catch break :trace;
|
||||
|
|
@ -140,20 +146,26 @@ pub fn execute(id: ID, ctx: Context) tp.result {
|
|||
}
|
||||
if (context_check) |check| check();
|
||||
if (id >= commands.items.len)
|
||||
return tp.exit_fmt("CommandNotFound: {d}", .{id});
|
||||
return notFoundError(id, name);
|
||||
const cmd = commands.items[id];
|
||||
if (cmd) |p| {
|
||||
// var buf: [tp.max_message_size]u8 = undefined;
|
||||
// log.print("cmd", "execute({s}) {s}", .{ p.name, ctx.args.to_json(&buf) catch "" }) catch |e| return tp.exit_error(e, @errorReturnTrace());
|
||||
if (log_execute) {
|
||||
var buf: [tp.max_message_size]u8 = undefined;
|
||||
log.print("cmd", "execute({d}) {s} {s}", .{ id, p.name, if (ctx.args.buf.len > 0) ctx.args.to_json(&buf) catch "(error)" else "" });
|
||||
}
|
||||
return p.run(p, ctx);
|
||||
} else {
|
||||
return tp.exit_fmt("CommandNotAvailable: {d}", .{id});
|
||||
return notFoundError(id, name);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_id(name: []const u8) ?ID {
|
||||
var id: ?ID = null;
|
||||
return get_id_cache(name, &id);
|
||||
const id = get_name_id(name);
|
||||
return if (commands.items[id]) |_| id else null;
|
||||
}
|
||||
|
||||
pub fn get_name_id(name: []const u8) ID {
|
||||
return command_names.get(name) orelse assignCommandId(name);
|
||||
}
|
||||
|
||||
pub fn get_name(id: ID) ?[]const u8 {
|
||||
|
|
@ -164,19 +176,17 @@ pub fn get_name(id: ID) ?[]const u8 {
|
|||
tp.trace(tp.channel.debug, .{ "command", "get_name", "null", id });
|
||||
}
|
||||
if (id >= commands.items.len) return null;
|
||||
return (commands.items[id] orelse return null).name;
|
||||
if (commands.items[id]) |cmd| return cmd.name;
|
||||
var iter = command_names.iterator();
|
||||
while (iter.next()) |kv| if (kv.value_ptr.* == id)
|
||||
return kv.key_ptr.*;
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn get_id_cache(name: []const u8, id: *?ID) ?ID {
|
||||
for (commands.items) |cmd| {
|
||||
if (cmd) |p|
|
||||
if (std.mem.eql(u8, p.name, name)) {
|
||||
id.* = p.id;
|
||||
return p.id;
|
||||
};
|
||||
}
|
||||
tp.trace(tp.channel.debug, .{ "command", "get_id_cache", "failed", name });
|
||||
return null;
|
||||
pub fn get_id_cache(name: []const u8, cached_id: *?ID) ID {
|
||||
const id = get_name_id(name);
|
||||
cached_id.* = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
pub fn get_description(id: ID) ?[]const u8 {
|
||||
|
|
@ -198,17 +208,16 @@ const suppressed_errors = std.StaticStringMap(void).initComptime(.{
|
|||
.{ "enable_fast_scroll", void },
|
||||
.{ "disable_fast_scroll", void },
|
||||
.{ "clear_diagnostics", void },
|
||||
.{ "palette_menu_cancel", void },
|
||||
});
|
||||
|
||||
pub fn executeName(name: []const u8, ctx: Context) tp.result {
|
||||
const id = get_id(name);
|
||||
if (id) |id_| return execute(id_, ctx);
|
||||
return notFoundError(name);
|
||||
return execute(get_name_id(name), name, ctx);
|
||||
}
|
||||
|
||||
pub fn notFoundError(name: []const u8) !void {
|
||||
fn notFoundError(id: ID, name: []const u8) !void {
|
||||
if (!suppressed_errors.has(name))
|
||||
return tp.exit_fmt("CommandNotFound: {s}", .{name});
|
||||
return tp.exit_fmt("CommandNotFound: {s}({d})", .{ name, id });
|
||||
}
|
||||
|
||||
fn CmdDef(comptime T: type) type {
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ centered_view_min_screen_width: usize = 145,
|
|||
lsp_output: enum { quiet, verbose } = .quiet,
|
||||
|
||||
keybind_mode: KeybindMode = .normal,
|
||||
dropdown_keybinds: DropdownKeybindMode = .standard,
|
||||
|
||||
include_files: []const u8 = "",
|
||||
|
||||
|
|
@ -158,6 +159,11 @@ pub const KeybindMode = enum {
|
|||
ignore_alt_text_modifiers,
|
||||
};
|
||||
|
||||
pub const DropdownKeybindMode = enum {
|
||||
standard,
|
||||
noninvasive,
|
||||
};
|
||||
|
||||
pub const InitialFindQuery = enum {
|
||||
empty,
|
||||
selection,
|
||||
|
|
|
|||
|
|
@ -375,7 +375,7 @@
|
|||
["right_control", "palette_menu_activate_quick"]
|
||||
]
|
||||
},
|
||||
"overlay/dropdown": {
|
||||
"overlay/dropdown-noninvasive": {
|
||||
"inherit": "normal",
|
||||
"press": [
|
||||
["alt+f9", "dropdown_next_widget_style"],
|
||||
|
|
@ -385,7 +385,24 @@
|
|||
["ctrl+up", "palette_menu_up"],
|
||||
["ctrl+down", "palette_menu_down"],
|
||||
["ctrl+enter", "palette_menu_activate"],
|
||||
["tab", "palette_menu_complete"]
|
||||
["tab", "palette_menu_complete"],
|
||||
["ctrl+backspace", "overlay_delete_word_left"],
|
||||
["backspace", "overlay_delete_backwards"]
|
||||
]
|
||||
},
|
||||
"overlay/dropdown": {
|
||||
"inherit": "normal",
|
||||
"press": [
|
||||
["alt+f9", "dropdown_next_widget_style"],
|
||||
["ctrl+p", "palette_menu_up"],
|
||||
["ctrl+n", "palette_menu_down"],
|
||||
["escape", "palette_menu_cancel"],
|
||||
["up", "palette_menu_up"],
|
||||
["down", "palette_menu_down"],
|
||||
["enter", "palette_menu_activate"],
|
||||
["tab", "palette_menu_complete"],
|
||||
["ctrl+backspace", "overlay_delete_word_left"],
|
||||
["backspace", "overlay_delete_backwards"]
|
||||
]
|
||||
},
|
||||
"mini/numeric": {
|
||||
|
|
|
|||
|
|
@ -349,19 +349,17 @@ const Command = struct {
|
|||
|
||||
fn execute(self: *@This()) !void {
|
||||
const id = self.command_id orelse
|
||||
command.get_id_cache(self.command, &self.command_id) orelse {
|
||||
return command.notFoundError(self.command);
|
||||
};
|
||||
command.get_id_cache(self.command, &self.command_id);
|
||||
var buf: [2048]u8 = undefined;
|
||||
@memcpy(buf[0..self.args.len], self.args);
|
||||
if (integer_argument) |int_arg| {
|
||||
if (cbor.match(self.args, .{}) catch false and has_integer_argument(id)) {
|
||||
integer_argument = null;
|
||||
try command.execute(id, command.fmt(.{int_arg}));
|
||||
try command.execute(id, self.command, command.fmt(.{int_arg}));
|
||||
return;
|
||||
}
|
||||
}
|
||||
try command.execute(id, .{ .args = .{ .buf = buf[0..self.args.len] } });
|
||||
try command.execute(id, self.command, .{ .args = .{ .buf = buf[0..self.args.len] } });
|
||||
}
|
||||
|
||||
fn execute_const(self: *const @This()) void {
|
||||
|
|
@ -662,11 +660,9 @@ const BindingSet = struct {
|
|||
if (enable_insert_events)
|
||||
self.send_insert_event(globals.insert_command, globals.input_buffer.items);
|
||||
const id = globals.insert_command_id orelse
|
||||
command.get_id_cache(globals.insert_command, &globals.insert_command_id) orelse {
|
||||
return tp.exit_error(error.InputTargetNotFound, null);
|
||||
};
|
||||
command.get_id_cache(globals.insert_command, &globals.insert_command_id);
|
||||
if (!builtin.is_test) {
|
||||
try command.execute(id, command.fmt(.{globals.input_buffer.items}));
|
||||
try command.execute(id, globals.insert_command, command.fmt(.{globals.input_buffer.items}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -258,6 +258,31 @@ pub const CurSel = struct {
|
|||
}
|
||||
};
|
||||
|
||||
pub const TriggerEvent = enum {
|
||||
insert,
|
||||
delete,
|
||||
};
|
||||
|
||||
pub const TriggerSymbol = struct {
|
||||
char: u8,
|
||||
command: command.ID,
|
||||
|
||||
pub fn cborEncode(self: @This(), writer: *std.Io.Writer) std.io.Writer.Error!void {
|
||||
try cbor.writeArrayHeader(writer, 2);
|
||||
try cbor.writeValue(writer, self.char);
|
||||
try cbor.writeValue(writer, command.get_name(self.command));
|
||||
}
|
||||
|
||||
pub fn cborExtract(self: *@This(), iter: *[]const u8) cbor.Error!bool {
|
||||
var command_name: []const u8 = undefined;
|
||||
if (try cbor.matchValue(iter, .{ cbor.extract(&self.char), cbor.extract(&command_name) })) {
|
||||
self.command = command.get_id(command_name) orelse command.ID_unknown;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
pub const Diagnostic = struct {
|
||||
source: []const u8,
|
||||
code: []const u8,
|
||||
|
|
@ -379,6 +404,9 @@ pub const Editor = struct {
|
|||
syntax_last_rendered_root: ?Buffer.Root = null,
|
||||
syntax_incremental_reparse: bool = false,
|
||||
|
||||
insert_triggers: std.ArrayList(TriggerSymbol) = .empty,
|
||||
delete_triggers: std.ArrayList(TriggerSymbol) = .empty,
|
||||
|
||||
style_cache: ?StyleCache = null,
|
||||
style_cache_theme: []const u8 = "",
|
||||
|
||||
|
|
@ -465,7 +493,7 @@ pub const Editor = struct {
|
|||
}
|
||||
|
||||
pub fn write_state(self: *const Self, writer: *std.Io.Writer) !void {
|
||||
try cbor.writeArrayHeader(writer, 10);
|
||||
try cbor.writeArrayHeader(writer, 12);
|
||||
try cbor.writeValue(writer, self.file_path orelse "");
|
||||
try cbor.writeValue(writer, self.last_find_query orelse "");
|
||||
try cbor.writeValue(writer, self.enable_format_on_save);
|
||||
|
|
@ -473,6 +501,8 @@ pub const Editor = struct {
|
|||
try cbor.writeValue(writer, self.tab_width);
|
||||
try cbor.writeValue(writer, self.indent_mode);
|
||||
try cbor.writeValue(writer, self.syntax_no_render);
|
||||
try cbor.writeValue(writer, self.insert_triggers.items);
|
||||
try cbor.writeValue(writer, self.delete_triggers.items);
|
||||
if (self.find_history) |history| {
|
||||
try cbor.writeArrayHeader(writer, history.items.len);
|
||||
for (history.items) |item|
|
||||
|
|
@ -494,6 +524,8 @@ pub const Editor = struct {
|
|||
var view_cbor: []const u8 = undefined;
|
||||
var cursels_cbor: []const u8 = undefined;
|
||||
var last_find_query: []const u8 = undefined;
|
||||
var insert_triggers: []TriggerSymbol = undefined;
|
||||
var delete_triggers: []TriggerSymbol = undefined;
|
||||
var find_history: []const u8 = undefined;
|
||||
if (!try cbor.matchValue(iter, .{
|
||||
tp.extract(&file_path),
|
||||
|
|
@ -503,11 +535,17 @@ pub const Editor = struct {
|
|||
tp.extract(&self.tab_width),
|
||||
tp.extract(&self.indent_mode),
|
||||
tp.extract(&self.syntax_no_render),
|
||||
cbor.extractAlloc(&insert_triggers, self.allocator),
|
||||
cbor.extractAlloc(&delete_triggers, self.allocator),
|
||||
tp.extract_cbor(&find_history),
|
||||
tp.extract_cbor(&view_cbor),
|
||||
tp.extract_cbor(&cursels_cbor),
|
||||
}))
|
||||
return error.RestoreStateMatch;
|
||||
self.insert_triggers.deinit(self.allocator);
|
||||
self.insert_triggers = .fromOwnedSlice(insert_triggers);
|
||||
self.delete_triggers.deinit(self.allocator);
|
||||
self.delete_triggers = .fromOwnedSlice(delete_triggers);
|
||||
self.refresh_tab_width();
|
||||
if (op == .open_file)
|
||||
try self.open(file_path);
|
||||
|
|
@ -566,6 +604,7 @@ pub const Editor = struct {
|
|||
.enable_terminal_cursor = tui.config().enable_terminal_cursor,
|
||||
.render_whitespace = tui.config().whitespace_mode,
|
||||
};
|
||||
self.add_default_symbol_triggers();
|
||||
}
|
||||
|
||||
fn deinit(self: *Self) void {
|
||||
|
|
@ -575,6 +614,8 @@ pub const Editor = struct {
|
|||
for (self.diagnostics.items) |*d| d.deinit(self.allocator);
|
||||
self.diagnostics.deinit(self.allocator);
|
||||
self.completions.deinit(self.allocator);
|
||||
self.insert_triggers.deinit(self.allocator);
|
||||
self.delete_triggers.deinit(self.allocator);
|
||||
if (self.syntax) |syn| syn.destroy(tui.query_cache());
|
||||
self.cancel_all_tabstops();
|
||||
self.cursels.deinit(self.allocator);
|
||||
|
|
@ -2243,8 +2284,9 @@ pub const Editor = struct {
|
|||
cursel.cursor = sel.begin;
|
||||
cursel.disable_selection_normal();
|
||||
var size: usize = 0;
|
||||
const root_ = try root.delete_range(sel, allocator, &size, self.metrics);
|
||||
const root_, const trigger_char = try root.delete_range_char(sel, allocator, &size, self.metrics);
|
||||
self.nudge_delete(sel, cursel, size);
|
||||
if (trigger_char) |char| self.run_triggers(cursel, char, .delete);
|
||||
return root_;
|
||||
}
|
||||
|
||||
|
|
@ -2823,6 +2865,7 @@ pub const Editor = struct {
|
|||
cursor.row, cursor.col, root_ = try root_.insert_chars(cursor.row, cursor.col, s, allocator, self.metrics);
|
||||
cursor.target = cursor.col;
|
||||
self.nudge_insert(.{ .begin = begin, .end = cursor.* }, cursel, s.len);
|
||||
if (s.len == 1) self.run_triggers(cursel, s[0], .insert);
|
||||
return root_;
|
||||
}
|
||||
|
||||
|
|
@ -5702,7 +5745,7 @@ pub const Editor = struct {
|
|||
pub fn goto_next_diagnostic(self: *Self, _: Context) Result {
|
||||
if (self.diagnostics.items.len == 0) {
|
||||
if (command.get_id("goto_next_file")) |id|
|
||||
return command.execute(id, .{});
|
||||
return command.execute(id, "goto_next_file", .{});
|
||||
return;
|
||||
}
|
||||
self.sort_diagnostics();
|
||||
|
|
@ -5718,7 +5761,7 @@ pub const Editor = struct {
|
|||
pub fn goto_prev_diagnostic(self: *Self, _: Context) Result {
|
||||
if (self.diagnostics.items.len == 0) {
|
||||
if (command.get_id("goto_prev_file")) |id|
|
||||
return command.execute(id, .{});
|
||||
return command.execute(id, "goto_prev_file", .{});
|
||||
return;
|
||||
}
|
||||
self.sort_diagnostics();
|
||||
|
|
@ -6184,6 +6227,54 @@ pub const Editor = struct {
|
|||
self.need_render();
|
||||
}
|
||||
|
||||
fn get_event_triggers(self: *Self, event: TriggerEvent) *std.ArrayList(TriggerSymbol) {
|
||||
return switch (event) {
|
||||
.insert => &self.insert_triggers,
|
||||
.delete => &self.delete_triggers,
|
||||
};
|
||||
}
|
||||
|
||||
fn add_default_symbol_triggers(self: *Self) void {
|
||||
const id = command.get_name_id("completion");
|
||||
self.add_symbol_trigger('.', id, .insert) catch {};
|
||||
}
|
||||
|
||||
pub fn add_symbol_trigger(self: *Self, char: u8, command_: command.ID, event: TriggerEvent) error{OutOfMemory}!void {
|
||||
for (self.get_event_triggers(event).items) |item| if (item.char == char and item.command == command_) return;
|
||||
(try self.get_event_triggers(event).addOne(self.allocator)).* = .{ .char = char, .command = command_ };
|
||||
}
|
||||
|
||||
pub fn remove_symbol_trigger(self: *Self, char: u8, command_: command.ID, event: TriggerEvent) bool {
|
||||
const triggers = self.get_event_triggers(event);
|
||||
for (triggers.items, 0..) |item, i| if (item.char == char and item.command == command_) {
|
||||
_ = triggers.orderedRemove(i);
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn run_triggers(self: *Self, cursel: *const CurSel, char: u8, event: TriggerEvent) void {
|
||||
switch (char) {
|
||||
'\n', '\t', ' ' => return,
|
||||
else => {},
|
||||
}
|
||||
if (!cursel.cursor.eql(self.get_primary().cursor)) return;
|
||||
for (self.get_event_triggers(event).items) |item| if (item.char == char) {
|
||||
if (command.log_execute)
|
||||
self.logger.print("trigger: {t} '{c}' {?s}({d})", .{ event, char, command.get_name(item.command), item.command });
|
||||
tp.self_pid().send(.{ "cmd", "run_trigger", .{ item.command, [_]u8{char} } }) catch {};
|
||||
};
|
||||
}
|
||||
|
||||
pub fn run_trigger(_: *Self, ctx: Context) Result {
|
||||
var cmd: command.ID = undefined;
|
||||
var trigger_char: []const u8 = undefined;
|
||||
if (!try ctx.args.match(.{ tp.extract(&cmd), tp.extract(&trigger_char) }))
|
||||
return error.InvalidRunTriggerArgument;
|
||||
tp.self_pid().send(.{ "cmd", cmd, .{trigger_char} }) catch {};
|
||||
}
|
||||
pub const run_trigger_meta: Meta = .{ .arguments = &.{ .integer, .string } };
|
||||
|
||||
pub fn add_completion(self: *Self, row: usize, col: usize, is_incomplete: bool, msg: tp.message) Result {
|
||||
if (!(row == self.completion_row and col == self.completion_col)) {
|
||||
self.completions.clearRetainingCapacity();
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ pub const Entry = struct {
|
|||
pub const ValueType = struct {
|
||||
start: ed.CurSel = .{},
|
||||
cursor: ed.Cursor = .{},
|
||||
view: ed.View = .{},
|
||||
replace: ?Buffer.Selection = null,
|
||||
};
|
||||
pub const defaultValue: ValueType = .{};
|
||||
|
|
@ -88,14 +89,16 @@ pub fn handle_event(self: *Type, _: tp.pid_ref, m: tp.message) tp.result {
|
|||
try m.match(.{ "E", "close" }))
|
||||
{
|
||||
const editor = tui.get_active_editor() orelse return;
|
||||
if (!self.value.cursor.eql(editor.get_primary().cursor))
|
||||
tp.self_pid().send(.{ "cmd", "exit_overlay_mode" }) catch |e| self.logger.err(module_name, e);
|
||||
if (!self.value.cursor.eql(editor.get_primary().cursor) or !self.value.view.eql(editor.view)) {
|
||||
tp.self_pid().send(.{ "cmd", "palette_menu_cancel" }) catch |e| self.logger.err(module_name, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn initial_query(self: *Type, allocator: std.mem.Allocator) error{OutOfMemory}![]const u8 {
|
||||
const editor = tui.get_active_editor() orelse return allocator.dupe(u8, "");
|
||||
self.value.cursor = editor.get_primary().cursor;
|
||||
self.value.view = editor.view;
|
||||
return if (self.value.replace) |replace| blk: {
|
||||
const sel: Buffer.Selection = .{ .begin = replace.begin, .end = self.value.start.cursor };
|
||||
break :blk editor.get_selection(sel, allocator) catch break :blk allocator.dupe(u8, "");
|
||||
|
|
@ -107,13 +110,20 @@ pub fn update_query(self: *Type, query: []const u8) void {
|
|||
const primary = editor.get_primary();
|
||||
primary.selection = get_insert_selection(self, editor.get_primary().cursor);
|
||||
const b = editor.buf_for_update() catch return;
|
||||
const root_ = editor.insert(b.root, primary, query, b.allocator) catch return;
|
||||
const root_ = if (query.len > 0)
|
||||
editor.insert(b.root, primary, query, b.allocator) catch return
|
||||
else
|
||||
editor.delete_selection(b.root, primary, b.allocator) catch return;
|
||||
self.value.cursor = editor.get_primary().cursor;
|
||||
if (self.value.replace) |*sel| sel.* = .{ .begin = sel.begin, .end = self.value.cursor };
|
||||
primary.selection = null;
|
||||
editor.update_buf(root_) catch {};
|
||||
editor.clamp();
|
||||
editor.need_render();
|
||||
if (query.len > 0) {
|
||||
const last_char = query[query.len - 1];
|
||||
editor.run_triggers(primary, last_char, .insert);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -275,6 +285,8 @@ fn select(menu: **Type.MenuType, button: *Type.ButtonType, _: Type.Pos) void {
|
|||
2 => editor.insert_snippet(text) catch |e| self.logger.err(module_name, e),
|
||||
else => editor.insert_cursels(text) catch |e| self.logger.err(module_name, e),
|
||||
}
|
||||
self.value.cursor = editor.get_primary().cursor;
|
||||
self.value.view = editor.view;
|
||||
const mv = tui.mainview() orelse return;
|
||||
mv.cancel_info_content() catch {};
|
||||
tp.self_pid().send(.{ "cmd", "exit_overlay_mode" }) catch |e| self.logger.err(module_name, e);
|
||||
|
|
@ -295,6 +307,8 @@ pub fn updated(self: *Type, button_: ?*Type.ButtonType) !void {
|
|||
}
|
||||
try mv.set_info_content(" ", .append); // blank line
|
||||
try mv.set_info_content(values.documentation, .append);
|
||||
if (mv.get_active_editor()) |editor|
|
||||
self.value.view = editor.view;
|
||||
}
|
||||
|
||||
pub fn cancel(_: *Type) !void {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ pub const Placement = enum {
|
|||
pub fn Create(options: type) type {
|
||||
return struct {
|
||||
allocator: std.mem.Allocator,
|
||||
modal: *ModalBackground.State(*Self),
|
||||
menu: *Menu.State(*Self),
|
||||
mode: keybind.Mode,
|
||||
query: std.ArrayList(u8),
|
||||
|
|
@ -68,17 +67,6 @@ pub fn Create(options: type) type {
|
|||
errdefer allocator.destroy(self);
|
||||
self.* = .{
|
||||
.allocator = allocator,
|
||||
.modal = try ModalBackground.create(*Self, allocator, tui.mainview_widget(), .{
|
||||
.ctx = self,
|
||||
.on_click = mouse_palette_menu_cancel,
|
||||
.on_render = if (@hasDecl(options, "modal_dim"))
|
||||
if (options.modal_dim)
|
||||
ModalBackground.Options(*Self).on_render_dim
|
||||
else
|
||||
ModalBackground.Options(*Self).on_render_default
|
||||
else
|
||||
ModalBackground.Options(*Self).on_render_dim,
|
||||
}),
|
||||
.menu = try Menu.create(*Self, allocator, tui.plane(), .{
|
||||
.ctx = self,
|
||||
.style = widget_type,
|
||||
|
|
@ -93,7 +81,10 @@ pub fn Create(options: type) type {
|
|||
.query = .empty,
|
||||
.view_rows = get_view_rows(tui.screen()),
|
||||
.entries = .empty,
|
||||
.mode = try keybind.mode("overlay/dropdown", allocator, .{
|
||||
.mode = try keybind.mode(switch (tui.config().dropdown_keybinds) {
|
||||
.standard => "overlay/dropdown",
|
||||
.noninvasive => "overlay/dropdown-noninvasive",
|
||||
}, allocator, .{
|
||||
.insert_command = "overlay_insert_bytes",
|
||||
}),
|
||||
.placement = if (@hasDecl(options, "placement")) options.placement else .top_center,
|
||||
|
|
@ -114,7 +105,6 @@ pub fn Create(options: type) type {
|
|||
try self.query.appendSlice(self.allocator, initial_query);
|
||||
}
|
||||
try self.start_query(0);
|
||||
try mv.floating_views.add(self.modal.widget());
|
||||
try mv.floating_views.add(self.menu.container_widget);
|
||||
|
||||
if (@hasDecl(options, "handle_event")) blk: {
|
||||
|
|
@ -133,10 +123,8 @@ pub fn Create(options: type) type {
|
|||
if (@hasDecl(options, "deinit"))
|
||||
options.deinit(self);
|
||||
self.entries.deinit(self.allocator);
|
||||
if (tui.mainview()) |mv| {
|
||||
if (tui.mainview()) |mv|
|
||||
mv.floating_views.remove(self.menu.container_widget);
|
||||
mv.floating_views.remove(self.modal.widget());
|
||||
}
|
||||
self.logger.deinit();
|
||||
self.allocator.destroy(self);
|
||||
}
|
||||
|
|
@ -365,13 +353,33 @@ pub fn Create(options: type) type {
|
|||
return matches.items.len;
|
||||
}
|
||||
|
||||
fn delete_word(self: *Self) !void {
|
||||
if (std.mem.lastIndexOfAny(u8, self.query.items, "/\\. -_")) |pos| {
|
||||
self.query.shrinkRetainingCapacity(pos);
|
||||
} else {
|
||||
self.query.shrinkRetainingCapacity(0);
|
||||
}
|
||||
if (@hasDecl(options, "update_query"))
|
||||
options.update_query(self, self.query.items);
|
||||
return self.start_query(0);
|
||||
}
|
||||
|
||||
fn delete_code_point(self: *Self) !void {
|
||||
if (self.query.items.len > 0) {
|
||||
self.query.shrinkRetainingCapacity(self.query.items.len - tui.egc_last(self.query.items).len);
|
||||
if (@hasDecl(options, "update_query"))
|
||||
options.update_query(self, self.query.items);
|
||||
}
|
||||
try self.start_query(0);
|
||||
}
|
||||
|
||||
fn insert_code_point(self: *Self, c: u32) !void {
|
||||
var buf: [6]u8 = undefined;
|
||||
const bytes = try input.ucs32_to_utf8(&[_]u32{c}, &buf);
|
||||
try self.query.appendSlice(self.allocator, buf[0..bytes]);
|
||||
if (@hasDecl(options, "update_query"))
|
||||
options.update_query(self, self.query.items);
|
||||
std.log.debug("insert_code_point: '{s}'", .{self.query.items});
|
||||
// std.log.debug("insert_code_point: '{s}'", .{self.query.items});
|
||||
return self.start_query(0);
|
||||
}
|
||||
|
||||
|
|
@ -379,7 +387,7 @@ pub fn Create(options: type) type {
|
|||
try self.query.appendSlice(self.allocator, bytes);
|
||||
if (@hasDecl(options, "update_query"))
|
||||
options.update_query(self, self.query.items);
|
||||
std.log.debug("insert_bytes: '{s}'", .{self.query.items});
|
||||
// std.log.debug("insert_bytes: '{s}'", .{self.query.items});
|
||||
return self.start_query(0);
|
||||
}
|
||||
|
||||
|
|
@ -425,7 +433,7 @@ pub fn Create(options: type) type {
|
|||
|
||||
pub fn palette_menu_down(self: *Self, _: Ctx) Result {
|
||||
if (self.menu.selected) |selected| {
|
||||
if (selected == self.view_rows - 1 and
|
||||
if (selected == self.view_rows -| 1 and
|
||||
self.view_pos + self.view_rows < self.total_items)
|
||||
{
|
||||
self.view_pos += 1;
|
||||
|
|
@ -535,6 +543,16 @@ pub fn Create(options: type) type {
|
|||
}
|
||||
pub const palette_menu_cancel_meta: Meta = .{};
|
||||
|
||||
pub fn overlay_delete_word_left(self: *Self, _: Ctx) Result {
|
||||
self.delete_word() catch |e| return tp.exit_error(e, @errorReturnTrace());
|
||||
}
|
||||
pub const overlay_delete_word_left_meta: Meta = .{ .description = "Delete word to the left" };
|
||||
|
||||
pub fn overlay_delete_backwards(self: *Self, _: Ctx) Result {
|
||||
self.delete_code_point() catch |e| return tp.exit_error(e, @errorReturnTrace());
|
||||
}
|
||||
pub const overlay_delete_backwards_meta: Meta = .{ .description = "Delete backwards" };
|
||||
|
||||
pub fn overlay_insert_code_point(self: *Self, ctx: Ctx) Result {
|
||||
var egc: u32 = 0;
|
||||
if (!try ctx.args.match(.{tp.extract(&egc)}))
|
||||
|
|
|
|||
|
|
@ -416,7 +416,7 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void {
|
|||
if (try m.match(.{ "cmd", tp.extract(&cmd) }))
|
||||
return command.executeName(cmd, ctx) catch |e| self.logger.err(cmd, e);
|
||||
if (try m.match(.{ "cmd", tp.extract(&cmd_id) }))
|
||||
return command.execute(cmd_id, ctx) catch |e| self.logger.err("command", e);
|
||||
return command.execute(cmd_id, command.get_name(cmd_id) orelse "(unknown)", ctx) catch |e| self.logger.err("command", e);
|
||||
|
||||
var arg: []const u8 = undefined;
|
||||
|
||||
|
|
@ -426,7 +426,7 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void {
|
|||
}
|
||||
if (try m.match(.{ "cmd", tp.extract(&cmd_id), tp.extract_cbor(&arg) })) {
|
||||
ctx.args = .{ .buf = arg };
|
||||
return command.execute(cmd_id, ctx) catch |e| self.logger.err("command", e);
|
||||
return command.execute(cmd_id, command.get_name(cmd_id) orelse "(unknown)", ctx) catch |e| self.logger.err("command", e);
|
||||
}
|
||||
if (try m.match(.{"quit"})) {
|
||||
project_manager.shutdown();
|
||||
|
|
@ -736,7 +736,7 @@ fn dispatch_event(ctx: *anyopaque, cbor_msg: []const u8) void {
|
|||
|
||||
fn handle_system_clipboard(self: *Self, text: []const u8) !void {
|
||||
if (command.get_id("mini_mode_paste")) |id|
|
||||
return command.execute(id, command.fmt(.{text}));
|
||||
return command.execute(id, "mini_mode_paste", command.fmt(.{text}));
|
||||
|
||||
{
|
||||
const text_ = try clipboard_system_clipboard_text(self.allocator);
|
||||
|
|
@ -1127,6 +1127,11 @@ const cmds = struct {
|
|||
}
|
||||
pub const toggle_keybind_hints_meta: Meta = .{ .description = "Toggle keybind hints" };
|
||||
|
||||
pub fn toggle_command_logging(_: *Self, _: Ctx) Result {
|
||||
command.log_execute = !command.log_execute;
|
||||
}
|
||||
pub const toggle_command_logging_meta: Meta = .{ .description = "Toggle logging of executed commands" };
|
||||
|
||||
pub fn scroll_keybind_hints(_: *Self, _: Ctx) Result {
|
||||
@import("keyhints.zig").scroll();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue