From ae65998d5b8080d1304de7c694f90458564f4b12 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sat, 24 Jan 2026 14:22:25 -0800 Subject: [PATCH] terminal: OSC 133;I --- src/terminal/Screen.zig | 1 + src/terminal/Terminal.zig | 20 ++++++++++++++++++-- src/terminal/stream_readonly.zig | 16 ++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/terminal/Screen.zig b/src/terminal/Screen.zig index 10d33a3a8..e92a81117 100644 --- a/src/terminal/Screen.zig +++ b/src/terminal/Screen.zig @@ -137,6 +137,7 @@ pub const Cursor = struct { /// The current semantic content type for the cursor that will be /// applied to any newly written cells. semantic_content: pagepkg.Cell.SemanticContent = .output, + semantic_content_clear_eol: bool = false, /// The pointers into the page list where the cursor is currently /// located. This makes it faster to move the cursor. diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 0e7cc5d92..2782b8d0e 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -1082,6 +1082,7 @@ pub fn semanticPrompt( // the prompt types (k=) because it isn't of value to us // currently. This may change in the future. self.screens.active.cursor.semantic_content = .prompt; + self.screens.active.cursor.semantic_content_clear_eol = false; // This is a kitty-specific flag that notes that the shell // is capable of redraw. @@ -1114,17 +1115,26 @@ pub fn semanticPrompt( // As noted above, we don't currently utilize the prompt type. self.screens.active.cursor.semantic_content = .prompt; + self.screens.active.cursor.semantic_content_clear_eol = false; }, .end_prompt_start_input => { // End of prompt and start of user input, terminated by a OSC // "133;C" or another prompt (OSC "133;P"). self.screens.active.cursor.semantic_content = .input; + self.screens.active.cursor.semantic_content_clear_eol = false; + }, + + .end_prompt_start_input_terminate_eol => { + // End of prompt and start of user input, terminated by end-of-line. + self.semanticPromptSet(.input); + self.screens.active.cursor.semantic_content_clear_eol = true; }, .end_input_start_output => { // "End of input, and start of output." self.screens.active.cursor.semantic_content = .output; + self.screens.active.cursor.semantic_content_clear_eol = false; }, .end_command => { @@ -1133,9 +1143,8 @@ pub fn semanticPrompt( // its reasonable at this point to reset our semantic content // state but the spec doesn't really say what to do. self.screens.active.cursor.semantic_content = .output; + self.screens.active.cursor.semantic_content_clear_eol = false; }, - - else => {}, } } @@ -1295,6 +1304,13 @@ pub fn index(self: *Terminal) !void { // Unset pending wrap state self.screens.active.cursor.pending_wrap = false; + // Always reset any semantic content clear-eol state + if (self.screens.active.cursor.semantic_content_clear_eol) { + @branchHint(.unlikely); + self.screens.active.cursor.semantic_content = .output; + self.screens.active.cursor.semantic_content_clear_eol = false; + } + // Outside of the scroll region we move the cursor one line down. if (self.screens.active.cursor.y < self.scrolling_region.top or self.screens.active.cursor.y > self.scrolling_region.bottom) diff --git a/src/terminal/stream_readonly.zig b/src/terminal/stream_readonly.zig index 5a5f0d65f..9ffe617bd 100644 --- a/src/terminal/stream_readonly.zig +++ b/src/terminal/stream_readonly.zig @@ -999,3 +999,19 @@ test "semantic prompt new_command at column zero" { try testing.expectEqual(@as(usize, 0), t.screens.active.cursor.y); try testing.expectEqual(.prompt, t.screens.active.cursor.semantic_content); } + +test "semantic prompt end_prompt_start_input_terminate_eol clears on linefeed" { + var t: Terminal = try .init(testing.allocator, .{ .cols = 10, .rows = 10 }); + defer t.deinit(testing.allocator); + + var s: Stream = .initAlloc(testing.allocator, .init(&t)); + defer s.deinit(); + + // Set input terminated by EOL + try s.nextSlice("\x1b]133;I\x07"); + try testing.expectEqual(.input, t.screens.active.cursor.semantic_content); + + // Linefeed should reset semantic content to output + try s.nextSlice("\n"); + try testing.expectEqual(.output, t.screens.active.cursor.semantic_content); +}