diff --git a/src/config.zig b/src/config.zig index 571cb70..c02ddf4 100644 --- a/src/config.zig +++ b/src/config.zig @@ -24,6 +24,7 @@ inline_vcs_blame_alignment: Alignment = .right, animation_min_lag: usize = 0, //milliseconds animation_max_lag: usize = 50, //milliseconds hover_time_ms: usize = 500, //milliseconds +hover_info_mode: HoverInfoMode = .box, input_idle_time_ms: usize = 100, //milliseconds idle_actions: []const IdleAction = &default_actions, idle_commands: ?[]const []const u8 = null, // a list of simple commands @@ -64,6 +65,7 @@ start_debugger_on_crash: bool = false, completion_trigger: CompletionTrigger = .automatic, completion_style: CompletionStyle = .dropdown, completion_insert_mode: CompletionInsertMode = .insert, +completion_info_mode: CompletionInfoMode = .box, widget_style: WidgetStyle = .compact, palette_style: WidgetStyle = .bars_top_bottom, @@ -74,6 +76,7 @@ pane_left_style: WidgetStyle = .bar_right, pane_right_style: WidgetStyle = .bar_left, pane_style: PaneStyle = .panel, hint_window_style: WidgetStyle = .thick_boxed, +info_box_style: WidgetStyle = .bar_left_spacious, centered_view: bool = false, centered_view_width: usize = 145, @@ -119,6 +122,7 @@ pub const WidgetType = enum { pane_left, pane_right, hint_window, + info_box, dropdown, }; @@ -135,6 +139,8 @@ pub const WidgetStyle = enum { single_double_top_bottom_boxed, single_double_left_right_boxed, boxed, + bar_left_spacious, + bar_right_spacious, spacious, compact, }; @@ -204,6 +210,17 @@ pub const CompletionInsertMode = enum { replace, }; +pub const CompletionInfoMode = enum { + none, + box, + panel, +}; + +pub const HoverInfoMode = enum { + box, + panel, +}; + pub const Alignment = enum { left, right, diff --git a/src/keybind/builtin/flow.json b/src/keybind/builtin/flow.json index 9f91d7c..411d821 100644 --- a/src/keybind/builtin/flow.json +++ b/src/keybind/builtin/flow.json @@ -396,7 +396,9 @@ "overlay/dropdown-noninvasive": { "inherit": "normal", "press": [ + ["ctrl+space", "toggle_completion_info_mode"], ["alt+f9", "dropdown_next_widget_style"], + ["shift+alt+f9", "info_box_next_widget_style"], ["ctrl+p", "palette_menu_up"], ["ctrl+n", "palette_menu_down"], ["escape", "palette_menu_cancel"], @@ -409,7 +411,9 @@ "overlay/dropdown": { "inherit": "normal", "press": [ + ["ctrl+space", "toggle_completion_info_mode"], ["alt+f9", "dropdown_next_widget_style"], + ["shift+alt+f9", "info_box_next_widget_style"], ["ctrl+p", "palette_menu_up"], ["ctrl+n", "palette_menu_down"], ["escape", "palette_menu_cancel"], diff --git a/src/tui/WidgetStyle.zig b/src/tui/WidgetStyle.zig index 00a447b..66dad51 100644 --- a/src/tui/WidgetStyle.zig +++ b/src/tui/WidgetStyle.zig @@ -61,6 +61,8 @@ pub const Border = struct { const @"thick box (half)": Border = .{ .nw = "▛", .n = "▀", .ne = "▜", .e = "▐", .se = "▟", .s = "▄", .sw = "▙", .w = "▌", .nib = "▌", .nie = "▐", .sib = "▌", .sie = "▐" }; const @"thick box (sextant)": Border = .{ .nw = "🬕", .n = "🬂", .ne = "🬨", .e = "▐", .se = "🬷", .s = "🬭", .sw = "🬲", .w = "▌", .nib = "▌", .nie = "▐", .sib = "▌", .sie = "▐" }; const @"thick box (octant)": Border = .{ .nw = "𜵊", .n = "🮂", .ne = "𜶘", .e = "▐", .se = "𜷕", .s = "▂", .sw = "𜷀", .w = "▌", .nib = "▌", .nie = "▐", .sib = "▌", .sie = "▐" }; + const @"thick bar left (octant)": Border = .{ .nw = "▌", .n = " ", .ne = " ", .e = " ", .se = " ", .s = " ", .sw = "▌", .w = "▌", .nib = " ", .nie = " ", .sib = " ", .sie = " " }; + const @"thick bar right (octant)": Border = .{ .nw = " ", .n = " ", .ne = "▐", .e = "▐", .se = "▐", .s = " ", .sw = " ", .w = " ", .nib = " ", .nie = " ", .sib = " ", .sie = " " }; const @"extra thick box": Border = .{ .nw = "█", .n = "▀", .ne = "█", .e = "█", .se = "█", .s = "▄", .sw = "█", .w = "█", .nib = "▌", .nie = "▐", .sib = "▌", .sie = "▐" }; const @"round thick box": Border = .{ .nw = "█", .n = "▀", .ne = "█", .e = "█", .se = "█", .s = "▄", .sw = "█", .w = "█", .nib = "▌", .nie = "▐", .sib = "▌", .sie = "▐" }; }; @@ -132,6 +134,16 @@ const bar_right: @This() = .{ .border = Border.@"thick box (octant)", }; +const bar_left_spacious: @This() = .{ + .padding = Margin.@"1/2", + .border = Border.@"thick bar left (octant)", +}; + +const bar_right_spacious: @This() = .{ + .padding = Margin.@"1/2", + .border = Border.@"thick bar right (octant)", +}; + pub fn from_tag(tag: WidgetStyle) *const @This() { return switch (tag) { .compact => &compact, @@ -148,6 +160,8 @@ pub fn from_tag(tag: WidgetStyle) *const @This() { .bars_left_right => &bars_left_right, .bar_left => &bar_left, .bar_right => &bar_right, + .bar_left_spacious => &bar_left_spacious, + .bar_right_spacious => &bar_right_spacious, }; } @@ -169,6 +183,7 @@ pub fn theme_style_from_type(style_type: WidgetType, theme: *const Theme) Theme. .editor => .{ .fg = theme.editor_widget.bg, .bg = theme.editor.bg }, }, .hint_window => .{ .fg = theme.editor_widget_border.fg, .bg = theme.editor_widget.bg }, + .info_box => .{ .fg = theme.editor_widget_border.fg, .bg = theme.editor_widget.bg }, }; } diff --git a/src/tui/editor.zig b/src/tui/editor.zig index cfb8890..1b686eb 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -28,6 +28,7 @@ const WidgetList = @import("WidgetList.zig"); const tui = @import("tui.zig"); const IndentMode = @import("config").IndentMode; const WhitespaceMode = @import("config").WhitespaceMode; +const info_view = @import("info_view.zig"); pub const Cursor = Buffer.Cursor; pub const View = Buffer.View; @@ -414,6 +415,7 @@ pub const Editor = struct { diag_warnings: usize = 0, diag_info: usize = 0, diag_hints: usize = 0, + info_box: ?Widget = null, completions: CompletionState = .empty, completions_request: ?CompletionState = .done, @@ -643,6 +645,7 @@ pub const Editor = struct { var meta: std.Io.Writer.Allocating = .init(self.allocator); defer meta.deinit(); if (self.buffer) |_| self.write_state(&meta.writer) catch {}; + if (self.info_box) |*w| w.deinit(self.allocator); for (self.diagnostics.items) |*d| d.deinit(self.allocator); self.diagnostics.deinit(self.allocator); self.completions.deinit(self.allocator); @@ -1294,6 +1297,7 @@ pub const Editor = struct { self.render_blame(theme, hl_row, ctx_.cell_map) catch {}; self.render_column_highlights() catch {}; self.render_cursors(theme, ctx_.cell_map, focused) catch {}; + if (self.info_box) |w| _ = w.render(theme); } fn render_cursors(self: *Self, theme: *const Widget.Theme, cell_map: CellMap, focused: bool) !void { @@ -1919,8 +1923,10 @@ pub const Editor = struct { self.last.cursels = self.cursels.items.len; } - if (lines != self.last.lines or !primary.cursor.eql(self.last.primary.cursor)) + if (lines != self.last.lines or !primary.cursor.eql(self.last.primary.cursor)) { + self.clear_info_box(); try self.send_editor_pos(lines, &primary.cursor); + } if (primary.selection) |primary_selection_| { var primary_selection = primary_selection_; @@ -1934,8 +1940,10 @@ pub const Editor = struct { } else if (self.last.primary.selection) |_| try self.send_editor_selection_removed(); - if (lines != self.last.lines or !self.view.eql(self.last.view)) + if (lines != self.last.lines or !self.view.eql(self.last.view)) { + self.clear_info_box(); try self.send_editor_view(lines, self.view); + } self.last.view = self.view; self.last.lines = lines; @@ -2044,6 +2052,56 @@ pub const Editor = struct { _ = try self.handlers.msg(.{ "E", "eol_mode", eol_mode, utf8_sanitized, indent_mode }); } + fn clear_info_box(self: *Self) void { + if (self.info_box) |*w| { + w.deinit(self.allocator); + self.info_box = null; + self.clear_matches(); + } + } + + const info_box_widget_type: Widget.Type = .info_box; + fn show_info_box(self: *Self) !*info_view { + const w = self.info_box orelse blk: { + self.info_box = try info_view.create_widget_type(self.allocator, self.plane, info_box_widget_type); + break :blk self.info_box.?; + }; + const info_ = if (w.get(@typeName(info_view))) |w_| w_.dynamic_cast(info_view) orelse null else null; + const info = info_ orelse @panic("Editor.show_info_box"); + return info; + } + + fn place_info_box(self: *Self, cursor: Cursor) void { + const w = self.info_box orelse return; + const info = self.show_info_box() catch return; + const padding = tui.get_widget_style(info_box_widget_type).padding; + const dim = info.content_size(); + const pos = self.screen_cursor(&cursor) orelse { + self.clear_info_box(); + return; + }; + const y, const x = self.plane.rel_yx_to_abs(@intCast(pos.row), @intCast(pos.col)); + + w.resize(.{ + .h = dim.rows + padding.top + padding.bottom, + .w = dim.cols + padding.left + padding.right + 3, + .y = @intCast(y + 1), + .x = @intCast(x + 1), + }); + } + + pub fn set_hover_content(self: *Self, range: Match, content: []const u8) !void { + self.add_hover_highlight(range); + if (content.len == 0) { + self.clear_info_box(); + return; + } + const info = try self.show_info_box(); + info.clear(); + try info.append_content(content); + self.place_info_box(range.begin); + } + fn clamp_abs_offset(self: *Self, abs: bool, offset: usize) void { var dest: View = self.view; dest.clamp_offset(&self.get_primary().cursor, abs, offset); @@ -4464,6 +4522,7 @@ pub const Editor = struct { pub const move_buffer_end_meta: Meta = .{ .description = "Move cursor to end of file" }; pub fn cancel(self: *Self, _: Context) Result { + self.clear_info_box(); self.cancel_all_tabstops(); self.cancel_all_selections(); self.cancel_all_matches(); diff --git a/src/tui/info_view.zig b/src/tui/info_view.zig index e9635b3..c3ee40b 100644 --- a/src/tui/info_view.zig +++ b/src/tui/info_view.zig @@ -14,9 +14,13 @@ plane: Plane, view_rows: usize = 0, lines: std.ArrayList([]const u8), -const widget_type: Widget.Type = .panel; +const default_widget_type: Widget.Type = .panel; pub fn create(allocator: Allocator, parent: Plane) !Widget { + return create_widget_type(allocator, parent, default_widget_type); +} + +pub fn create_widget_type(allocator: Allocator, parent: Plane, widget_type: Widget.Type) !Widget { const self = try allocator.create(Self); errdefer allocator.destroy(self); const container = try WidgetList.createHStyled(allocator, parent, "panel_frame", .dynamic, widget_type); @@ -51,8 +55,9 @@ pub fn handle_resize(self: *Self, pos: Widget.Box) void { pub fn append_content(self: *Self, content: []const u8) !void { var iter = std.mem.splitScalar(u8, content, '\n'); - while (iter.next()) |line| + while (iter.next()) |line| if (line.len > 0) { (try self.lines.addOne(self.allocator)).* = try self.allocator.dupe(u8, line); + }; } pub fn set_content(self: *Self, content: []const u8) !void { @@ -60,6 +65,12 @@ pub fn set_content(self: *Self, content: []const u8) !void { return self.append_content(content); } +pub fn content_size(self: *Self) struct { rows: usize, cols: usize } { + var cols: usize = 0; + for (self.lines.items) |line| cols = @max(cols, line.len); + return .{ .rows = self.lines.items.len, .cols = cols }; +} + pub fn render(self: *Self, theme: *const Widget.Theme) bool { self.plane.set_base_style(theme.panel); self.plane.erase(); diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index 5cdf21c..c610989 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -175,12 +175,14 @@ pub fn receive(self: *Self, from_: tp.pid_ref, m: tp.message) error{Exit}!bool { if (self.get_editor_for_file(path)) |editor| editor.done_highlight_reference(); return true; } else if (try m.match(.{ "hover", tp.extract(&path), tp.string, tp.extract(&lines), tp.extract(&begin_line), tp.extract(&begin_pos), tp.extract(&end_line), tp.extract(&end_pos) })) { - try self.set_info_content(lines, .replace); - if (self.get_editor_for_file(path)) |editor| - editor.add_hover_highlight(.{ - .begin = .{ .row = begin_line, .col = begin_pos }, - .end = .{ .row = end_line, .col = end_pos }, - }); + switch (tui.config().hover_info_mode) { + .panel => self.set_info_content(lines, .replace) catch {}, + .box => if (self.get_editor_for_file(path)) |editor| + editor.set_hover_content(.{ + .begin = .{ .row = begin_line, .col = begin_pos }, + .end = .{ .row = end_line, .col = end_pos }, + }, lines) catch |e| return tp.exit_error(e, @errorReturnTrace()), + } return true; } else if (try m.match(.{ "navigate_complete", tp.extract(&path), tp.extract(&goto_args), tp.extract(&line), tp.extract(&column) })) { cmds.navigate_complete(self, null, path, goto_args, line, column, null) catch |e| return tp.exit_error(e, @errorReturnTrace()); @@ -339,6 +341,10 @@ fn close_all_panel_views(self: *Self) void { tui.resize(); } +pub fn hide_info_view_panel(self: *Self) void { + self.toggle_panel_view(info_view, .disable) catch {}; +} + fn check_all_not_dirty(self: *const Self) command.Result { if (self.buffer_manager.is_dirty()) return tp.exit("unsaved changes"); diff --git a/src/tui/mode/overlay/completion_dropdown.zig b/src/tui/mode/overlay/completion_dropdown.zig index c3e3bee..d9beb30 100644 --- a/src/tui/mode/overlay/completion_dropdown.zig +++ b/src/tui/mode/overlay/completion_dropdown.zig @@ -13,6 +13,7 @@ pub const Type = @import("dropdown.zig").Create(@This()); const ed = @import("../../editor.zig"); const module_name = @typeName(@This()); const Widget = @import("../../Widget.zig"); +const info_view = @import("../../info_view.zig"); pub const label = "Select completion"; pub const name = "completion"; @@ -24,6 +25,8 @@ pub const widget_type: Widget.Type = .dropdown; pub var detail_limit: usize = 40; pub var description_limit: usize = 25; +const info_box_widget_type: Widget.Type = .info_box; + pub const Entry = struct { label: []const u8, sort_text: []const u8, @@ -38,6 +41,7 @@ pub const ValueType = struct { last_query: ?[]const u8 = null, commands: command.Collection(cmds) = undefined, data: []const u8 = &.{}, + info_box: ?Widget = null, }; pub const defaultValue: ValueType = .{}; @@ -97,6 +101,7 @@ pub fn load_entries(self: *Type) !usize { pub fn deinit(self: *Type) void { self.allocator.free(self.value.data); if (self.value.last_query) |p| self.allocator.free(p); + if (self.value.info_box) |w| w.deinit(self.allocator); self.value.commands.deinit(); } @@ -199,6 +204,9 @@ pub fn on_render_menu(self: *Type, button: *Type.ButtonType, theme: *const Widge tui.rdr().cursor_enable(@intCast(cursor.row), @intCast(cursor.col), tui.get_cursor_shape()) catch {}; } + defer if (selected) if (self.value.info_box) |w| { + _ = w.render(theme); + }; return tui.render_symbol( &button.plane, values.label, @@ -217,6 +225,13 @@ pub fn on_render_menu(self: *Type, button: *Type.ButtonType, theme: *const Widge ); } +pub fn after_resize(self: *Type) void { + if (self.value.info_box) |w| { + w.deinit(self.allocator); + self.value.info_box = null; + } +} + pub const Values = struct { label: []const u8, sort_text: []const u8, @@ -351,8 +366,18 @@ fn select(menu: **Type.MenuType, button: *Type.ButtonType, _: Type.Pos) void { pub fn updated(self: *Type, button_: ?*Type.ButtonType) !void { const button = button_ orelse return cancel(self); - const values = get_values(button.opts.label); const mv = tui.mainview() orelse return; + const values = get_values(button.opts.label); + switch (tui.config().completion_info_mode) { + .none => {}, + .box => try show_info_box(self, button, values), + .panel => try show_info_panel(mv, values), + } + if (mv.get_active_editor()) |editor| + self.value.view = editor.view; +} + +fn show_info_panel(mv: anytype, values: Values) !void { try mv.set_info_content(values.label, .replace); try mv.set_info_content(" ", .append); // blank line try mv.set_info_content(values.detail, .append); @@ -364,8 +389,41 @@ 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; +} + +fn show_info_box(self: *Type, button: *Type.ButtonType, values: Values) !void { + const w = self.value.info_box orelse blk: { + self.value.info_box = try info_view.create_widget_type(self.allocator, self.menu.container.plane, info_box_widget_type); + break :blk self.value.info_box.?; + }; + const info_ = if (w.get(@typeName(info_view))) |w_| w_.dynamic_cast(info_view) orelse null else null; + const info = info_ orelse @panic("show_info_box"); + + try info.set_content(values.label); + if (values.detail.len > 0) { + try info.append_content(" "); // blank line + try info.append_content(values.detail); + } + if (builtin.mode == .Debug) { + try info.append_content("newText:"); // blank line + try info.append_content(values.textEdit_newText); + try info.append_content("insertText:"); // blank line + try info.append_content(values.insertText); + } + if (values.documentation.len > 0) { + try info.append_content(" "); // blank line + try info.append_content(values.documentation); + } + + const padding = tui.get_widget_style(info_box_widget_type).padding; + const btn_box = Widget.Box.from(button.plane); + const dim = info.content_size(); + w.resize(.{ + .h = dim.rows + padding.top + padding.bottom, + .w = dim.cols + padding.left + padding.right + 3, + .y = btn_box.y, + .x = btn_box.x + btn_box.w + 1, + }); } pub fn cancel(_: *Type) !void { diff --git a/src/tui/mode/overlay/dropdown.zig b/src/tui/mode/overlay/dropdown.zig index 7f6f1d6..8a38530 100644 --- a/src/tui/mode/overlay/dropdown.zig +++ b/src/tui/mode/overlay/dropdown.zig @@ -71,7 +71,7 @@ pub fn Create(options: type) type { .menu = try Menu.create(*Self, allocator, tui.plane(), .{ .ctx = self, .style = widget_type, - .on_render = if (@hasDecl(options, "on_render_menu")) options.on_render_menu else on_render_menu, + .on_render = options.on_render_menu, .prepare_resize = prepare_resize_menu, .after_resize = after_resize_menu, .on_scroll = EventHandler.bind(self, Self.on_scroll), @@ -145,41 +145,6 @@ pub fn Create(options: type) type { .{ .fg = theme.scrollbar.fg, .bg = theme.editor_widget.bg }; } - fn on_render_menu(_: *Self, button: *ButtonType, theme: *const Widget.Theme, selected: bool) bool { - const style_base = theme.editor_widget; - const style_label = if (button.active) theme.editor_cursor else if (button.hover or selected) theme.editor_selection else theme.editor_widget; - const style_hint = if (tui.find_scope_style(theme, "entity.name")) |sty| sty.style else style_label; - button.plane.set_base_style(style_base); - button.plane.erase(); - button.plane.home(); - button.plane.set_style(style_label); - if (button.active or button.hover or selected) { - button.plane.fill(" "); - button.plane.home(); - } - var label: []const u8 = undefined; - var hint: []const u8 = undefined; - var iter = button.opts.label; // label contains cbor, first the file name, then multiple match indexes - if (!(cbor.matchString(&iter, &label) catch false)) - label = "#ERROR#"; - if (!(cbor.matchString(&iter, &hint) catch false)) - hint = ""; - button.plane.set_style(style_hint); - tui.render_pointer(&button.plane, selected); - button.plane.set_style(style_label); - _ = button.plane.print("{s} ", .{label}) catch {}; - button.plane.set_style(style_hint); - _ = button.plane.print_aligned_right(0, "{s} ", .{hint}) catch {}; - var index: usize = 0; - var len = cbor.decodeArrayHeader(&iter) catch return false; - while (len > 0) : (len -= 1) { - if (cbor.matchValue(&iter, cbor.extract(&index)) catch break) { - tui.render_match_cell(&button.plane, 0, index + 2, theme) catch break; - } else break; - } - return false; - } - fn prepare_resize_menu(self: *Self, menu: *Menu.State(*Self), _: Widget.Box) Widget.Box { const padding = tui.get_widget_style(menu.opts.style).padding; return self.prepare_resize(padding) orelse .{}; @@ -217,6 +182,8 @@ pub fn Create(options: type) type { fn after_resize(self: *Self) void { self.update_scrollbar(); // self.start_query(0) catch {}; + if (@hasDecl(options, "after_resize")) + options.after_resize(self); } fn do_resize(self: *Self, padding: Widget.Style.Margin) void { diff --git a/src/tui/tui.zig b/src/tui/tui.zig index cef9e45..abe8c8a 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -19,6 +19,7 @@ const syntax = @import("syntax"); const Widget = @import("Widget.zig"); const MessageFilter = @import("MessageFilter.zig"); const MainView = @import("mainview.zig"); +const IdleAction = @import("config").IdleAction; // exports for unittesting pub const exports = struct { @@ -86,6 +87,7 @@ color_scheme: enum { dark, light } = .dark, color_scheme_locked: bool = false, hint_mode: HintMode = .prefix, last_palette: ?LastPalette = null, +idle_actions_: std.ArrayList(IdleAction) = .empty, fast_scroll_: bool = false, alt_scroll_: bool = false, @@ -296,6 +298,7 @@ fn deinit(self: *Self) void { m.deinit(); self.delayed_init_input_mode = null; } + self.idle_actions_.deinit(self.allocator); self.commands.deinit(); if (self.mainview_) |*mv| mv.deinit(self.allocator); self.message_filters_.deinit(); @@ -607,6 +610,9 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void { return; } + if (try m.match(.{ "filter", "term", "exited", tp.more })) // drop late filter errors + return; + if (try m.match(.{ "line_number_mode", tp.more })) // drop broadcast messages return; @@ -1019,6 +1025,30 @@ fn current_theme(self: *const Self) *const Widget.Theme { }; } +fn toggle_idle_action(self: *Self, action: IdleAction) !bool { + var enable: bool = true; + + if (self.idle_actions_.items.len == 0) + try self.idle_actions_.appendSlice(self.allocator, self.config_.idle_actions); + + for (self.idle_actions_.items) |item| if (item == action) { + enable = false; + }; + + if (enable) { + (try self.idle_actions_.addOne(self.allocator)).* = action; + } else { + for (self.idle_actions_.items, 0..) |item, idx| if (item == action) { + _ = self.idle_actions_.orderedRemove(idx); + break; + }; + } + + self.config_.idle_actions = self.idle_actions_.items; + try save_config(); + return enable; +} + const cmds = struct { pub const Target = Self; const Ctx = command.Context; @@ -1178,6 +1208,35 @@ const cmds = struct { } pub const toggle_completion_insert_mode_meta: Meta = .{ .description = "Toggle completion insert mode" }; + pub fn toggle_completion_info_mode(self: *Self, _: Ctx) Result { + self.config_.completion_info_mode = switch (self.config_.completion_info_mode) { + .none => .box, + .box => .panel, + .panel => blk: { + if (mainview()) |mv| mv.hide_info_view_panel(); + break :blk .none; + }, + }; + defer self.logger.print("completion info mode {t}", .{self.config_.completion_info_mode}); + try save_config(); + resize(); + } + pub const toggle_completion_info_mode_meta: Meta = .{ .description = "Toggle completion item info display" }; + + pub fn toggle_hover_info_mode(self: *Self, _: Ctx) Result { + self.config_.hover_info_mode = switch (self.config_.hover_info_mode) { + .box => .panel, + .panel => blk: { + if (mainview()) |mv| mv.hide_info_view_panel(); + break :blk .box; + }, + }; + defer self.logger.print("hover info mode {t}", .{self.config_.hover_info_mode}); + try save_config(); + resize(); + } + pub const toggle_hover_info_mode_meta: Meta = .{ .description = "Toggle hover info display" }; + pub fn toggle_keybind_hints(self: *Self, _: Ctx) Result { self.hint_mode = switch (self.hint_mode) { .all => .prefix, @@ -1203,6 +1262,18 @@ const cmds = struct { } pub const toggle_auto_find_meta: Meta = .{ .description = "Toggle auto find mode" }; + pub fn toggle_auto_highlight_references(self: *Self, _: Ctx) Result { + const enabled = try self.toggle_idle_action(.highlight_references); + self.logger.print("auto highlight references {s}", .{if (enabled) "enabled" else "disabled"}); + } + pub const toggle_auto_highlight_references_meta: Meta = .{ .description = "Toggle auto highlight references" }; + + pub fn toggle_auto_hover(self: *Self, _: Ctx) Result { + const enabled = try self.toggle_idle_action(.hover); + self.logger.print("auto hover {s}", .{if (enabled) "enabled" else "disabled"}); + } + pub const toggle_auto_hover_meta: Meta = .{ .description = "Toggle auto hover" }; + pub fn force_color_scheme(self: *Self, ctx: Ctx) Result { self.force_color_scheme(if (try ctx.args.match(.{"dark"})) .dark @@ -1643,9 +1714,17 @@ const cmds = struct { pub const shrink_centered_view_meta: Meta = .{ .description = "Shrink centered view" }; pub fn panel_next_widget_style(_: *Self, _: Ctx) Result { - set_next_style(.panel); + if (mainview()) |mv| if (mv.is_any_panel_view_showing()) { + set_next_style(.panel); + need_render(@src()); + try save_config(); + return; + }; + set_next_style(.info_box); need_render(@src()); try save_config(); + tp.self_pid().send(.{ "cmd", "hover" }) catch {}; + return; } pub const panel_next_widget_style_meta: Meta = .{}; @@ -1663,6 +1742,13 @@ const cmds = struct { } pub const dropdown_next_widget_style_meta: Meta = .{}; + pub fn info_box_next_widget_style(_: *Self, _: Ctx) Result { + set_next_style(.info_box); + need_render(@src()); + try save_config(); + } + pub const info_box_next_widget_style_meta: Meta = .{}; + pub fn enable_fast_scroll(self: *Self, _: Ctx) Result { self.fast_scroll_ = true; } @@ -2355,6 +2441,7 @@ pub fn get_widget_style(widget_type: WidgetType) *const WidgetStyle { .pane_left => WidgetStyle.from_tag(config_.pane_left_style), .pane_right => WidgetStyle.from_tag(config_.pane_right_style), .hint_window => WidgetStyle.from_tag(config_.hint_window_style), + .info_box => WidgetStyle.from_tag(config_.info_box_style), }; } @@ -2368,7 +2455,8 @@ pub fn set_next_style(widget_type: WidgetType) void { fn next_widget_style(tag: ConfigWidgetStyle) ConfigWidgetStyle { const max_tag = comptime std.meta.tags(ConfigWidgetStyle).len; - const new_value = @intFromEnum(tag) + 1; + const value: usize = @intFromEnum(tag); + const new_value = value + 1; return if (new_value >= max_tag) @enumFromInt(0) else @enumFromInt(new_value); } @@ -2383,6 +2471,7 @@ fn widget_type_config_variable(widget_type: WidgetType) *ConfigWidgetStyle { .pane_left => &config_.pane_left_style, .pane_right => &config_.pane_right_style, .hint_window => &config_.hint_window_style, + .info_box => &config_.info_box_style, }; }