mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
Merge pull request #899 from Raiden1411/select-all
core: implement select all binding
This commit is contained in:
@ -2534,6 +2534,14 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
||||
} else log.warn("runtime doesn't implement toggleFullscreen", .{});
|
||||
},
|
||||
|
||||
.select_all => {
|
||||
const sel = self.io.terminal.screen.selectAll();
|
||||
if (sel) |s| {
|
||||
self.setSelection(s);
|
||||
try self.queueRender();
|
||||
}
|
||||
},
|
||||
|
||||
.inspector => |mode| {
|
||||
if (@hasDecl(apprt.Surface, "controlInspector")) {
|
||||
self.rt_surface.controlInspector(mode);
|
||||
|
@ -913,6 +913,13 @@ pub fn default(alloc_gpa: Allocator) Allocator.Error!Config {
|
||||
}
|
||||
}
|
||||
|
||||
// Select all
|
||||
try result.keybind.set.put(
|
||||
alloc,
|
||||
.{ .key = .a, .mods = ctrlOrSuper(.{}) },
|
||||
.{ .select_all = {} },
|
||||
);
|
||||
|
||||
// Toggle fullscreen
|
||||
try result.keybind.set.put(
|
||||
alloc,
|
||||
|
@ -152,6 +152,9 @@ pub const Action = union(enum) {
|
||||
/// Clear the screen. This also clears all scrollback.
|
||||
clear_screen: void,
|
||||
|
||||
/// Select all text on the screen.
|
||||
select_all: void,
|
||||
|
||||
/// Scroll the screen varying amounts.
|
||||
scroll_to_top: void,
|
||||
scroll_to_bottom: void,
|
||||
|
@ -1392,6 +1392,73 @@ pub fn clear(self: *Screen, mode: ClearMode) !void {
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the selection for all contents on the screen. Surrounding
|
||||
/// whitespace is omitted. If there is no selection, this returns null.
|
||||
pub fn selectAll(self: *Screen) ?Selection {
|
||||
const whitespace = &[_]u32{ 0, ' ', '\t' };
|
||||
const y_max = self.rowsWritten() - 1;
|
||||
|
||||
const start: point.ScreenPoint = start: {
|
||||
var y: usize = 0;
|
||||
while (y <= y_max) : (y += 1) {
|
||||
const current_row = self.getRow(.{ .screen = y });
|
||||
var x: usize = 0;
|
||||
while (x < self.cols) : (x += 1) {
|
||||
const cell = current_row.getCell(x);
|
||||
|
||||
// Empty is whitespace
|
||||
if (cell.empty()) continue;
|
||||
|
||||
// Non-empty means we found it.
|
||||
const this_whitespace = std.mem.indexOfAny(
|
||||
u32,
|
||||
whitespace,
|
||||
&[_]u32{cell.char},
|
||||
) != null;
|
||||
if (this_whitespace) continue;
|
||||
|
||||
break :start .{ .x = x, .y = y };
|
||||
}
|
||||
}
|
||||
|
||||
// There is no start point and therefore no line that can be selected.
|
||||
return null;
|
||||
};
|
||||
|
||||
const end: point.ScreenPoint = end: {
|
||||
var y: usize = y_max;
|
||||
while (true) {
|
||||
const current_row = self.getRow(.{ .screen = y });
|
||||
|
||||
var x: usize = 0;
|
||||
while (x < self.cols) : (x += 1) {
|
||||
const real_x = self.cols - x - 1;
|
||||
const cell = current_row.getCell(real_x);
|
||||
|
||||
// Empty or whitespace, ignore.
|
||||
if (cell.empty()) continue;
|
||||
const this_whitespace = std.mem.indexOfAny(
|
||||
u32,
|
||||
whitespace,
|
||||
&[_]u32{cell.char},
|
||||
) != null;
|
||||
if (this_whitespace) continue;
|
||||
|
||||
// Got it
|
||||
break :end .{ .x = real_x, .y = y };
|
||||
}
|
||||
|
||||
if (y == 0) break;
|
||||
y -= 1;
|
||||
}
|
||||
};
|
||||
|
||||
return Selection{
|
||||
.start = start,
|
||||
.end = end,
|
||||
};
|
||||
}
|
||||
|
||||
/// Select the line under the given point. This will select across soft-wrapped
|
||||
/// lines and will omit the leading and trailing whitespace. If the point is
|
||||
/// over whitespace but the line has non-whitespace characters elsewhere, the
|
||||
@ -3856,6 +3923,31 @@ test "Screen: selectLine" {
|
||||
try testing.expectEqual(@as(usize, 0), sel.end.y);
|
||||
}
|
||||
}
|
||||
test "Screen: selectAll" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var s = try init(alloc, 10, 10, 0);
|
||||
defer s.deinit();
|
||||
|
||||
{
|
||||
try s.testWriteString("ABC DEF\n 123\n456");
|
||||
const sel = s.selectAll().?;
|
||||
try testing.expectEqual(@as(usize, 0), sel.start.x);
|
||||
try testing.expectEqual(@as(usize, 0), sel.start.y);
|
||||
try testing.expectEqual(@as(usize, 2), sel.end.x);
|
||||
try testing.expectEqual(@as(usize, 2), sel.end.y);
|
||||
}
|
||||
|
||||
{
|
||||
try s.testWriteString("\nFOO\n BAR\n BAZ\n QWERTY\n 12345678");
|
||||
const sel = s.selectAll().?;
|
||||
try testing.expectEqual(@as(usize, 0), sel.start.x);
|
||||
try testing.expectEqual(@as(usize, 0), sel.start.y);
|
||||
try testing.expectEqual(@as(usize, 8), sel.end.x);
|
||||
try testing.expectEqual(@as(usize, 7), sel.end.y);
|
||||
}
|
||||
}
|
||||
|
||||
test "Screen: selectLine across soft-wrap" {
|
||||
const testing = std.testing;
|
||||
|
Reference in New Issue
Block a user