Phase 5: IDE-Rahmen implementieren, synchronisieren und archivieren
This commit is contained in:
102
crates/tb-ide/src/terminal.rs
Normal file
102
crates/tb-ide/src/terminal.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
use crossterm::{
|
||||
cursor::{Hide, Show},
|
||||
event::{DisableMouseCapture, EnableMouseCapture},
|
||||
execute,
|
||||
style::ResetColor,
|
||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||
};
|
||||
use std::io::{self, Write};
|
||||
|
||||
/// Ein Besitzer für Raw Mode und Terminalzustand, auch bei teilweiser Initialisierung.
|
||||
pub struct TerminalGuard<W: Write> {
|
||||
writer: W,
|
||||
raw: fn(bool) -> io::Result<()>,
|
||||
}
|
||||
impl<W: Write> TerminalGuard<W> {
|
||||
pub fn enter(writer: W) -> io::Result<Self> {
|
||||
Self::with_raw(writer, |on| {
|
||||
if on {
|
||||
enable_raw_mode()
|
||||
} else {
|
||||
disable_raw_mode()
|
||||
}
|
||||
})
|
||||
}
|
||||
fn with_raw(writer: W, raw: fn(bool) -> io::Result<()>) -> io::Result<Self> {
|
||||
let mut guard = Self { writer, raw };
|
||||
(guard.raw)(true)?;
|
||||
execute!(guard.writer, EnterAlternateScreen, EnableMouseCapture, Hide)?;
|
||||
Ok(guard)
|
||||
}
|
||||
}
|
||||
impl<W: Write> Drop for TerminalGuard<W> {
|
||||
fn drop(&mut self) {
|
||||
let _ = (self.raw)(false);
|
||||
let _ = execute!(
|
||||
self.writer,
|
||||
ResetColor,
|
||||
Show,
|
||||
DisableMouseCapture,
|
||||
LeaveAlternateScreen
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
rc::Rc,
|
||||
sync::atomic::{AtomicI32, Ordering},
|
||||
};
|
||||
static RAW: AtomicI32 = AtomicI32::new(0);
|
||||
#[derive(Clone)]
|
||||
struct Output(Rc<RefCell<Vec<u8>>>);
|
||||
impl Write for Output {
|
||||
fn write(&mut self, b: &[u8]) -> io::Result<usize> {
|
||||
self.0.borrow_mut().extend_from_slice(b);
|
||||
Ok(b.len())
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
fn raw(on: bool) -> io::Result<()> {
|
||||
RAW.store(i32::from(on), Ordering::SeqCst);
|
||||
Ok(())
|
||||
}
|
||||
#[test]
|
||||
fn terminal_cleanup_on_normal_error_and_partial_initialization() {
|
||||
for fail in [false, true] {
|
||||
let out = Output(Rc::default());
|
||||
let copy = out.clone();
|
||||
let result = (|| -> io::Result<()> {
|
||||
let _guard = TerminalGuard::with_raw(out, raw)?;
|
||||
if fail {
|
||||
return Err(io::Error::other("Renderfehler"));
|
||||
}
|
||||
Ok(())
|
||||
})();
|
||||
assert_eq!(result.is_err(), fail);
|
||||
assert_eq!(RAW.load(Ordering::SeqCst), 0);
|
||||
let s = String::from_utf8(copy.0.borrow().clone()).unwrap();
|
||||
assert!(s.contains("?1049h") && s.contains("?1049l") && s.contains("?25h"));
|
||||
}
|
||||
let out = Output(Rc::default());
|
||||
let copy = out.clone();
|
||||
assert!(TerminalGuard::with_raw(out, |on| {
|
||||
raw(on)?;
|
||||
if on {
|
||||
Err(io::Error::other("Raw-Fehler"))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
})
|
||||
.is_err());
|
||||
assert_eq!(RAW.load(Ordering::SeqCst), 0);
|
||||
assert!(String::from_utf8(copy.0.borrow().clone())
|
||||
.unwrap()
|
||||
.contains("?25h"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user