use std::{cell::RefCell, ops::Range, rc::Rc};
use gpui::{Bounds, Hsla, Pixels, TextLayout};
pub(crate) trait EditorSink {
fn report(&self, key: u64, layout: &TextLayout);
fn begin_pieces(&self, key: u64);
fn report_text_piece(&self, key: u64, start: usize, layout: &TextLayout);
fn report_box_piece(&self, key: u64, range: Range<usize>, bounds: Bounds<Pixels>);
fn report_empty_piece(&self, key: u64, start: usize, bounds: Bounds<Pixels>);
fn backgrounds(&self, key: u64) -> Vec<(Range<usize>, Hsla)>;
}
thread_local! {
static SINK: RefCell<Option<Rc<dyn EditorSink>>> = const { RefCell::new(None) };
}
pub(crate) fn with_sink<R>(sink: Rc<dyn EditorSink>, f: impl FnOnce() -> R) -> R {
struct Restore(Option<Rc<dyn EditorSink>>);
impl Drop for Restore {
fn drop(&mut self) {
let previous = self.0.take();
SINK.with(|s| *s.borrow_mut() = previous);
}
}
let _restore = Restore(SINK.with(|s| s.replace(Some(sink))));
f()
}
pub(crate) fn current() -> Option<Rc<dyn EditorSink>> {
SINK.with(|s| s.borrow().clone())
}
#[cfg(test)]
mod tests {
use super::*;
struct Nop;
impl EditorSink for Nop {
fn report(&self, _: u64, _: &TextLayout) {}
fn begin_pieces(&self, _: u64) {}
fn report_text_piece(&self, _: u64, _: usize, _: &TextLayout) {}
fn report_box_piece(&self, _: u64, _: Range<usize>, _: Bounds<Pixels>) {}
fn report_empty_piece(&self, _: u64, _: usize, _: Bounds<Pixels>) {}
fn backgrounds(&self, _: u64) -> Vec<(Range<usize>, Hsla)> {
Vec::new()
}
}
#[test]
fn with_sink_restores_on_panic() {
let result = std::panic::catch_unwind(|| {
with_sink(Rc::new(Nop), || {
assert!(current().is_some());
panic!("boom");
})
});
assert!(result.is_err());
assert!(current().is_none(), "sink is cleared after a panic");
}
}