diff --git a/src/Surface.zig b/src/Surface.zig index 9c4431ddc..24100544a 100644 --- a/src/Surface.zig +++ b/src/Surface.zig @@ -1364,54 +1364,6 @@ pub fn keyCallback( } } - // Expand selection if one exists and event is shift + - if (event.mods.shift) adjust_selection: { - self.renderer_state.mutex.lock(); - defer self.renderer_state.mutex.unlock(); - var screen = &self.io.terminal.screen; - const sel = if (screen.selection) |*sel| sel else break :adjust_selection; - - // Silently consume key releases. We only want to process selection - // adjust on press. - if (event.action != .press and event.action != .repeat) return .consumed; - - sel.adjust(screen, switch (event.key) { - .left => .left, - .right => .right, - .up => .up, - .down => .down, - .page_up => .page_up, - .page_down => .page_down, - .home => .home, - .end => .end, - else => break :adjust_selection, - }); - - // If the selection endpoint is outside of the current viewpoint, - // scroll it in to view. Note we always specifically use sel.end - // because that is what adjust modifies. - scroll: { - const viewport_tl = screen.pages.getTopLeft(.viewport); - const viewport_br = screen.pages.getBottomRight(.viewport).?; - if (sel.end().isBetween(viewport_tl, viewport_br)) - break :scroll; - - // Our end point is not within the viewport. If the end - // point is after the br then we need to adjust the end so - // that it is at the bottom right of the viewport. - const target = if (sel.end().before(viewport_tl)) - sel.end() - else - sel.end().up(screen.pages.rows - 1) orelse sel.end(); - - screen.scroll(.{ .pin = target }); - } - - // Queue a render so its shown - try self.queueRender(); - return .consumed; - } - // If we allow KAM and KAM is enabled then we do nothing. if (self.config.vt_kam_allowed) { self.renderer_state.mutex.lock(); @@ -3535,6 +3487,48 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool }, .quit => try self.app.setQuit(), + + .adjust_selection => |direction| adjust_selection: { + self.renderer_state.mutex.lock(); + defer self.renderer_state.mutex.unlock(); + + const screen = &self.io.terminal.screen; + const sel = if (screen.selection) |*sel| sel else break :adjust_selection; + sel.adjust(screen, switch (direction) { + .left => .left, + .right => .right, + .up => .up, + .down => .down, + .page_up => .page_up, + .page_down => .page_down, + .home => .home, + .end => .end, + }); + + // If the selection endpoint is outside of the current viewpoint, + // scroll it in to view. Note we always specifically use sel.end + // because that is what adjust modifies. + scroll: { + const viewport_tl = screen.pages.getTopLeft(.viewport); + const viewport_br = screen.pages.getBottomRight(.viewport).?; + if (sel.end().isBetween(viewport_tl, viewport_br)) + break :scroll; + + // Our end point is not within the viewport. If the end + // point is after the br then we need to adjust the end so + // that it is at the bottom right of the viewport. + const target = if (sel.end().before(viewport_tl)) + sel.end() + else + sel.end().up(screen.pages.rows - 1) orelse sel.end(); + + screen.scroll(.{ .pin = target }); + } + + // Queue a render so its shown + screen.dirty.selection = true; + try self.queueRender(); + }, } return true; diff --git a/src/config/Config.zig b/src/config/Config.zig index e95a2ba8e..72a3cb909 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -1325,6 +1325,48 @@ pub fn default(alloc_gpa: Allocator) Allocator.Error!Config { .{ .write_scrollback_file = {} }, ); + // Expand Selection + try result.keybind.set.put( + alloc, + .{ .key = .{ .translated = .left }, .mods = .{ .shift = true } }, + .{ .adjust_selection = .left }, + ); + try result.keybind.set.put( + alloc, + .{ .key = .{ .translated = .right }, .mods = .{ .shift = true } }, + .{ .adjust_selection = .right }, + ); + try result.keybind.set.put( + alloc, + .{ .key = .{ .translated = .up }, .mods = .{ .shift = true } }, + .{ .adjust_selection = .up }, + ); + try result.keybind.set.put( + alloc, + .{ .key = .{ .translated = .down }, .mods = .{ .shift = true } }, + .{ .adjust_selection = .down }, + ); + try result.keybind.set.put( + alloc, + .{ .key = .{ .translated = .page_up }, .mods = .{ .shift = true } }, + .{ .adjust_selection = .page_up }, + ); + try result.keybind.set.put( + alloc, + .{ .key = .{ .translated = .page_down }, .mods = .{ .shift = true } }, + .{ .adjust_selection = .page_down }, + ); + try result.keybind.set.put( + alloc, + .{ .key = .{ .translated = .home }, .mods = .{ .shift = true } }, + .{ .adjust_selection = .home }, + ); + try result.keybind.set.put( + alloc, + .{ .key = .{ .translated = .end }, .mods = .{ .shift = true } }, + .{ .adjust_selection = .end }, + ); + // Windowing if (comptime !builtin.target.isDarwin()) { try result.keybind.set.put( diff --git a/src/input/Binding.zig b/src/input/Binding.zig index 27c620f15..4232ea252 100644 --- a/src/input/Binding.zig +++ b/src/input/Binding.zig @@ -195,6 +195,10 @@ pub const Action = union(enum) { scroll_page_fractional: f32, scroll_page_lines: i16, + /// Adjust an existing selection in a given direction. This action + /// does nothing if there is no active selection. + adjust_selection: AdjustSelection, + /// Jump the viewport forward or back by prompt. Positive number is the /// number of prompts to jump forward, negative is backwards. jump_to_prompt: i16, @@ -277,6 +281,17 @@ pub const Action = union(enum) { application: []const u8, }; + pub const AdjustSelection = enum { + left, + right, + up, + down, + page_up, + page_down, + home, + end, + }; + pub const SplitDirection = enum { right, down,