mirror of
https://github.com/ghostty-org/ghostty.git
synced 2026-06-05 03:14:14 +00:00
remove legacy promptPath
This commit is contained in:
@@ -3974,7 +3974,11 @@ pub fn mouseButtonCallback(
|
||||
// and we support some kind of click events, then we need to
|
||||
// move to it.
|
||||
if (self.maybePromptClick()) |handled| {
|
||||
if (handled) return true;
|
||||
if (handled) {
|
||||
// Moving always resets the click count so that we don't highlight.
|
||||
self.mouse.left_click_count = 0;
|
||||
return true;
|
||||
}
|
||||
} else |err| {
|
||||
log.warn("error processing prompt click err={}", .{err});
|
||||
}
|
||||
@@ -4019,25 +4023,6 @@ pub fn mouseButtonCallback(
|
||||
}
|
||||
}
|
||||
|
||||
// For left button click release we check if we are moving our cursor.
|
||||
if (button == .left and
|
||||
action == .release and
|
||||
mods.alt)
|
||||
click_move: {
|
||||
self.renderer_state.mutex.lock();
|
||||
defer self.renderer_state.mutex.unlock();
|
||||
|
||||
// If we have a selection then we do not do click to move because
|
||||
// it means that we moved our cursor while pressing the mouse button.
|
||||
if (self.io.terminal.screens.active.selection != null) break :click_move;
|
||||
|
||||
// Moving always resets the click count so that we don't highlight.
|
||||
self.mouse.left_click_count = 0;
|
||||
const pin = self.mouse.left_click_pin orelse break :click_move;
|
||||
try self.clickMoveCursor(pin.*);
|
||||
return true;
|
||||
}
|
||||
|
||||
// For left button clicks we always record some information for
|
||||
// selection/highlighting purposes.
|
||||
if (button == .left and action == .press) click: {
|
||||
@@ -4397,60 +4382,6 @@ fn maybePromptClick(self: *Surface) !bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Performs the "click-to-move" logic to move the cursor to the given
|
||||
/// screen point if possible. This works by converting the path to the
|
||||
/// given point into a series of arrow key inputs.
|
||||
fn clickMoveCursor(self: *Surface, to: terminal.Pin) !void {
|
||||
// If click-to-move is disabled then we're done.
|
||||
if (!self.config.cursor_click_to_move) return;
|
||||
|
||||
const t = &self.io.terminal;
|
||||
|
||||
// Click to move cursor only works on the primary screen where prompts
|
||||
// exist. This means that alt screen multiplexers like tmux will not
|
||||
// support this feature. It is just too messy.
|
||||
if (t.screens.active_key != .primary) return;
|
||||
|
||||
// This flag is only set if we've seen at least one semantic prompt
|
||||
// OSC sequence. If we've never seen that sequence, we can't possibly
|
||||
// move the cursor so we can fast path out of here.
|
||||
if (!t.screens.active.semantic_prompt.seen) return;
|
||||
|
||||
// Get our path
|
||||
const from = t.screens.active.cursor.page_pin.*;
|
||||
const path = t.screens.active.promptPath(from, to);
|
||||
log.debug("click-to-move-cursor from={} to={} path={}", .{ from, to, path });
|
||||
|
||||
// If we aren't moving at all, fast path out of here.
|
||||
if (path.x == 0 and path.y == 0) return;
|
||||
|
||||
// Convert our path to arrow key inputs. Yes, that is how this works.
|
||||
// Yes, that is pretty sad. Yes, this could backfire in various ways.
|
||||
// But its the best we can do.
|
||||
|
||||
// We do Y first because it prevents any weird wrap behavior.
|
||||
if (path.y != 0) {
|
||||
const arrow = if (path.y < 0) arrow: {
|
||||
break :arrow if (t.modes.get(.cursor_keys)) "\x1bOA" else "\x1b[A";
|
||||
} else arrow: {
|
||||
break :arrow if (t.modes.get(.cursor_keys)) "\x1bOB" else "\x1b[B";
|
||||
};
|
||||
for (0..@abs(path.y)) |_| {
|
||||
self.queueIo(.{ .write_stable = arrow }, .locked);
|
||||
}
|
||||
}
|
||||
if (path.x != 0) {
|
||||
const arrow = if (path.x < 0) arrow: {
|
||||
break :arrow if (t.modes.get(.cursor_keys)) "\x1bOD" else "\x1b[D";
|
||||
} else arrow: {
|
||||
break :arrow if (t.modes.get(.cursor_keys)) "\x1bOC" else "\x1b[C";
|
||||
};
|
||||
for (0..@abs(path.x)) |_| {
|
||||
self.queueIo(.{ .write_stable = arrow }, .locked);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Link = struct {
|
||||
action: input.Link.Action,
|
||||
selection: terminal.Selection,
|
||||
|
||||
@@ -3123,51 +3123,6 @@ fn promptClickLine(self: *Screen, click_pin: Pin) PromptClickMove {
|
||||
return .{ .left = count, .right = 0 };
|
||||
}
|
||||
|
||||
/// Returns the change in x/y that is needed to reach "to" from "from"
|
||||
/// within a prompt. If "to" is before or after the prompt bounds then
|
||||
/// the result will be bounded to the prompt.
|
||||
///
|
||||
/// This feature requires shell integration. If shell integration is not
|
||||
/// enabled, this will always return zero for both x and y (no path).
|
||||
pub fn promptPath(
|
||||
self: *Screen,
|
||||
from: Pin,
|
||||
to: Pin,
|
||||
) struct {
|
||||
x: isize,
|
||||
y: isize,
|
||||
} {
|
||||
// Verify "from" is on a prompt row before calling highlightSemanticContent.
|
||||
// highlightSemanticContent asserts the starting point is a prompt.
|
||||
switch (from.rowAndCell().row.semantic_prompt) {
|
||||
.prompt, .prompt_continuation => {},
|
||||
.none => return .{ .x = 0, .y = 0 },
|
||||
}
|
||||
|
||||
// Get our prompt bounds assuming "from" is at a prompt.
|
||||
const hl = self.pages.highlightSemanticContent(from, .prompt) orelse return .{ .x = 0, .y = 0 };
|
||||
const bounds: Selection = .init(hl.start, hl.end, false);
|
||||
|
||||
// Get our actual "to" point clamped to the bounds of the prompt.
|
||||
const to_clamped = if (bounds.contains(self, to))
|
||||
to
|
||||
else if (to.before(bounds.start()))
|
||||
bounds.start()
|
||||
else
|
||||
bounds.end();
|
||||
|
||||
// Convert to points
|
||||
const from_pt = self.pages.pointFromPin(.screen, from).?.screen;
|
||||
const to_pt = self.pages.pointFromPin(.screen, to_clamped).?.screen;
|
||||
|
||||
// Basic math to calculate our path.
|
||||
const from_x: isize = @intCast(from_pt.x);
|
||||
const from_y: isize = @intCast(from_pt.y);
|
||||
const to_x: isize = @intCast(to_pt.x);
|
||||
const to_y: isize = @intCast(to_pt.y);
|
||||
return .{ .x = to_x - from_x, .y = to_y - from_y };
|
||||
}
|
||||
|
||||
/// Dump the screen to a string. The writer given should be buffered;
|
||||
/// this function does not attempt to efficiently write and generally writes
|
||||
/// one byte at a time.
|
||||
@@ -8801,134 +8756,6 @@ test "Screen: selectOutput" {
|
||||
}
|
||||
}
|
||||
|
||||
test "Screen: promptPath" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
var s = try init(alloc, .{ .cols = 10, .rows = 15, .max_scrollback = 0 });
|
||||
defer s.deinit();
|
||||
|
||||
try testing.expect(s.pages.pages.first == s.pages.pages.last);
|
||||
const page = &s.pages.pages.first.?.data;
|
||||
|
||||
// Set up:
|
||||
// Row 0-1: output
|
||||
// Row 2: prompt
|
||||
// Row 3: input
|
||||
// Row 4-5: output
|
||||
// Row 6: prompt + input
|
||||
// Row 7-9: output
|
||||
|
||||
// Row 2: prompt (with prompt cells) and input
|
||||
{
|
||||
const rac = page.getRowAndCell(0, 2);
|
||||
rac.row.semantic_prompt = .prompt;
|
||||
// First 3 cols are prompt
|
||||
for (0..3) |x| {
|
||||
const cell = page.getRowAndCell(x, 2).cell;
|
||||
cell.* = .{
|
||||
.content_tag = .codepoint,
|
||||
.content = .{ .codepoint = 'P' },
|
||||
.semantic_content = .prompt,
|
||||
};
|
||||
}
|
||||
// Next cols are input
|
||||
for (3..10) |x| {
|
||||
const cell = page.getRowAndCell(x, 2).cell;
|
||||
cell.* = .{
|
||||
.content_tag = .codepoint,
|
||||
.content = .{ .codepoint = 'I' },
|
||||
.semantic_content = .input,
|
||||
};
|
||||
}
|
||||
}
|
||||
// Row 3: continuation line with input cells (same prompt block)
|
||||
{
|
||||
const rac = page.getRowAndCell(0, 3);
|
||||
rac.row.semantic_prompt = .prompt_continuation;
|
||||
for (0..6) |x| {
|
||||
const cell = page.getRowAndCell(x, 3).cell;
|
||||
cell.* = .{
|
||||
.content_tag = .codepoint,
|
||||
.content = .{ .codepoint = 'I' },
|
||||
.semantic_content = .input,
|
||||
};
|
||||
}
|
||||
}
|
||||
// Row 6: next prompt + input on same line
|
||||
{
|
||||
const rac = page.getRowAndCell(0, 6);
|
||||
rac.row.semantic_prompt = .prompt;
|
||||
for (0..2) |x| {
|
||||
const cell = page.getRowAndCell(x, 6).cell;
|
||||
cell.* = .{
|
||||
.content_tag = .codepoint,
|
||||
.content = .{ .codepoint = '$' },
|
||||
.semantic_content = .prompt,
|
||||
};
|
||||
}
|
||||
for (2..8) |x| {
|
||||
const cell = page.getRowAndCell(x, 6).cell;
|
||||
cell.* = .{
|
||||
.content_tag = .codepoint,
|
||||
.content = .{ .codepoint = 'i' },
|
||||
.semantic_content = .input,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// From is not in the prompt
|
||||
{
|
||||
const path = s.promptPath(
|
||||
s.pages.pin(.{ .active = .{ .x = 0, .y = 1 } }).?,
|
||||
s.pages.pin(.{ .active = .{ .x = 0, .y = 2 } }).?,
|
||||
);
|
||||
try testing.expectEqual(@as(isize, 0), path.x);
|
||||
try testing.expectEqual(@as(isize, 0), path.y);
|
||||
}
|
||||
|
||||
// Same line
|
||||
{
|
||||
const path = s.promptPath(
|
||||
s.pages.pin(.{ .active = .{ .x = 6, .y = 2 } }).?,
|
||||
s.pages.pin(.{ .active = .{ .x = 3, .y = 2 } }).?,
|
||||
);
|
||||
try testing.expectEqual(@as(isize, -3), path.x);
|
||||
try testing.expectEqual(@as(isize, 0), path.y);
|
||||
}
|
||||
|
||||
// Different lines
|
||||
{
|
||||
const path = s.promptPath(
|
||||
s.pages.pin(.{ .active = .{ .x = 6, .y = 2 } }).?,
|
||||
s.pages.pin(.{ .active = .{ .x = 3, .y = 3 } }).?,
|
||||
);
|
||||
try testing.expectEqual(@as(isize, -3), path.x);
|
||||
try testing.expectEqual(@as(isize, 1), path.y);
|
||||
}
|
||||
|
||||
// To is out of bounds before
|
||||
{
|
||||
const path = s.promptPath(
|
||||
s.pages.pin(.{ .active = .{ .x = 6, .y = 2 } }).?,
|
||||
s.pages.pin(.{ .active = .{ .x = 3, .y = 1 } }).?,
|
||||
);
|
||||
try testing.expectEqual(@as(isize, -6), path.x);
|
||||
try testing.expectEqual(@as(isize, 0), path.y);
|
||||
}
|
||||
|
||||
// To is out of bounds after
|
||||
// Prompt ends at (5, 3) since that's the last input cell
|
||||
{
|
||||
const path = s.promptPath(
|
||||
s.pages.pin(.{ .active = .{ .x = 6, .y = 2 } }).?,
|
||||
s.pages.pin(.{ .active = .{ .x = 3, .y = 9 } }).?,
|
||||
);
|
||||
try testing.expectEqual(@as(isize, -1), path.x);
|
||||
try testing.expectEqual(@as(isize, 1), path.y);
|
||||
}
|
||||
}
|
||||
|
||||
test "Screen: selectionString basic" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
Reference in New Issue
Block a user