Compare commits

...

8 commits

5 changed files with 83 additions and 20 deletions

View file

@ -1012,6 +1012,7 @@ fn send_completion_items(to: tp.pid_ref, file_path: []const u8, row: usize, col:
if (!(try cbor.matchValue(&iter, cbor.extract_cbor(&item)))) return error.InvalidMessageField;
try send_completion_item(to, file_path, row, col, item, if (len > 1) true else is_incomplete);
}
return to.send(.{ "cmd", "add_completion_done", .{ file_path, row, col } }) catch error.ClientFailed;
}
fn invalid_field(field: []const u8) error{InvalidMessage} {

View file

@ -838,11 +838,8 @@ const cmds = struct {
tp.more,
})) return error.InvalidAddDiagnosticArgument;
file_path = project_manager.normalize_file_path(file_path);
if (self.get_active_editor()) |editor| {
if (std.mem.eql(u8, file_path, editor.file_path orelse ""))
try editor.add_completion(row, col, is_incomplete, ctx.args);
try tui.open_overlay(@import("mode/overlay/completion_palette.zig").Type);
}
if (self.get_active_editor()) |editor| if (std.mem.eql(u8, file_path, editor.file_path orelse ""))
try editor.add_completion(row, col, is_incomplete, ctx.args);
}
pub const add_completion_meta: Meta = .{
.arguments = &.{
@ -871,6 +868,28 @@ const cmds = struct {
},
};
pub fn add_completion_done(self: *Self, ctx: Ctx) Result {
var file_path: []const u8 = undefined;
var row: usize = undefined;
var col: usize = undefined;
if (!try ctx.args.match(.{
tp.extract(&file_path),
tp.extract(&row),
tp.extract(&col),
})) return error.InvalidAddDiagnosticArgument;
file_path = project_manager.normalize_file_path(file_path);
if (self.get_active_editor()) |editor| if (std.mem.eql(u8, file_path, editor.file_path orelse ""))
try tui.open_overlay(@import("mode/overlay/completion_palette.zig").Type);
}
pub const add_completion_done_meta: Meta = .{
.arguments = &.{
.string, // file_path
.integer, // row
.integer, // col
},
};
pub fn rename_symbol_item(self: *Self, ctx: Ctx) Result {
const editor = self.get_active_editor() orelse return;
// because the incoming message is an array of Renames, we manuallly

View file

@ -3,9 +3,11 @@ const cbor = @import("cbor");
const tp = @import("thespian");
const root = @import("root");
const command = @import("command");
const Buffer = @import("Buffer");
const tui = @import("../../tui.zig");
pub const Type = @import("palette.zig").Create(@This());
const ed = @import("../../editor.zig");
const module_name = @typeName(@This());
const Widget = @import("../../Widget.zig");
@ -20,8 +22,15 @@ pub const Entry = struct {
cbor: []const u8,
};
pub const ValueType = struct {
start: ed.CurSel = .{},
replace: ?Buffer.Selection = null,
};
pub const defaultValue: ValueType = .{};
pub fn load_entries(palette: *Type) !usize {
const editor = tui.get_active_editor() orelse return error.NotFound;
palette.value.start = editor.get_primary().*;
var iter: []const u8 = editor.completions.items;
while (iter.len > 0) {
var cbor_item: []const u8 = undefined;
@ -31,7 +40,9 @@ pub fn load_entries(palette: *Type) !usize {
var max_label_len: usize = 0;
for (palette.entries.items) |*item| {
const label_, const sort_text, _ = get_values(item.cbor);
const label_, const sort_text, _, const replace = get_values(item.cbor);
if (palette.value.replace == null)
palette.value.replace = replace;
item.label = label_;
item.sort_text = sort_text;
max_label_len = @max(max_label_len, item.label.len);
@ -49,6 +60,14 @@ pub fn load_entries(palette: *Type) !usize {
return if (max_label_len > label.len + 3) 0 else label.len + 3 - max_label_len;
}
pub fn initial_query(palette: *Type, allocator: std.mem.Allocator) error{OutOfMemory}![]const u8 {
return if (palette.value.replace) |replace| blk: {
const editor = tui.get_active_editor() orelse break :blk allocator.dupe(u8, "");
const sel: Buffer.Selection = .{ .begin = replace.begin, .end = palette.value.start.cursor };
break :blk editor.get_selection(sel, allocator) catch break :blk allocator.dupe(u8, "");
} else allocator.dupe(u8, "");
}
pub fn clear_entries(palette: *Type) void {
palette.entries.clearRetainingCapacity();
}
@ -71,7 +90,7 @@ pub fn on_render_menu(_: *Type, button: *Type.ButtonState, theme: *const Widget.
if (!(cbor.matchValue(&iter, cbor.extract_cbor(&item_cbor)) catch false)) return false;
if (!(cbor.matchValue(&iter, cbor.extract_cbor(&matches_cbor)) catch false)) return false;
const label_, _, const kind = get_values(item_cbor);
const label_, _, const kind, _ = get_values(item_cbor);
const icon_: []const u8 = kind_icon(@enumFromInt(kind));
const color: u24 = 0x0;
const indicator: []const u8 = &.{};
@ -79,10 +98,11 @@ pub fn on_render_menu(_: *Type, button: *Type.ButtonState, theme: *const Widget.
return tui.render_file_item(&button.plane, label_, icon_, color, indicator, matches_cbor, button.active, selected, button.hover, theme);
}
fn get_values(item_cbor: []const u8) struct { []const u8, []const u8, u8 } {
fn get_values(item_cbor: []const u8) struct { []const u8, []const u8, u8, Buffer.Selection } {
var label_: []const u8 = "";
var sort_text: []const u8 = "";
var kind: u8 = 0;
var replace: Buffer.Selection = .{};
_ = cbor.match(item_cbor, .{
cbor.any, // file_path
cbor.any, // row
@ -102,20 +122,32 @@ fn get_values(item_cbor: []const u8) struct { []const u8, []const u8, u8 } {
cbor.any, // insert.begin.col
cbor.any, // insert.end.row
cbor.any, // insert.end.col
cbor.any, // replace.begin.row
cbor.any, // replace.begin.col
cbor.any, // replace.end.row
cbor.any, // replace.end.col
cbor.extract(&replace.begin.row), // replace.begin.row
cbor.extract(&replace.begin.col), // replace.begin.col
cbor.extract(&replace.end.row), // replace.end.row
cbor.extract(&replace.end.col), // replace.end.col
}) catch false;
return .{ label_, sort_text, kind };
return .{ label_, sort_text, kind, replace };
}
fn select(menu: **Type.MenuState, button: *Type.ButtonState) void {
const label_, _, _ = get_values(button.opts.label);
const label_, _, _, _ = get_values(button.opts.label);
tp.self_pid().send(.{ "cmd", "exit_overlay_mode" }) catch |e| menu.*.opts.ctx.logger.err(module_name, e);
tp.self_pid().send(.{ "cmd", "insert_chars", .{label_} }) catch |e| menu.*.opts.ctx.logger.err(module_name, e);
}
pub fn updated(palette: *Type, button_: ?*Type.ButtonState) !void {
const button = button_ orelse return cancel(palette);
_, _, _, const replace = get_values(button.opts.label);
const editor = tui.get_active_editor() orelse return error.NotFound;
editor.get_primary().selection = if (replace.empty()) null else replace;
}
pub fn cancel(palette: *Type) !void {
const editor = tui.get_active_editor() orelse return;
editor.get_primary().selection = palette.value.start.selection;
}
const CompletionItemKind = enum(u8) {
None = 0,
Text = 1,

View file

@ -40,8 +40,11 @@ pub fn Create(options: type) type {
view_pos: usize = 0,
total_items: usize = 0,
value: ValueType = if (@hasDecl(options, "defaultValue")) options.defaultValue else {},
const Entry = options.Entry;
const Self = @This();
const ValueType = if (@hasDecl(options, "ValueType")) options.ValueType else void;
pub const MenuState = Menu.State(*Self);
pub const ButtonState = Button.State(*Menu.State(*Self));
@ -88,6 +91,12 @@ pub fn Create(options: type) type {
if (@hasDecl(options, "restore_state"))
options.restore_state(self) catch {};
try self.commands.init(self);
if (@hasDecl(options, "initial_query")) blk: {
const initial_query = options.initial_query(self, self.allocator) catch break :blk;
defer self.allocator.free(initial_query);
try self.inputbox.text.appendSlice(self.allocator, initial_query);
self.inputbox.cursor = tui.egc_chunk_width(self.inputbox.text.items, 0, 8);
}
try self.start_query(0);
try mv.floating_views.add(self.modal.widget());
try mv.floating_views.add(self.menu.container_widget);

View file

@ -189,7 +189,7 @@ fn init(allocator: Allocator) InitError!*Self {
}
self.mainview_ = try MainView.create(allocator);
resize();
self.set_terminal_style();
self.set_terminal_style(self.current_theme());
try save_config();
try self.init_input_namespace();
if (tp.env.get().is("restore-session")) {
@ -773,7 +773,7 @@ fn set_theme_by_name(self: *Self, name: []const u8, action: enum { none, store }
self.light_parsed_theme = parsed_theme;
},
}
self.set_terminal_style();
self.set_terminal_style(&theme_);
self.logger.print("theme: {s}", .{theme_.description});
switch (action) {
.none => {},
@ -790,12 +790,14 @@ fn set_theme_by_name(self: *Self, name: []const u8, action: enum { none, store }
fn force_color_scheme(self: *Self, color_scheme: @TypeOf(self.color_scheme)) void {
self.color_scheme = color_scheme;
self.color_scheme_locked = true;
self.set_terminal_style(self.current_theme());
self.logger.print("color scheme: {s} ({s})", .{ @tagName(self.color_scheme), self.current_theme().name });
}
fn set_color_scheme(self: *Self, color_scheme: @TypeOf(self.color_scheme)) void {
if (self.color_scheme_locked) return;
self.color_scheme = color_scheme;
self.set_terminal_style(self.current_theme());
self.logger.print("color scheme: {s} ({s})", .{ @tagName(self.color_scheme), self.current_theme().name });
}
@ -1548,12 +1550,12 @@ pub const fallbacks: []const FallBack = &[_]FallBack{
.{ .ts = "text.title", .tm = "entity.name.section" },
};
fn set_terminal_style(self: *Self) void {
fn set_terminal_style(self: *Self, theme_: *const Widget.Theme) void {
if (build_options.gui or self.config_.enable_terminal_color_scheme) {
self.rdr_.set_terminal_style(self.current_theme().editor);
self.rdr_.set_terminal_cursor_color(self.current_theme().editor_cursor.bg.?);
self.rdr_.set_terminal_style(theme_.editor);
self.rdr_.set_terminal_cursor_color(theme_.editor_cursor.bg.?);
if (self.rdr_.vx.caps.multi_cursor)
self.rdr_.set_terminal_secondary_cursor_color(self.current_theme().editor_cursor_secondary.bg orelse self.current_theme().editor_cursor.bg.?);
self.rdr_.set_terminal_secondary_cursor_color(theme_.editor_cursor_secondary.bg orelse theme_.editor_cursor.bg.?);
}
}