diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 4a007e262..81c21d3b0 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -1087,9 +1087,11 @@ pub fn semanticPrompt( // "First do a fresh-line." try self.semanticPromptFreshLine(); + const screen: *Screen = self.screens.active; + // "Subsequent text (until a OSC "133;B" or OSC "133;I" command) // is a prompt string (as if followed by OSC 133;P;k=i\007)." - self.screens.active.cursorSetSemanticContent(.{ + screen.cursorSetSemanticContent(.{ .prompt = cmd.readOption(.prompt_kind) orelse .initial, }); @@ -1100,6 +1102,24 @@ pub fn semanticPrompt( self.flags.shell_redraws_prompt = v; } + click: { + // Handle click_events as a priority over cl. click_events + // is another Kitty-specific extension that converts clicks + // within a prompt area to SGR mouse events and defers to the + // shell to handle them. + if (cmd.readOption(.click_events)) |v| { + if (v) { + screen.semantic_prompt.click = .click_events; + break :click; + } + } + + // If click_events was not set or disabled, fallback to `cl`. + if (cmd.readOption(.cl)) |v| { + screen.semantic_prompt.click = .{ .cl = v }; + } + } + // The "aid" and "cl" options are also valid for this // command but we don't yet handle these in any meaningful way. }, @@ -11635,6 +11655,109 @@ test "Terminal: multiple newlines in prompt mode marks all rows" { } } +test "Terminal: OSC133A click_events=1 sets click to click_events" { + const alloc = testing.allocator; + var t = try init(alloc, .{ .cols = 10, .rows = 5 }); + defer t.deinit(alloc); + + // Verify default state is none + try testing.expectEqual(.none, t.screens.active.semantic_prompt.click); + + // OSC 133;A with click_events=1 + try t.semanticPrompt(.{ + .action = .fresh_line_new_prompt, + .options_unvalidated = "click_events=1", + }); + + try testing.expectEqual(.click_events, t.screens.active.semantic_prompt.click); +} + +test "Terminal: OSC133A click_events=0 does not set click_events" { + const alloc = testing.allocator; + var t = try init(alloc, .{ .cols = 10, .rows = 5 }); + defer t.deinit(alloc); + + // OSC 133;A with click_events=0 + try t.semanticPrompt(.{ + .action = .fresh_line_new_prompt, + .options_unvalidated = "click_events=0", + }); + + // Should remain none since click_events=0 doesn't activate anything + try testing.expectEqual(.none, t.screens.active.semantic_prompt.click); +} + +test "Terminal: OSC133A cl option sets click to cl value" { + const alloc = testing.allocator; + var t = try init(alloc, .{ .cols = 10, .rows = 5 }); + defer t.deinit(alloc); + + // OSC 133;A with cl=m (multiple) + try t.semanticPrompt(.{ + .action = .fresh_line_new_prompt, + .options_unvalidated = "cl=m", + }); + + try testing.expectEqual(Screen.SemanticPrompt.SemanticClick{ .cl = .multiple }, t.screens.active.semantic_prompt.click); +} + +test "Terminal: OSC133A cl=line sets click to line" { + const alloc = testing.allocator; + var t = try init(alloc, .{ .cols = 10, .rows = 5 }); + defer t.deinit(alloc); + + try t.semanticPrompt(.{ + .action = .fresh_line_new_prompt, + .options_unvalidated = "cl=line", + }); + + try testing.expectEqual(Screen.SemanticPrompt.SemanticClick{ .cl = .line }, t.screens.active.semantic_prompt.click); +} + +test "Terminal: OSC133A click_events=1 takes priority over cl" { + const alloc = testing.allocator; + var t = try init(alloc, .{ .cols = 10, .rows = 5 }); + defer t.deinit(alloc); + + // OSC 133;A with both click_events=1 and cl=m + try t.semanticPrompt(.{ + .action = .fresh_line_new_prompt, + .options_unvalidated = "click_events=1;cl=m", + }); + + // click_events should take priority + try testing.expectEqual(.click_events, t.screens.active.semantic_prompt.click); +} + +test "Terminal: OSC133A click_events=0 falls back to cl" { + const alloc = testing.allocator; + var t = try init(alloc, .{ .cols = 10, .rows = 5 }); + defer t.deinit(alloc); + + // OSC 133;A with click_events=0 and cl=v + try t.semanticPrompt(.{ + .action = .fresh_line_new_prompt, + .options_unvalidated = "click_events=0;cl=v", + }); + + // Should fall back to cl since click_events is disabled + try testing.expectEqual(Screen.SemanticPrompt.SemanticClick{ .cl = .conservative_vertical }, t.screens.active.semantic_prompt.click); +} + +test "Terminal: OSC133A no click options leaves click as none" { + const alloc = testing.allocator; + var t = try init(alloc, .{ .cols = 10, .rows = 5 }); + defer t.deinit(alloc); + + // OSC 133;A with no click-related options + try t.semanticPrompt(.{ + .action = .fresh_line_new_prompt, + .options_unvalidated = "aid=123", + }); + + try testing.expectEqual(.none, t.screens.active.semantic_prompt.click); +} + test "Terminal: cursorIsAtPrompt" { const alloc = testing.allocator; var t = try init(alloc, .{ .cols = 10, .rows = 3 });