mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 16:56:09 +03:00
Merge pull request #1333 from mitchellh/select-line
select line considers semantic prompt change a boundary
This commit is contained in:
@ -207,6 +207,11 @@ pub const RowHeader = struct {
|
||||
|
||||
/// This line is the start of command output.
|
||||
command = 4,
|
||||
|
||||
/// True if this is a prompt or input line.
|
||||
pub fn promptOrInput(self: SemanticPrompt) bool {
|
||||
return self == .prompt or self == .prompt_continuation or self == .input;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@ -1592,6 +1597,15 @@ pub fn selectLine(self: *Screen, pt: point.ScreenPoint) ?Selection {
|
||||
const y_max = self.rowsWritten() - 1;
|
||||
if (pt.y > y_max or pt.x >= self.cols) return null;
|
||||
|
||||
// Get the current point semantic prompt state since that determines
|
||||
// boundary conditions too. This makes it so that line selection can
|
||||
// only happen within the same prompt state. For example, if you triple
|
||||
// click output, but the shell uses spaces to soft-wrap to the prompt
|
||||
// then the selection will stop prior to the prompt. See issue #1329.
|
||||
const semantic_prompt_state = self.getRow(.{ .screen = pt.y })
|
||||
.getSemanticPrompt()
|
||||
.promptOrInput();
|
||||
|
||||
// The real start of the row is the first row in the soft-wrap.
|
||||
const start_row: usize = start_row: {
|
||||
if (pt.y == 0) break :start_row 0;
|
||||
@ -1600,6 +1614,11 @@ pub fn selectLine(self: *Screen, pt: point.ScreenPoint) ?Selection {
|
||||
while (true) {
|
||||
const current = self.getRow(.{ .screen = y });
|
||||
if (!current.header().flags.wrap) break :start_row y + 1;
|
||||
|
||||
// See semantic_prompt_state comment for why
|
||||
const current_prompt = current.getSemanticPrompt().promptOrInput();
|
||||
if (current_prompt != semantic_prompt_state) break :start_row y + 1;
|
||||
|
||||
if (y == 0) break :start_row y;
|
||||
y -= 1;
|
||||
}
|
||||
@ -1611,6 +1630,12 @@ pub fn selectLine(self: *Screen, pt: point.ScreenPoint) ?Selection {
|
||||
var y: usize = pt.y;
|
||||
while (y <= y_max) : (y += 1) {
|
||||
const current = self.getRow(.{ .screen = y });
|
||||
|
||||
// See semantic_prompt_state comment for why
|
||||
const current_prompt = current.getSemanticPrompt().promptOrInput();
|
||||
if (current_prompt != semantic_prompt_state) break :end_row y - 1;
|
||||
|
||||
// End of the screen or not wrapped, we're done.
|
||||
if (y == y_max or !current.header().flags.wrap) break :end_row y;
|
||||
}
|
||||
unreachable;
|
||||
@ -4503,6 +4528,41 @@ test "Screen: selectLine across soft-wrap" {
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/mitchellh/ghostty/issues/1329
|
||||
test "Screen: selectLine semantic prompt boundary" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var s = try init(alloc, 10, 5, 0);
|
||||
defer s.deinit();
|
||||
try s.testWriteString("ABCDE\nA > ");
|
||||
|
||||
{
|
||||
const contents = try s.testString(alloc, .screen);
|
||||
defer alloc.free(contents);
|
||||
try testing.expectEqualStrings("ABCDE\nA \n> ", contents);
|
||||
}
|
||||
|
||||
var row = s.getRow(.{ .screen = 2 });
|
||||
row.setSemanticPrompt(.prompt);
|
||||
|
||||
// Selecting output stops at the prompt even if soft-wrapped
|
||||
{
|
||||
const sel = s.selectLine(.{ .x = 1, .y = 1 }).?;
|
||||
try testing.expectEqual(@as(usize, 0), sel.start.x);
|
||||
try testing.expectEqual(@as(usize, 1), sel.start.y);
|
||||
try testing.expectEqual(@as(usize, 0), sel.end.x);
|
||||
try testing.expectEqual(@as(usize, 1), sel.end.y);
|
||||
}
|
||||
{
|
||||
const sel = s.selectLine(.{ .x = 1, .y = 2 }).?;
|
||||
try testing.expectEqual(@as(usize, 0), sel.start.x);
|
||||
try testing.expectEqual(@as(usize, 2), sel.start.y);
|
||||
try testing.expectEqual(@as(usize, 0), sel.end.x);
|
||||
try testing.expectEqual(@as(usize, 2), sel.end.y);
|
||||
}
|
||||
}
|
||||
|
||||
test "Screen: selectLine across soft-wrap ignores blank lines" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
Reference in New Issue
Block a user