Skip to content

Commit e71eaab

Browse files
CopilotbadMade
andauthored
fix: preserve spinner cursor recovery on write errors
Agent-Logs-Url: https://github.com/badMade/claw-code/sessions/56bca0b3-24cf-4c30-a1e4-19d7b17df2c5 Co-authored-by: badMade <106821302+badMade@users.noreply.github.com>
1 parent 3ffa9ab commit e71eaab

1 file changed

Lines changed: 57 additions & 6 deletions

File tree

rust/crates/rusty-claude-cli/src/render.rs

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,16 +99,19 @@ impl Spinner {
9999
out: &mut impl Write,
100100
) -> io::Result<()> {
101101
self.frame_index = 0;
102-
self.cursor_hidden = false;
103-
execute!(
102+
let show_result = execute!(
104103
out,
105104
MoveToColumn(0),
106105
Clear(ClearType::CurrentLine),
107106
SetForegroundColor(theme.spinner_done),
108107
Print(format!("✔ {label}\n")),
109108
ResetColor,
110109
Show
111-
)?;
110+
);
111+
if show_result.is_ok() {
112+
self.cursor_hidden = false;
113+
}
114+
show_result?;
112115
out.flush()
113116
}
114117

@@ -119,16 +122,19 @@ impl Spinner {
119122
out: &mut impl Write,
120123
) -> io::Result<()> {
121124
self.frame_index = 0;
122-
self.cursor_hidden = false;
123-
execute!(
125+
let show_result = execute!(
124126
out,
125127
MoveToColumn(0),
126128
Clear(ClearType::CurrentLine),
127129
SetForegroundColor(theme.spinner_failed),
128130
Print(format!("✘ {label}\n")),
129131
ResetColor,
130132
Show
131-
)?;
133+
);
134+
if show_result.is_ok() {
135+
self.cursor_hidden = false;
136+
}
137+
show_result?;
132138
out.flush()
133139
}
134140
}
@@ -927,6 +933,7 @@ fn strip_ansi(input: &str) -> String {
927933
#[cfg(test)]
928934
mod tests {
929935
use super::{strip_ansi, MarkdownStreamState, Spinner, TerminalRenderer};
936+
use std::io::{self, Write};
930937

931938
#[test]
932939
fn renders_markdown_with_styling_and_lists() {
@@ -1081,4 +1088,48 @@ mod tests {
10811088
let output = String::from_utf8_lossy(&out);
10821089
assert!(output.contains("Working"));
10831090
}
1091+
1092+
struct AlwaysFailWriter;
1093+
1094+
impl Write for AlwaysFailWriter {
1095+
fn write(&mut self, _buf: &[u8]) -> io::Result<usize> {
1096+
Err(io::Error::other("write failed"))
1097+
}
1098+
1099+
fn flush(&mut self) -> io::Result<()> {
1100+
Ok(())
1101+
}
1102+
}
1103+
1104+
#[test]
1105+
fn spinner_finish_error_keeps_cursor_hidden_for_drop_recovery() {
1106+
let terminal_renderer = TerminalRenderer::new();
1107+
let mut spinner = Spinner::new();
1108+
let mut out = Vec::new();
1109+
spinner
1110+
.tick("Working", terminal_renderer.color_theme(), &mut out)
1111+
.expect("tick succeeds");
1112+
1113+
let mut failing_out = AlwaysFailWriter;
1114+
let result = spinner.finish("Done", terminal_renderer.color_theme(), &mut failing_out);
1115+
1116+
assert!(result.is_err());
1117+
assert!(spinner.cursor_hidden);
1118+
}
1119+
1120+
#[test]
1121+
fn spinner_fail_error_keeps_cursor_hidden_for_drop_recovery() {
1122+
let terminal_renderer = TerminalRenderer::new();
1123+
let mut spinner = Spinner::new();
1124+
let mut out = Vec::new();
1125+
spinner
1126+
.tick("Working", terminal_renderer.color_theme(), &mut out)
1127+
.expect("tick succeeds");
1128+
1129+
let mut failing_out = AlwaysFailWriter;
1130+
let result = spinner.fail("Done", terminal_renderer.color_theme(), &mut failing_out);
1131+
1132+
assert!(result.is_err());
1133+
assert!(spinner.cursor_hidden);
1134+
}
10841135
}

0 commit comments

Comments
 (0)