use crossterm::{
QueueableCommand,
cursor::{self, position},
event::{Event, EventStream, KeyCode, KeyEvent, KeyEventKind, KeyModifiers},
style::Print,
terminal::{self, disable_raw_mode},
};
use futures_util::{FutureExt, StreamExt, select};
use grapheme_utils::*;
use historybuffer::HistoryBuffer;
use std::{
io::{self, Stdout, Write, stdout},
ops::DerefMut,
rc::Rc,
string::String,
};
use thingbuf::mpsc::{Receiver, Sender, errors::TrySendError};
use unicode_segmentation::UnicodeSegmentation;
mod error;
pub use self::error::{Error, Result};
const HISTORY_BUFFER_SIZE: usize = 300 * 160 * 4;
#[derive(Debug)]
pub enum EditorEvent {
CtrlC,
CtrlD,
CtrlQ,
CtrlN,
CtrlS,
CtrlX,
}
pub enum WriteHistoryType {
PageUp,
PageDown,
Quit,
}
pub struct AsyncEditor {
event_stream: EventStream, stdout_rx: Receiver<Vec<u8>>, editor: Editor, }
impl AsyncEditor {
pub fn new(
initial_content: &str,
split_prompt: String,
print_height: f32,
tabstop: u8,
) -> Result<(Self, SharedStdout)> {
let (stdout_tx, stdout_rx) = thingbuf::mpsc::channel(500);
let editor = Editor::new(initial_content, split_prompt, print_height, tabstop)?;
let mut async_editor = AsyncEditor {
event_stream: EventStream::new(),
stdout_rx,
editor,
};
async_editor.editor.term.queue(terminal::EnableLineWrap)?;
async_editor.editor.term.flush()?;
Ok((
async_editor,
SharedStdout {
buf: Vec::new(),
stdout_tx: stdout_tx,
},
))
}
pub fn flush(&mut self) -> Result<()> {
while let Ok(buf) = self.stdout_rx.try_recv_ref() {
self.editor.writeout(&buf)?;
}
self.editor.term.flush()?;
Ok(())
}
pub async fn async_editor(&mut self) -> Result<EditorEvent> {
loop {
select! {
event = self.event_stream.next().fuse() => match event {
Some(Ok(event)) => {
match self.editor.handle_event(event) {
Ok(Some(event)) => {
self.editor.term.flush()?;
return Result::<_>::Ok(event) },
Err(e) => return Err(e),
Ok(None) => self.editor.term.flush()?,
}
}
Some(Err(e)) => return Err(e.into()),
None => {},
},
result = self.stdout_rx.recv_ref().fuse() => match result {
Some(buf) => {
self.editor.writeout(&buf)?;
self.editor.term.flush()?;
},
None => return Err(Error::SharedStdoutClosed),
},
}
}
}
pub fn text(&self) -> String {
self.editor.text()
}
}
fn string_to_hex(s: &str, maxlen: usize) -> String {
let mut new_hex_string = String::with_capacity(s.as_bytes().len() * 2);
for byte in s.as_bytes().iter() {
let s = format!("{:02x} ", byte);
new_hex_string.push_str(&s);
if new_hex_string.len() >= maxlen {
return new_hex_string[..maxlen].to_string();
}
}
new_hex_string
}
pub struct Editor {
curx: u16, cury: u16,
hb_active: bool,
hb_start_index: usize,
hb_end_index: usize,
histbuf: HistoryBuffer, lidx: usize,
lines: Vec<String>, lineidx: usize, lofs: usize,
loose_cursor: bool, printlines: u16, printx: u16, printy: u16,
scrollstart: usize,
sizex: u16, sizey: u16,
split_prompt: String,
tabstop: u8,
term: Stdout,
tmpbuf: Rc<String>,
}
impl Editor {
pub fn new(
initial_content: &str,
split_prompt: String,
print_height: f32,
tabstop: u8,
) -> Result<Self> {
let term = stdout();
let (sizex, sizey) = terminal::size()?;
let (_curx, cury) = position()?;
let newprintlines = (sizey as f32 * print_height.max(0.1).min(0.9)) as u16;
terminal::enable_raw_mode()?;
Ok(Self {
curx: 0,
cury: newprintlines + 2,
hb_active: false,
hb_start_index: 0,
hb_end_index: 0,
histbuf: HistoryBuffer::new(HISTORY_BUFFER_SIZE), lidx: 0, lines: initial_content.split("\n").map(|s| s.to_string()).collect(), lineidx: 0,
lofs: 0, loose_cursor: false,
printlines: newprintlines,
printx: 0,
printy: cury + 1,
scrollstart: 0,
sizex: sizex,
sizey: sizey,
split_prompt: split_prompt,
tabstop: tabstop,
term: term,
tmpbuf: Rc::new(String::new()), })
}
fn ch(&self, idx: usize) -> char {
grapheme_at_idx(&self.lines[self.lineidx], idx)
.chars()
.next()
.unwrap_or('\0')
}
fn grapheme_idx_at_idx(&self, idx: usize) -> usize {
grapheme_idx_at_idx(&self.lines[self.lineidx], idx)
}
fn grapheme_width_lofs_to_lidx(&self) -> u16 {
let st = &self.lines[self.lineidx][self.lofs..self.lidx];
if !st.contains('\t') {
return string_width(&st) as u16;
}
let ofs = string_width(&self.lines[self.lineidx][..self.lofs]) % self.tabstop as usize;
let mut char_width;
let mut width = 0;
for (_, g) in st.grapheme_indices(true) {
if g == "\t" {
let ts = self.tabstop as usize;
char_width = ts - ((width + ofs) % ts);
} else {
char_width = string_width(g);
}
width += char_width;
}
return width as u16;
}
pub fn handle_event(&mut self, event: Event) -> Result<Option<EditorEvent>> {
match event {
Event::Key(KeyEvent {
code,
modifiers: KeyModifiers::CONTROL,
kind: KeyEventKind::Press,
..
}) => match code {
KeyCode::Char('a') => {
self.lidx = 0;
self.lofs = 0;
self.setpos()?;
}
KeyCode::Char('c') => {
return Ok(Some(EditorEvent::CtrlC));
}
KeyCode::Char('d') => {
return Ok(Some(EditorEvent::CtrlD));
}
KeyCode::Char('e') => {
self.move_end()?;
}
KeyCode::Char('l') => {
self.printx = 0;
self.printy = 0;
self.redraw()?;
}
KeyCode::Char('n') => {
return Ok(Some(EditorEvent::CtrlS));
}
KeyCode::Char('q') => {
return Ok(Some(EditorEvent::CtrlQ));
}
KeyCode::Char('s') => {
return Ok(Some(EditorEvent::CtrlS));
}
KeyCode::Char('x') => {
return Ok(Some(EditorEvent::CtrlX));
}
KeyCode::Char('u') => {
self.lines[self.lineidx].drain(0..self.lidx);
self.redraw()?;
}
KeyCode::Down => {
self.resize_split(3)?;
}
KeyCode::End => {
self.lineidx = self.lines.len().saturating_sub(1);
self.scrollstart = self.lineidx; self.cury = self.printlines + 2; self.lidx = self.len();
self.setpos()?;
self.redraw()?;
}
KeyCode::Home => {
self.lineidx = 0;
self.scrollstart = 0;
self.cury = self.printlines + 2;
self.lidx = 0;
self.setpos()?;
self.redraw()?;
}
KeyCode::Left => {
if self.lidx == 0 {
self.move_up(1, true)?;
self.lidx = self.len();
}
while self.lidx > 0 && self.prev_char(self.lidx).is_whitespace() {
self.lidx = self.prev_grapheme_idx_from_idx(self.lidx);
}
while self.lidx > 0 && !self.prev_char(self.lidx).is_whitespace() {
self.lidx = self.prev_grapheme_idx_from_idx(self.lidx);
}
self.setpos()?;
}
KeyCode::PageDown => {
self.writehistory(WriteHistoryType::PageDown)?;
}
KeyCode::PageUp => {
if !self.hb_active {
self.hb_active = true;
self.hb_start_index = self.histbuf.get_last_index();
self.hb_end_index = self.hb_start_index;
} else {
if self.hb_start_index == 0 {
return Ok(None);
}
}
self.writehistory(WriteHistoryType::PageUp)?;
}
KeyCode::Right => {
while self.lidx < self.len() && !self.ch(self.lidx).is_whitespace() {
self.lidx = self.next_grapheme_idx_from_idx(self.lidx);
}
if self.lidx == self.len() {
self.move_down(1, true)?;
self.lidx = 0;
}
while self.lidx < self.len() && self.ch(self.lidx).is_whitespace() {
self.lidx = self.next_grapheme_idx_from_idx(self.lidx);
}
self.setpos()?;
}
KeyCode::Up => {
self.resize_split(-3)?;
}
_ => {}
},
Event::Key(KeyEvent {
code,
modifiers: _,
kind: KeyEventKind::Press,
..
}) => match code {
KeyCode::Backspace => {
if self.lidx == 0 {
if self.lineidx == 0 {
return Ok(None);
}
self.lidx = self.lines[self.lineidx - 1].len();
let s = self.lines[self.lineidx].clone();
self.lines[self.lineidx - 1].push_str(&s);
self.lines.remove(self.lineidx);
self.move_up(1, false)?;
self.setpos()?;
self.redraw()?;
} else {
if self.lidx > self.len() {
self.lidx = self.len();
self.lofs = 0;
}
let start = self.prev_grapheme_idx_from_idx(self.lidx);
let mut gwid = self.grapheme_width_lofs_to_lidx(); self.lines[self.lineidx].replace_range(start..self.lidx, "");
self.lidx = start;
gwid = gwid.saturating_sub(self.grapheme_width_lofs_to_lidx());
self.curx = self.curx.saturating_sub(gwid);
self.redrawline()?;
}
}
KeyCode::Char(c) => {
self.insert_charstr(&c.to_string())?;
}
KeyCode::Delete => {
if self.lidx == self.len() {
if self.lineidx + 1 < self.lines.len() {
let s = self.lines[self.lineidx + 1].clone();
self.lines[self.lineidx].push_str(&s);
self.lines.remove(self.lineidx + 1);
self.redraw()?;
}
} else {
let end = self.next_grapheme_idx_from_idx(self.lidx);
self.lines[self.lineidx].replace_range(self.lidx..end, "");
self.redrawline()?;
}
}
KeyCode::Down => {
self.move_down(1, false)?;
self.redraw()?;
}
KeyCode::End => {
self.move_end()?;
}
KeyCode::Esc => {
self.writehistory(WriteHistoryType::Quit)?;
self.hb_active = false;
}
KeyCode::Enter => {
if self.lidx > self.len() {
self.lidx = self.len();
}
self.lines.insert(
self.lineidx + 1,
self.lines[self.lineidx][self.lidx..].to_string(),
);
self.lines[self.lineidx].drain(self.lidx..);
self.move_down(1, true)?;
self.redraw()?;
}
KeyCode::Home => {
self.lidx = 0;
self.lofs = 0;
self.setpos()?;
}
KeyCode::Left => {
if self.lidx == 0 {
if self.lineidx == 0 {
return Ok(None);
}
self.move_up(1, true)?;
self.lidx = self.len();
} else {
self.lidx = self.prev_grapheme_idx_from_idx(self.lidx);
}
self.setpos()?;
}
KeyCode::PageDown => {
let numlines = self.sizey - self.printlines - 2;
self.move_down(numlines, false)?;
}
KeyCode::PageUp => {
let numlines = self.sizey - self.printlines - 2;
self.move_up(numlines, false)?;
}
KeyCode::Right => {
if self.lidx >= self.len() {
if self.lineidx + 1 == self.lines.len() {
return Ok(None);
}
self.move_down(1, true)?;
self.lidx = 0;
} else {
self.lidx = self.next_grapheme_idx_from_idx(self.lidx);
}
self.setpos()?;
}
KeyCode::Tab => {
self.insert_charstr("\t")?;
}
KeyCode::Up => {
self.move_up(1, false)?;
}
_ => {}
},
Event::Resize(x, y) => {
let curp: f32 = (self.printlines as f32 / self.sizey as f32)
.max(0.9)
.min(0.1);
let delta: i16 = (curp * y as f32) as i16 - self.printlines as i16;
self.sizex = x;
self.sizey = y;
self.resize_split(delta)?;
self.sizey = y;
}
_ => {}
}
if false {
self.split_prompt = string_to_hex(&self.lines[self.lineidx], 40);
self.redraw()?;
}
self.term.queue(cursor::MoveTo(self.curx, self.cury))?;
self.term.flush()?;
Ok(None)
}
fn insert_charstr(&mut self, ch: &str) -> Result<()> {
if self.lidx > self.len() {
self.lidx = self.len();
self.lofs = 0;
}
self.lines[self.lineidx].insert_str(self.lidx, ch);
self.lidx = self.next_grapheme_idx_from_idx(self.lidx);
self.curx = self.grapheme_width_lofs_to_lidx();
self.redrawline()?;
Ok(())
}
fn len(&mut self) -> usize {
self.lines[self.lineidx].len()
}
fn matchpos(&mut self) -> Result<()> {
self.setpos()?;
Ok(())
}
fn move_down(&mut self, num: u16, move_to_beginning: bool) -> Result<()> {
self.loose_cursor = true;
if self.lineidx + 1 == self.lines.len() && self.scrollstart + 1 == self.lines.len() as usize
{
self.lidx = self.len();
self.setpos()?;
self.redrawline()?;
return Ok(());
}
let virtidx;
if self.scrollstart
<= (self.sizey as usize + 2).saturating_sub((self.cury + self.printlines) as usize)
{
virtidx = (self.cury - self.printlines) as usize - 2;
} else {
virtidx = self.scrollstart + (self.sizey - self.printlines) as usize - 3;
}
self.cury += num;
self.lineidx = (self.lineidx + num as usize).min(self.lines.len().saturating_sub(1));
if self.cury > self.sizey - 1 || self.lineidx + 1 == self.lines.len() {
if num > 10 {
self.scrollstart = (self.scrollstart + num as usize).min(self.lineidx);
} else {
self.scrollstart = (virtidx + num as usize + self.printlines as usize + 3)
.saturating_sub(self.sizey as usize)
.min(self.lineidx);
}
}
self.cury = self
.cury
.min((self.lineidx - self.scrollstart) as u16 + self.printlines + 2)
.min(self.sizey - 1);
if move_to_beginning {
self.lidx = 0;
self.lofs = 0;
self.curx = 0;
} else {
self.matchpos()?;
}
self.redraw()?;
Ok(())
}
fn move_end(&mut self) -> Result<()> {
self.lidx = self.len();
self.setpos()?;
self.redrawline()?;
Ok(())
}
fn move_up(&mut self, num: u16, move_to_end: bool) -> Result<()> {
self.loose_cursor = true;
if self.lineidx == 0 && self.scrollstart == 0 {
self.lofs = 0;
self.lidx = 0;
self.curx = 0;
self.redrawline()?;
return Ok(());
}
self.lineidx = self.lineidx.saturating_sub(num as usize);
if self.cury == self.printlines + 2 || self.lineidx < self.scrollstart {
self.scrollstart = self.scrollstart.saturating_sub(num as usize);
}
self.cury = (self.lineidx - self.scrollstart) as u16 + self.printlines + 2; if move_to_end {
self.lidx = self.len();
}
self.matchpos()?;
self.redraw()?;
Ok(())
}
fn next_grapheme_from_idx(&self, idx: usize) -> &str {
next_grapheme_from_idx(&self.lines[self.lineidx], idx)
}
fn next_grapheme_idx_from_idx(&self, idx: usize) -> usize {
next_grapheme_idx_from_idx(&self.lines[self.lineidx], idx)
}
fn prev_char(&self, idx: usize) -> char {
self.prev_grapheme_from_idx(idx)
.chars()
.next()
.unwrap_or('\0')
}
fn prev_grapheme_from_idx(&self, idx: usize) -> &str {
prev_grapheme_from_idx(&self.lines[self.lineidx], idx)
}
fn prev_grapheme_idx_from_idx(&self, idx: usize) -> usize {
prev_grapheme_idx_from_idx(&self.lines[self.lineidx], idx)
}
pub fn redraw(&mut self) -> Result<()> {
let tbuf = Rc::get_mut(&mut self.tmpbuf).ok_or(Error::RedrawRcError)?;
tbuf.clear();
let s = format!(
"===== AsyncEditor ========== {} == Ctrl ⬅️ / ⮕ / ⬆️/ / ⬇️ == Ctrl-PgUp/Ctrl-PgDn ",
self.split_prompt
);
let extend_count = (self.sizex as usize).saturating_sub(string_width(&s));
self.term
.queue(cursor::MoveTo(0, self.printlines + 1 as u16))?;
self.term
.queue(terminal::Clear(terminal::ClearType::FromCursorDown))?;
self.term.queue(Print(&format!(
"{}{}\n",
s,
std::iter::repeat("=")
.take(extend_count)
.collect::<String>()
)))?;
self.term.queue(cursor::MoveToColumn(0))?;
let end_index = (self.scrollstart
+ (self.sizey.saturating_sub(self.printlines + 2) as usize))
.min(self.lines.len());
for lidx in self.scrollstart..end_index {
if lidx == self.lineidx {
self.redrawline()?;
if lidx != end_index - 1 {
self.term.queue(cursor::MoveToNextLine(1))?;
}
continue;
}
let line = &self.lines[lidx];
let maxwidth = self.sizex as usize - 1;
let stwidth = string_width(line);
let mut width = 0usize;
let mut char_width;
let mut s = String::with_capacity(200);
let mut news = String::with_capacity(200);
match (stwidth > maxwidth, line.contains('\t')) {
(false, false) => {
self.term.queue(Print(&line))?;
}
(_, _) => {
s.clear();
for (_, g) in line.grapheme_indices(true) {
news.clear();
if g == "\t" {
let ts = self.tabstop as usize;
char_width = ts - (width % ts);
let tab_arrow_string: String =
std::iter::repeat("→").take(char_width as usize).collect();
news.push_str(&tab_arrow_string);
} else {
char_width = string_width(g);
news.push_str(g);
}
if width + char_width as usize > maxwidth {
break;
}
s.push_str(&news);
width += char_width;
}
self.term.queue(Print(&s))?;
if stwidth > maxwidth {
self.term.queue(cursor::MoveToColumn(self.sizex - 1))?;
self.term.queue(Print(&'>'))?;
}
}
}
if lidx != end_index - 1 {
self.term.queue(cursor::MoveToNextLine(1))?;
}
}
self.term.queue(cursor::MoveTo(self.curx, self.cury))?;
self.term.flush()?;
Ok(())
}
fn redrawline(&mut self) -> Result<()> {
self.term
.queue(terminal::Clear(terminal::ClearType::CurrentLine))?;
self.term.queue(cursor::MoveToColumn(0))?;
let start = if self.lofs > self.len() {
0
} else {
self.grapheme_idx_at_idx(self.lofs)
};
let line = &self.lines[self.lineidx][start..];
let maxwidth = self.sizex as usize - 2;
let stwidth = string_width(line);
let mut width = 0usize;
let mut char_width;
let mut s = String::with_capacity(200);
let mut news = String::with_capacity(200);
match (stwidth > maxwidth, line.contains('\t')) {
(false, false) => {
self.term.queue(Print(&line))?;
}
(_, _) => {
s.clear();
for (_, g) in line.grapheme_indices(true) {
news.clear();
if g == "\t" {
let ts = self.tabstop as usize;
char_width = ts - (width % ts);
let tab_arrow_string: String =
std::iter::repeat("→").take(char_width as usize).collect();
news.push_str(&tab_arrow_string);
} else {
char_width = string_width(g);
news.push_str(g);
}
if width + char_width as usize > maxwidth + 1 {
break;
}
s.push_str(&news);
width += char_width;
}
self.term.queue(Print(&s))?;
}
}
self.term.queue(cursor::MoveTo(self.curx, self.cury))?;
Ok(())
}
fn resize_split(&mut self, delta: i16) -> Result<()> {
self.term
.queue(cursor::MoveTo(self.printx, self.printy as u16))?;
self.term
.queue(terminal::Clear(terminal::ClearType::FromCursorDown))?;
let pre = self.printlines as i16;
self.printlines = (self.printlines as i16 + delta)
.max((self.sizey >> 3) as i16)
.min(self.sizey as i16 - 8) as u16;
let new_delta = self.printlines as i16 - pre;
self.cury = (self.cury as i16 + new_delta).max(self.printlines as i16 + 2) as u16;
while self.cury >= self.sizey {
self.cury -= 1;
self.lineidx -= 1;
}
self.setpos()?;
self.writebuf(&[])?;
Ok(())
}
fn setpos(&mut self) -> Result<()> {
let maxwidth = self.sizex - 1;
self.lidx = self.grapheme_idx_at_idx(self.lidx);
if self.loose_cursor {
if self.lidx < (self.lofs + (self.sizey << 2) as usize + 15) {
self.lofs = 0;
} else {
self.lofs = self.grapheme_idx_at_idx(self.lofs); }
}
if self.lidx < self.lofs {
self.lofs = 0;
}
self.loose_cursor = false;
let mut stwidth = self.grapheme_width_lofs_to_lidx();
loop {
if stwidth <= maxwidth {
self.curx = stwidth;
return Ok(());
}
self.lofs = self.next_grapheme_idx_from_idx(self.lofs);
stwidth = self.grapheme_width_lofs_to_lidx();
}
}
pub fn text(&self) -> String {
self.lines.join("\n")
}
fn writebuf(&mut self, buf: &[u8]) -> Result<()> {
for line in buf.split_inclusive(|b| *b == b'\n') {
self.term.write_all(line)?;
self.term.flush()?;
if line.ends_with(b"\n") {
self.term.queue(cursor::MoveToColumn(0))?;
self.printx = 0;
self.printy += 1;
} else {
let mut x = self.printx as usize + line.len(); while x > self.sizex as usize {
x -= self.sizex as usize;
self.printy += 1;
}
self.printx = x as u16;
}
}
self.printy = self.printy.min(self.sizey);
if self.printy > self.printlines {
self.term
.queue(terminal::ScrollUp(self.printy - self.printlines))?;
self.printy = self.printlines;
}
self.redraw()?;
Ok(())
}
fn writehistory(&mut self, write_history_type: WriteHistoryType) -> Result<()> {
self.term.queue(cursor::MoveTo(0, 0))?;
self.term.queue(terminal::Clear(terminal::ClearType::All))?;
self.printx = 0;
self.printy = 0;
let mut linecnt = self.sizex;
let mut num_lines = 0;
let mut buf = Vec::<u8>::with_capacity(self.printlines as usize * self.sizex as usize);
let mut revbuf: Vec<u8> = vec![];
match write_history_type {
WriteHistoryType::PageDown => {
self.hb_start_index = self.hb_end_index;
while let Some(ch) = self.histbuf.get(self.hb_start_index + buf.len()) {
buf.push(ch);
linecnt -= 1;
if ch == b'\n' || linecnt == 0 {
linecnt = self.sizex - 1;
num_lines += 1;
if num_lines >= self.printlines {
break;
}
}
}
self.hb_end_index = self.hb_start_index + buf.len();
if num_lines < self.printlines {
buf = self
.histbuf
.get_recent(self.printlines as usize * self.sizex as usize);
self.writebuf(&buf)?;
self.hb_active = false;
} else {
self.writebuf(&buf)?;
}
}
WriteHistoryType::PageUp => {
self.hb_end_index = self.hb_start_index;
while let Some(ch) = self.histbuf.get(self.hb_start_index) {
buf.push(ch);
linecnt -= 1;
if ch == b'\n' || linecnt == 0 {
linecnt = self.sizex - 1;
num_lines += 1;
if num_lines >= self.printlines {
break;
}
}
if self.hb_start_index == 0 {
break;
}
self.hb_start_index = self.hb_start_index.saturating_sub(1);
}
if self.hb_start_index == 0 {
let mut linecnt = self.sizex;
let mut num_lines = 0;
revbuf =
Vec::<u8>::with_capacity(self.printlines as usize * self.sizex as usize);
while let Some(ch) = self.histbuf.get(self.hb_start_index + revbuf.len()) {
revbuf.push(ch);
linecnt -= 1;
if ch == b'\n' || linecnt == 0 {
linecnt = self.sizex - 1;
num_lines += 1;
if num_lines >= self.printlines {
break;
}
}
}
self.hb_end_index = revbuf.len();
} else {
revbuf = buf.into_iter().rev().collect();
}
self.writebuf(&revbuf)?;
}
WriteHistoryType::Quit => {
buf = self
.histbuf
.get_recent(self.printlines as usize * self.sizex as usize);
self.writebuf(&buf)?;
self.hb_active = false;
}
}
Ok(())
}
fn writeout(&mut self, buf: &[u8]) -> Result<()> {
self.histbuf.add(buf);
if !self.hb_active {
self.term
.queue(cursor::MoveTo(self.printx, self.printy as u16))?;
self.term
.queue(terminal::Clear(terminal::ClearType::FromCursorDown))?;
self.writebuf(buf)?;
}
Ok(())
}
}
impl Drop for Editor {
fn drop(&mut self) {
let _ = disable_raw_mode();
self.term.queue(cursor::MoveTo(0, self.sizey - 1)).unwrap();
self.term.queue(cursor::MoveToNextLine(1)).unwrap();
self.term.flush().unwrap();
}
}
#[pin_project::pin_project]
pub struct SharedStdout {
#[pin]
buf: Vec<u8>,
stdout_tx: Sender<Vec<u8>>,
}
impl io::Write for SharedStdout {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.buf.extend_from_slice(buf);
if true {
match self.stdout_tx.try_send_ref() {
Ok(mut send_buf) => {
std::mem::swap(send_buf.deref_mut(), &mut self.buf);
self.buf.clear();
}
Err(TrySendError::Full(_)) => return Err(io::ErrorKind::WouldBlock.into()),
_ => {
return Err(io::Error::new(
io::ErrorKind::Other,
"io Error: ThingBuf Receiver Closed",
));
}
}
}
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}