mirror of
https://github.com/lotabout/skim.git
synced 2026-09-10 07:16:23 -04:00
fix: stream preview command instead of waiting for completion
closes #1174
This commit is contained in:
parent
a177153700
commit
0f2322970b
|
|
@ -35,17 +35,35 @@ const VT_SCROLLBACK: usize = 100_000;
|
|||
type PlainChild = Arc<Mutex<Option<Child>>>;
|
||||
|
||||
fn read_bounded(mut reader: impl Read) -> Vec<u8> {
|
||||
read_bounded_with_updates(&mut reader, |_| {})
|
||||
}
|
||||
|
||||
fn read_bounded_with_updates(mut reader: impl Read, mut update: impl FnMut(&[u8])) -> Vec<u8> {
|
||||
const UPDATE_INTERVAL: Duration = Duration::from_millis(16);
|
||||
|
||||
let mut output = Vec::with_capacity(PREVIEW_MAX_BYTES);
|
||||
let mut buffer = [0; 8192];
|
||||
let mut last_update = None;
|
||||
let mut published_len = 0;
|
||||
loop {
|
||||
match reader.read(&mut buffer) {
|
||||
Ok(0) | Err(_) => break,
|
||||
Ok(read) => {
|
||||
let retained = PREVIEW_MAX_BYTES.saturating_sub(output.len()).min(read);
|
||||
output.extend_from_slice(&buffer[..retained]);
|
||||
|
||||
let update_due = last_update.is_none_or(|last: Instant| last.elapsed() >= UPDATE_INTERVAL);
|
||||
if retained > 0 && (update_due || output.len() == PREVIEW_MAX_BYTES) {
|
||||
update(&output);
|
||||
published_len = output.len();
|
||||
last_update = Some(Instant::now());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if output.len() != published_len {
|
||||
update(&output);
|
||||
}
|
||||
output
|
||||
}
|
||||
|
||||
|
|
@ -570,7 +588,17 @@ impl Preview {
|
|||
self.plain_child = Some(child.clone());
|
||||
|
||||
self.thread_handle = Some(std::thread::spawn(move || {
|
||||
let stdout_reader = std::thread::spawn(move || read_bounded(stdout));
|
||||
let streaming_content = content.clone();
|
||||
let stdout_reader = std::thread::spawn(move || {
|
||||
read_bounded_with_updates(stdout, |output| {
|
||||
let Ok(text) = output.to_vec().into_text() else {
|
||||
return;
|
||||
};
|
||||
if let Ok(mut content) = streaming_content.write() {
|
||||
*content = PreviewContent::Text(text);
|
||||
}
|
||||
})
|
||||
});
|
||||
let stderr_reader = std::thread::spawn(move || read_bounded(stderr));
|
||||
|
||||
let status = loop {
|
||||
|
|
|
|||
|
|
@ -86,6 +86,42 @@ fn bounded_reader_discards_output_after_limit() {
|
|||
assert_eq!(output.len(), PREVIEW_MAX_BYTES);
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn plain_preview_streams_before_command_exits() {
|
||||
use ratatui::backend::TestBackend;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
let mut preview = Preview::default();
|
||||
preview.pty = None;
|
||||
let mut tui =
|
||||
super::super::Tui::new_with_height_and_backend(TestBackend::new(20, 5), super::super::Size::Percent(100))
|
||||
.unwrap();
|
||||
preview.spawn(&mut tui, "printf streamed; sleep 30").unwrap();
|
||||
|
||||
let started = Instant::now();
|
||||
loop {
|
||||
let has_streamed_output = preview.content.read().is_ok_and(|content| match &*content {
|
||||
PreviewContent::Text(text) => text
|
||||
.lines
|
||||
.iter()
|
||||
.any(|line| line.spans.iter().any(|span| span.content.as_ref().contains("streamed"))),
|
||||
_ => false,
|
||||
});
|
||||
if has_streamed_output {
|
||||
break;
|
||||
}
|
||||
assert!(
|
||||
started.elapsed() < Duration::from_secs(2),
|
||||
"preview output did not stream"
|
||||
);
|
||||
std::thread::sleep(Duration::from_millis(10));
|
||||
}
|
||||
|
||||
preview.kill();
|
||||
preview.thread_handle.take().unwrap().join().unwrap();
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn plain_preview_can_be_cancelled() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue