async_editor/
lib.rs

1//! AsyncEditor
2//! Async Console Editor that supports asyncronous output to a resizeable print window. Perfect for an AI Chat Interface.
3//!
4//! Keyboard Commands:
5//!
6//! - Arrows, PgUp, PgDn => Move
7//! todo - Ctrl-W: Erase the input from the cursor to the previous whitespace
8//! todo - Ctrl-U: Erase the input before the cursor
9//! - Ctrl-L: Clear the screen
10//! - Ctrl-Left / Ctrl-Right: Move to previous/next whitespace
11//! - Home: Jump to the start of the line
12//! - End: Jump to the end of the line
13//! - Ctrl-Up, Ctrl-Down:  Contract / Expand Print vs Edit windows
14//! - Ctrl-C: Ignored
15//!   Ctrl Left/Right => Move Left/Right by Word
16//! - Ctrl PgUp / PgDn - Print History Scrollback, ESC to exit.
17//! in dev - Ctrl-k => Delete current line
18//!   Ctrl-C, Ctrl-D, Ctrl-Q, Ctrl-X => Exit/Quit
19//!
20//! Note: this works, but doctest will fail, so doc test have been disabled in Cargo.toml
21//! ```rust
22//! use async_editor::{EditorEvent, AsyncEditor, Result};
23//! use std::io::Write;
24//! use std::time::Duration;
25//! use tokio::time::sleep;
26//!
27//! #[tokio::main]
28//! async fn main() -> Result<()> {
29//!   let mut st = r###"LInitial String"###;
30//!   let st = st.to_string();
31//!   let (mut async_editor, mut async_editor_stdout) = AsyncEditor::new(&st, "Ctrl-C/D/Q/X to Quit".to_string(), 0.5f32, 4)?;
32//!   let mut count: u32 = 0;
33//!
34//!   loop {
35//! 	tokio::select! {
36//! 	_ = sleep(Duration::from_millis(200)) => {
37//! 		count += 1;
38//! 		//write!(async_editor_stdout, "{}", format!("_Message {}\nreceived\n!", count))?;
39//! 		write!(async_editor_stdout, "{}", format!("_Message {} received!", count))?;
40//! 	}
41//! 	cmd = async_editor.async_editor() => match cmd {
42//! 		Ok(EditorEvent::CtrlC | EditorEvent::CtrlD | EditorEvent::CtrlQ | EditorEvent::CtrlX) => {
43//! 			break;
44//! 		}
45//! 		Ok(EditorEvent::CtrlS) => {
46//! 			writeln!(async_editor_stdout, "\n\nCtrlS\n")?;
47//! 		}
48//! 		Ok(_) => {continue;}
49//! 			Err(e) => {
50//! 				writeln!(async_editor_stdout, "\n\nError: {e:?}\n")?;
51//! 				break;
52//! 			}
53//! 		}
54//! 	}
55//! 	async_editor.flush()?;
56//!   }
57//!   async_editor.flush()?;
58//!
59//!   let text = async_editor.text();
60//!   drop(async_editor);
61//!
62//!   println!("\n\nEdited Text:\n{}",text);
63//!   Ok(())
64//! }
65//!```
66
67
68// Note: Extended Grapheme Clusters are NOT rust char, and cannot be converted
69//       to / from rust char. This software handles everything as a
70//       Extended Grapheme Cluster.
71//
72
73use crossterm::{
74    QueueableCommand,
75    cursor::{self, position},
76    event::{Event, EventStream, KeyCode, KeyEvent, KeyEventKind, KeyModifiers},
77    style::Print,
78    terminal::{self, disable_raw_mode,},
79};
80use futures_util::{FutureExt, StreamExt, select};
81use grapheme_utils::*;
82use historybuffer::HistoryBuffer;
83use std::{
84    io::{self, Stdout, Write, stdout},
85    ops::DerefMut,
86    rc::Rc,
87    string::String,
88};
89use thingbuf::mpsc::{Receiver, Sender, errors::TrySendError};
90use unicode_segmentation::UnicodeSegmentation;
91
92mod error;
93pub use self::error::{Error, Result};
94
95const HISTORY_BUFFER_SIZE: usize = 300 * 160 * 4;
96
97#[derive(Debug)]
98pub enum EditorEvent {
99    CtrlC,
100    CtrlD,
101    CtrlQ,
102    CtrlN,
103    CtrlS,
104    CtrlX,
105}
106
107pub enum WriteHistoryType {
108    PageUp,
109    PageDown,
110    Quit,
111}
112
113/// AsyncEditor - Multiline Terminal Editor with simultaneous stdout
114///
115/// AsyncEditor is a functional multline editor supporting standard
116/// control keys, while simultaneously supporting stdout printing
117/// on the upper portion of the screen.
118///
119/// Extensible keyboard events are bubbled out for special functions.
120///
121//
122// The main AsyncEditor struct functions as a ReadWriteRouter
123pub struct AsyncEditor {
124    event_stream: EventStream,    // Crossterm Event Stream
125    stdout_rx: Receiver<Vec<u8>>, // Stdout pipe
126    editor: Editor,               // Multiline Editor
127}
128
129impl AsyncEditor {
130    // Create a new `AsyncEditor` instance with an associated
131    // [`SharedStdout`]
132    pub fn new(
133        initial_content: &str,
134        split_prompt: String,
135        print_height: f32,
136        tabstop: u8,
137    ) -> Result<(Self, SharedStdout)> {
138        let (stdout_tx, stdout_rx) = thingbuf::mpsc::channel(500);
139
140        let editor = Editor::new(initial_content, split_prompt, print_height, tabstop)?;
141
142        let mut async_editor = AsyncEditor {
143            event_stream: EventStream::new(),
144            stdout_rx,
145            editor,
146        };
147        async_editor.editor.term.queue(terminal::EnableLineWrap)?;
148        async_editor.editor.term.flush()?;
149        Ok((
150            async_editor,
151            SharedStdout {
152                buf: Vec::new(),
153                stdout_tx: stdout_tx,
154            },
155        ))
156    }
157
158    pub fn flush(&mut self) -> Result<()> {
159        while let Ok(buf) = self.stdout_rx.try_recv_ref() {
160            self.editor.writeout(&buf)?;
161        }
162        self.editor.term.flush()?;
163        Ok(())
164    }
165
166    /// Polling function for async_editor, manages all input and output.
167    /// Returns either an EditorEvent or an Error
168    pub async fn async_editor(&mut self) -> Result<EditorEvent> {
169        loop {
170            select! {
171                event = self.event_stream.next().fuse() => match event {
172                    Some(Ok(event)) => {
173                        match self.editor.handle_event(event) {
174                            Ok(Some(event)) => {
175                                self.editor.term.flush()?;
176                                return Result::<_>::Ok(event) // Try return Ok(event);
177                            },
178                            Err(e) => return Err(e),
179                            Ok(None) => self.editor.term.flush()?,
180                        }
181                    }
182                    Some(Err(e)) => return Err(e.into()),
183                    None => {},
184                },
185                result = self.stdout_rx.recv_ref().fuse() => match result {
186                    Some(buf) => {
187                        self.editor.writeout(&buf)?;
188                        self.editor.term.flush()?;
189                    },
190                    None => return Err(Error::SharedStdoutClosed),
191                },
192            }
193        }
194    }
195
196    pub fn text(&self) -> String {
197        self.editor.text()
198    }
199}
200
201fn string_to_hex(s: &str, maxlen: usize) -> String {
202    let mut new_hex_string = String::with_capacity(s.as_bytes().len() * 2);
203
204    for byte in s.as_bytes().iter() {
205        let s = format!("{:02x} ", byte);
206        new_hex_string.push_str(&s);
207        if new_hex_string.len() >= maxlen {
208            return new_hex_string[..maxlen].to_string();
209        }
210    }
211    new_hex_string
212}
213
214pub struct Editor {
215    curx: u16, // Grapheme Cursor Position
216    cury: u16,
217	hb_active: bool,
218	hb_start_index: usize,
219	hb_end_index: usize,
220    histbuf: HistoryBuffer, // Make the buffer large enough to hold a huge terminal window screen with LOTS of escape characters
221    lidx: usize,
222    lines: Vec<String>, // Editor text without \n
223    lineidx: usize,     // Which line active
224    lofs: usize,
225    loose_cursor: bool, // Detects when we've moved off a long line.
226    printlines: u16,    // Number of Lines used printing
227    printx: u16,        // print cursor pos
228    printy: u16,
229    scrollstart: usize,
230    sizex: u16, // screen size
231    sizey: u16,
232    split_prompt: String,
233    tabstop: u8,
234    term: Stdout,
235    tmpbuf: Rc<String>,
236}
237
238impl Editor {
239    pub fn new(
240        initial_content: &str,
241        split_prompt: String,
242        print_height: f32,
243        tabstop: u8,
244    ) -> Result<Self> {
245        let term = stdout();
246        let (sizex, sizey) = terminal::size()?;
247        let (_curx, cury) = position()?;
248        let newprintlines = (sizey as f32 * print_height.max(0.1).min(0.9)) as u16;
249        terminal::enable_raw_mode()?;
250
251        Ok(Self {
252            curx: 0,
253            cury: newprintlines + 2,
254		hb_active: false,
255		hb_start_index: 0,
256		hb_end_index: 0,
257            histbuf: HistoryBuffer::new(HISTORY_BUFFER_SIZE), // Make the buffer large enough to hold a huge terminal window screen with LOTS of escape characters
258            lidx: 0,				// line index of grapheme at the cursor
259            lines: initial_content.split("\n").map(|s| s.to_string()).collect(), // convert_tabs(s,'→',8).to_string()).collect(),  // Exlusive \n makes a few painful things easier
260            lineidx: 0,
261            lofs: 0,				// line index offset to the start of the displayed text
262            loose_cursor: false,
263            printlines: newprintlines,
264            printx: 0,
265            printy: cury + 1,
266            scrollstart: 0,
267            sizex: sizex,
268            sizey: sizey,
269            split_prompt: split_prompt,
270            tabstop: tabstop,
271            term: term,
272            tmpbuf: Rc::new(String::new()), // with_capacity(BUFFER_SIZE)), not needed...  Grows to largest need, then reused
273        })
274    }
275
276    fn ch(&self, idx: usize) -> char {
277        grapheme_at_idx(&self.lines[self.lineidx], idx)
278            .chars()
279            .next()
280            .unwrap_or('\0')
281    }
282
283    fn grapheme_idx_at_idx(&self, idx: usize) -> usize {
284        grapheme_idx_at_idx(&self.lines[self.lineidx], idx)
285    }
286
287    fn grapheme_width_lofs_to_lidx(&self) -> u16 {
288	let st = &self.lines[self.lineidx][self.lofs..self.lidx];
289        if !st.contains('\t') {
290            return string_width(&st) as u16;
291        }
292        let ofs = string_width(&self.lines[self.lineidx][..self.lofs]) % self.tabstop as usize;
293        let mut char_width;
294        let mut width = 0;
295        for (_, g) in st.grapheme_indices(true) {
296            if g == "\t" {
297                let ts = self.tabstop as usize;
298                char_width = ts - ((width + ofs) % ts);
299            } else {
300                char_width = string_width(g);
301            }
302            width += char_width;
303        }
304        return width as u16;
305    }
306
307    pub fn handle_event(&mut self, event: Event) -> Result<Option<EditorEvent>> {
308        match event {
309            // Doesn't work to detect ctrl-shift  <= a *terminal* thing I thinks
310            // Control Keys
311            Event::Key(KeyEvent {
312                code,
313                modifiers: KeyModifiers::CONTROL,
314                kind: KeyEventKind::Press,
315                ..
316            }) => match code {
317                // Move to beginning
318                KeyCode::Char('a') => {
319                    self.lidx = 0;
320		self.lofs = 0;
321                    self.setpos()?;
322		}
323                // End of text (CTRL-C)
324                KeyCode::Char('c') => {
325                    //if self.should_print_line_on_control_c {
326                    //	self.print(&format!("{}{}", self.prompt, self.editor))?;
327                    //}
328                    return Ok(Some(EditorEvent::CtrlC));
329                }
330                // End of transmission (CTRL-D)
331                KeyCode::Char('d') => {
332                    //writeln!(self.term)?;
333                    return Ok(Some(EditorEvent::CtrlD));
334                }
335                // Move to end
336                KeyCode::Char('e') => {
337                    self.move_end()?;
338                }
339                KeyCode::Char('l') => {
340			self.printx = 0;
341			self.printy = 0;
342			self.redraw()?;
343		}
344                KeyCode::Char('n') => {
345                    return Ok(Some(EditorEvent::CtrlS));
346                }
347                KeyCode::Char('q') => {
348                    return Ok(Some(EditorEvent::CtrlQ));
349                }
350                KeyCode::Char('s') => {
351                    return Ok(Some(EditorEvent::CtrlS));
352                }
353                KeyCode::Char('x') => {
354                    return Ok(Some(EditorEvent::CtrlX));
355                }
356                KeyCode::Char('u') => {
357                    self.lines[self.lineidx].drain(0..self.lidx);
358                    self.redraw()?;
359                }
360                KeyCode::Down => {
361                    self.resize(3)?;
362                }
363                KeyCode::End => {
364			self.lineidx = self.lines.len().saturating_sub(1);
365			self.scrollstart = self.lineidx; //.saturating_sub(1);
366			self.cury = self.printlines + 2; // + (self.lineidx - self.scrollstart) as u16;
367                    self.lidx = self.len();
368                    self.setpos()?;
369			self.redraw()?;
370                }
371                KeyCode::Home => {
372			self.lineidx = 0;
373			self.scrollstart = 0;
374			self.cury = self.printlines + 2;
375                    self.lidx = 0;
376                    self.setpos()?;
377			self.redraw()?;
378                }
379                // Ctrl-Left  Move cursor left to previous word
380                KeyCode::Left => {
381                    if self.lidx == 0 {
382                        self.move_up(1, true)?;
383                        self.lidx = self.len();
384                    }
385                    while self.lidx > 0 && self.prev_char(self.lidx).is_whitespace() {
386                        self.lidx = self.prev_grapheme_idx_from_idx(self.lidx);
387                    }
388                    while self.lidx > 0 && !self.prev_char(self.lidx).is_whitespace() {
389                        self.lidx = self.prev_grapheme_idx_from_idx(self.lidx);
390                    }
391                    self.setpos()?;
392                }
393                KeyCode::PageDown => {	// Go forward in history, Quit if you're caught up
394			// Only pageUp can activate history...  No need to save the index here
395			self.writehistory(WriteHistoryType::PageDown)?;
396                }
397                KeyCode::PageUp => {	// Go back in history. Activate if necessary
398			if !self.hb_active {
399				self.hb_active = true;
400				self.hb_start_index = self.histbuf.get_last_index();
401				self.hb_end_index = self.hb_start_index;
402			} else {
403				if self.hb_start_index == 0 {
404					return Ok(None);
405				}
406			}
407			self.writehistory(WriteHistoryType::PageUp)?;
408                }
409                // Ctrl-Right  Move cursor right to next word
410                KeyCode::Right => {
411                    while self.lidx < self.len() && !self.ch(self.lidx).is_whitespace() {
412                        self.lidx = self.next_grapheme_idx_from_idx(self.lidx);
413                    }
414                    if self.lidx == self.len() {
415                        self.move_down(1, true)?;
416                        self.lidx = 0;
417                    }
418                    while self.lidx < self.len() && self.ch(self.lidx).is_whitespace() {
419                        self.lidx = self.next_grapheme_idx_from_idx(self.lidx);
420                    }
421                    self.setpos()?;
422                }
423                KeyCode::Up => {
424                    self.resize(-3)?;
425                }
426                _ => {}
427            },
428            /////////////////////////////////////////////////////////////////////////////
429            // Everything Else
430            Event::Key(KeyEvent {
431                code,
432                modifiers: _,
433                kind: KeyEventKind::Press,
434                ..
435            }) => match code {
436                KeyCode::Backspace => {
437                    if self.lidx == 0 {
438                        if self.lineidx == 0 {
439                            return Ok(None);
440                        }
441                        self.lidx = self.lines[self.lineidx - 1].len();
442                        //self.loose_cursor = true;
443                        let s = self.lines[self.lineidx].clone();
444                        self.lines[self.lineidx - 1].push_str(&s);
445                        self.lines.remove(self.lineidx);
446                        self.move_up(1, false)?;
447                        self.setpos()?;
448                        self.redraw()?;
449                    } else {
450                        if self.lidx > self.len() {
451                            self.lidx = self.len();
452                            self.lofs = 0;
453                        }
454                        let start = self.prev_grapheme_idx_from_idx(self.lidx);
455			let mut gwid = self.grapheme_width_lofs_to_lidx(); // width with tabs computed the correct width
456                        self.lines[self.lineidx].replace_range(start..self.lidx, "");
457                        self.lidx = start;
458			gwid = gwid.saturating_sub(self.grapheme_width_lofs_to_lidx());
459                        self.curx = self.curx.saturating_sub(gwid);
460                        self.redrawline()?;
461                    }
462                }
463                KeyCode::Char(c) => {
464                    self.insert_charstr(&c.to_string())?;
465                }
466                KeyCode::Delete => {
467                    if self.lidx == self.len() {
468                        if self.lineidx + 1 < self.lines.len() {
469                            let s = self.lines[self.lineidx + 1].clone();
470                            self.lines[self.lineidx].push_str(&s);
471                            self.lines.remove(self.lineidx + 1);
472                            self.redraw()?;
473                        }
474                    } else {
475                        let end = self.next_grapheme_idx_from_idx(self.lidx);
476                        self.lines[self.lineidx].replace_range(self.lidx..end, "");
477                        self.redrawline()?;
478                    }
479                }
480                KeyCode::Down => {
481                    self.move_down(1, false)?;
482                    self.redraw()?;
483                }
484                KeyCode::End => {
485                    self.move_end()?;
486                }
487                KeyCode::Esc => {
488			self.writehistory(WriteHistoryType::Quit)?;
489                    	self.hb_active = false;
490                }
491                KeyCode::Enter => {
492                    if self.lidx > self.len() {
493                        self.lidx = self.len();
494                    }
495                    self.lines.insert(
496                        self.lineidx + 1,
497                        self.lines[self.lineidx][self.lidx..].to_string(),
498                    );
499                    self.lines[self.lineidx].drain(self.lidx..);
500                    self.move_down(1, true)?;
501                    self.redraw()?;
502                }
503                KeyCode::Home => {
504                    self.lidx = 0;
505		self.lofs = 0;
506                    self.setpos()?;
507                }
508                KeyCode::Left => {
509                    if self.lidx == 0 {
510			if self.lineidx == 0 { return Ok(None) }
511                        self.move_up(1, true)?;
512                        self.lidx = self.len();
513                    } else {
514                        self.lidx = self.prev_grapheme_idx_from_idx(self.lidx);
515                    }
516                    self.setpos()?;
517                }
518                KeyCode::PageDown => {
519                    let numlines = self.sizey - self.printlines - 2;
520			self.move_down(numlines, false)?;
521                }
522                KeyCode::PageUp => {
523                    let numlines = self.sizey - self.printlines - 2;
524			self.move_up(numlines, false)?;
525                }
526                KeyCode::Right => {
527                    if self.lidx >= self.len() {
528			if self.lineidx + 1 == self.lines.len() { return Ok(None) }
529                        self.move_down(1, true)?;
530                        self.lidx = 0;
531                    } else {
532                        self.lidx = self.next_grapheme_idx_from_idx(self.lidx);
533                    }
534                    self.setpos()?;
535                }
536                KeyCode::Tab => {
537                    self.insert_charstr("\t")?;
538                }
539                KeyCode::Up => {
540                    self.move_up(1, false)?;
541                }
542                _ => {}
543            },
544            Event::Resize(x, y) => {
545                let curp: f32 = (self.printlines as f32 / self.sizey as f32)
546                    .max(0.9)
547                    .min(0.1);
548                let delta: i16 = (curp * y as f32) as i16 - self.printlines as i16;
549
550                self.sizex = x;
551                self.sizey = y;
552                self.resize(delta)?;
553
554                self.sizey = y;
555            }
556            _ => {}
557        }
558        if false {
559            // Debug code
560            self.split_prompt = string_to_hex(&self.lines[self.lineidx], 40);
561            self.redraw()?;
562        }
563        self.term.queue(cursor::MoveTo(self.curx, self.cury))?;
564        self.term.flush()?;
565        Ok(None)
566    }
567
568    fn insert_charstr(&mut self, ch: &str) -> Result<()> {
569        if self.lidx > self.len() {
570            self.lidx = self.len();
571            self.lofs = 0;
572        }
573        //let pre_cnt = self.num_graphemes();
574        self.lines[self.lineidx].insert_str(self.lidx, ch);
575        //if pre_cnt != self.num_graphemes() {
576        self.lidx = self.next_grapheme_idx_from_idx(self.lidx);
577        self.curx = self.grapheme_width_lofs_to_lidx();
578
579        self.redrawline()?;
580        Ok(())
581    }
582
583    fn len(&mut self) -> usize {
584        self.lines[self.lineidx].len()
585    }
586
587    /// Match the curx position as much as possible moving from line to line
588    fn matchpos(&mut self) -> Result<()> {
589        // Future work -
590        // It's nice for the cursor to stay at nearly the same curx as you move
591        // up and down... but it's a feature that can wait.
592        self.setpos()?;
593        Ok(())
594    }
595
596    fn move_down(&mut self, num: u16, move_to_beginning: bool) -> Result<()> {
597        self.loose_cursor = true;
598	if self.lineidx + 1 == self.lines.len() && self.scrollstart + 1 == self.lines.len() as usize {
599		self.lidx = self.len();
600		self.setpos()?;
601		self.redrawline()?;
602		return Ok(());
603	}
604
605	// To support scrolling beyond the bottom of a full screen, the
606	// scrollstart and cury calculations sometimes use a virtual
607	// lineidx - the line number that would be printed at the bottom of the 
608	// screen.
609	// The switch occurs when the real bottom line rises above the bottom
610	// of the screen, the virtual bottom line numbers.
611	let virtidx;
612	if self.scrollstart <= (self.sizey as usize + 2).saturating_sub((self.cury + self.printlines) as usize)  {
613		virtidx = (self.cury - self.printlines) as usize - 2;
614	} else {
615		virtidx = self.scrollstart + (self.sizey - self.printlines) as usize - 3;
616	}
617
618	self.cury += num;
619	self.lineidx = (self.lineidx + num as usize).min(self.lines.len().saturating_sub(1));
620	if self.cury > self.sizey - 1 || self.lineidx + 1 == self.lines.len() {
621		if num > 10 {
622			// Pagedown - Max scrollstart move
623			self.scrollstart = (self.scrollstart + num as usize).min(self.lineidx);
624		} else {
625			// Linedown - Minimum scrollstart
626			self.scrollstart = (virtidx + num as usize + self.printlines as usize + 3).saturating_sub(self.sizey as usize).min(self.lineidx);
627		}
628        }
629	self.cury = self.cury.min((self.lineidx - self.scrollstart) as u16 + self.printlines + 2).min(self.sizey - 1);
630
631	if move_to_beginning {
632                    self.lidx = 0;
633                    self.lofs = 0;
634                    self.curx = 0;
635	} else {
636	        self.matchpos()?;
637	}
638        self.redraw()?;
639        Ok(())
640    }
641
642    fn move_end(&mut self) -> Result<()> {
643        self.lidx = self.len();
644        self.setpos()?;
645        self.redrawline()?;
646	Ok(())
647    }
648
649    fn move_up(&mut self, num: u16, move_to_end: bool) -> Result<()> {
650        self.loose_cursor = true;
651
652	if self.lineidx == 0 && self.scrollstart == 0 {
653		self.lofs = 0;
654		self.lidx = 0;
655		self.curx = 0;
656		self.redrawline()?;
657		return Ok(())
658	}
659
660        self.lineidx = self.lineidx.saturating_sub(num as usize);
661        if self.cury == self.printlines+2 || self.lineidx < self.scrollstart  {
662	        self.scrollstart = self.scrollstart.saturating_sub(num as usize);
663	}
664        self.cury = (self.lineidx - self.scrollstart) as u16 + self.printlines + 2; // Possible underflow, but...
665	if move_to_end {
666		self.lidx = self.len();
667	}
668        self.matchpos()?;
669        self.redraw()?;
670	Ok(())
671    }
672
673    fn next_grapheme_from_idx(&self, idx: usize) -> &str {
674        next_grapheme_from_idx(&self.lines[self.lineidx], idx)
675    }
676
677    fn next_grapheme_idx_from_idx(&self, idx: usize) -> usize {
678        next_grapheme_idx_from_idx(&self.lines[self.lineidx], idx)
679    }
680
681    fn prev_char(&self, idx: usize) -> char {
682        self.prev_grapheme_from_idx(idx)
683            .chars()
684            .next()
685            .unwrap_or('\0')
686    }
687
688    fn prev_grapheme_from_idx(&self, idx: usize) -> &str {
689        prev_grapheme_from_idx(&self.lines[self.lineidx], idx)
690    }
691
692    fn prev_grapheme_idx_from_idx(&self, idx: usize) -> usize {
693        prev_grapheme_idx_from_idx(&self.lines[self.lineidx], idx)
694    }
695
696    pub fn redraw(&mut self) -> Result<()> {
697        //   Can't run  "let (cx, cy) = position()?;"  for the case when redraw called from writeout:
698
699        let tbuf = Rc::get_mut(&mut self.tmpbuf).ok_or(Error::RedrawRcError)?;
700        //let tbuf = Rc::get_mut(&mut self.tmpbuf).ok_or(Error::Msg("Redraw Rc Error"))?;
701        tbuf.clear();
702        // Just ='s self.buf.extend(std::iter::repeat("=").take(extend).chain(std::iter::once("\n")).collect::<String>().as_bytes());
703        //let s = format!("== {} == {}, c: {} {} cursor: {} {} print: {} {} pline: {} scroll: {}  screen: {} {}  ==", self.split_prompt, s, cx, cy, self.curx, self.cury, self.printx, self.printy, self.printlines, self.scrollstart, self.sizex, self.sizey);
704
705        /* Cursor Debug * /
706	let s = format!(
707            "== {} ==  l: {} o: {} == curx: {} {} print: {} {} pline: {} lineidx: {} sofs: {}  screen: {} {} == Ctrl-D to Quit ",
708            self.split_prompt,
709            self.lidx,
710            self.lofs,
711            self.curx,
712            self.cury,
713            self.printx,
714            self.printy,
715            self.printlines,
716            self.lineidx,
717            self.scrollstart,
718            self.sizex,
719            self.sizey
720        );
721	let s = format!(
722            "=============== {} ==  s: {} e: {} =======  Ctrl-PgUp/Ctrl-PgDn  ===  Ctrl-C/D/Q/X to Quit  ",
723            self.split_prompt,
724            self.hb_start_index,
725            self.hb_end_index);*/
726	let s = format!(
727            "=====  AsyncEditor  ========== {} ==  Ctrl ⬅️  / ⮕ / ⬆️/ / ⬇️  ==  Ctrl-PgUp/Ctrl-PgDn  ",
728            self.split_prompt);
729        let extend_count = (self.sizex as usize).saturating_sub(string_width(&s));
730
731        self.term
732            .queue(cursor::MoveTo(0, self.printlines + 1 as u16))?;
733        self.term
734            .queue(terminal::Clear(terminal::ClearType::FromCursorDown))?;
735        //tbuf.push_str(&format!("{}{}\n", s, std::iter::repeat("=").take(extend_count).collect::<String>()));
736        self.term.queue(Print(&format!(
737            "{}{}\n",
738            s,
739            std::iter::repeat("=")
740                .take(extend_count)
741                .collect::<String>()
742        )))?;
743        self.term.queue(cursor::MoveToColumn(0))?;
744
745        let end_index = (self.scrollstart
746            + (self.sizey.saturating_sub(self.printlines + 2) as usize))
747            .min(self.lines.len());
748        for lidx in self.scrollstart..end_index {
749            // For each line in range
750            if lidx == self.lineidx {
751                self.redrawline()?;
752                if lidx != end_index - 1 {
753                    self.term.queue(cursor::MoveToNextLine(1))?;
754                }
755                continue;
756            }
757
758            let line = &self.lines[lidx];
759            let maxwidth = self.sizex as usize - 1;
760            let stwidth = string_width(line);
761            let mut width = 0usize;
762            let mut char_width;
763            let mut s = String::with_capacity(200);
764            let mut news = String::with_capacity(200);
765
766            match (stwidth > maxwidth, line.contains('\t')) {
767                (false, false) => {
768                    //Printable"
769                    self.term.queue(Print(&line))?;
770                }
771                (_, _) => {
772                    // Too Wide or Tabs or both
773                    s.clear();
774                    for (_, g) in line.grapheme_indices(true) {
775                        news.clear();
776                        if g == "\t" {
777                            let ts = self.tabstop as usize;
778                            char_width = ts - (width % ts);
779                            let tab_arrow_string: String =
780                                std::iter::repeat("→").take(char_width as usize).collect();
781                            news.push_str(&tab_arrow_string);
782                        } else {
783                            char_width = string_width(g);
784                            news.push_str(g);
785                        }
786                        if width + char_width as usize > maxwidth {
787                            break;
788                        }
789                        s.push_str(&news);
790                        width += char_width;
791                    }
792                    self.term.queue(Print(&s))?;
793                    if stwidth > maxwidth {
794                        self.term.queue(cursor::MoveToColumn(self.sizex - 1))?;
795                        self.term.queue(Print(&'>'))?;
796                    }
797                }
798            }
799            if lidx != end_index - 1 {
800                self.term.queue(cursor::MoveToNextLine(1))?;
801            }
802        }
803        self.term.queue(cursor::MoveTo(self.curx, self.cury))?;
804        self.term.flush()?;
805        Ok(())
806    }
807
808    fn redrawline(&mut self) -> Result<()> {
809        self.term
810            .queue(terminal::Clear(terminal::ClearType::CurrentLine))?;
811        self.term.queue(cursor::MoveToColumn(0))?;
812        //if self.lofs > self.len() { self.lofs = 0; }
813        let start = if self.lofs > self.len() {
814            0
815        } else {
816            self.grapheme_idx_at_idx(self.lofs)
817        };
818
819        // Current line may start at lofs
820        let line = &self.lines[self.lineidx][start..];
821        let maxwidth = self.sizex as usize - 2;
822        let stwidth = string_width(line);
823        let mut width = 0usize;
824        let mut char_width;
825        let mut s = String::with_capacity(200);
826        let mut news = String::with_capacity(200);
827
828        match (stwidth > maxwidth, line.contains('\t')) {
829            (false, false) => {
830                //Printable"
831                //tbuf.push_str(line);
832                self.term.queue(Print(&line))?;
833            }
834            (_, _) => {
835                // Too Wide or Tabs or both
836                s.clear();
837                for (_, g) in line.grapheme_indices(true) {
838                    news.clear();
839                    if g == "\t" {
840                        let ts = self.tabstop as usize;
841                        char_width = ts - (width % ts);
842                        let tab_arrow_string: String =
843                            std::iter::repeat("→").take(char_width as usize).collect();
844                        news.push_str(&tab_arrow_string);
845                    } else {
846                        char_width = string_width(g);
847                        news.push_str(g);
848                    }
849                    if width + char_width as usize > maxwidth + 1 {
850                        break;
851                    }
852                    s.push_str(&news);
853                    width += char_width;
854                }
855                //tbuf.push_str(&line[0..end]);
856                self.term.queue(Print(&s))?;
857            }
858        }
859        self.term.queue(cursor::MoveTo(self.curx, self.cury))?;
860        Ok(())
861    }
862
863    fn resize(&mut self, delta: i16) -> Result<()> {
864        self.term
865            .queue(cursor::MoveTo(self.printx, self.printy as u16))?;
866        self.term
867            .queue(terminal::Clear(terminal::ClearType::FromCursorDown))?;
868	let pre = self.printlines as i16;
869        self.printlines = (self.printlines as i16 + delta)
870            .max((self.sizey >> 3) as i16)
871            .min(self.sizey as i16 - 8) as u16;
872	let new_delta = self.printlines as i16 - pre;
873        self.cury = (self.cury as i16 + new_delta)
874            .max(self.printlines as i16 + 2) as u16;
875	while self.cury >= self.sizey {
876		self.cury -= 1;
877		self.lineidx -= 1;
878	}
879	self.setpos()?;
880        self.writebuf(&[])?;
881        Ok(())
882    }
883
884    // Set self.curx / self.lofs so self.lidx is visable (string_width[lofs to idx] is < maxsize
885    fn setpos(&mut self) -> Result<()> {
886        let maxwidth = self.sizex - 1;
887        self.lidx = self.grapheme_idx_at_idx(self.lidx);
888
889        // loose_cursor - Signals that the cursor just moved off a possibly long line
890        // We want to stay loose until the user starts a changing action, AND
891        // We want to keep the current alignment if we're on another long line.
892        if self.loose_cursor {
893            // Lean toward resetting lofs, but don't for other long lines
894            if self.lidx < (self.lofs + (self.sizey << 2) as usize + 15) {
895                // really short line detection  - Know changed mode.
896                self.lofs = 0;
897            } else {
898                //self.lofs = grapheme_idx_at_idx(&self.lines[self.lineidx], self.lofs); // Fix when lofs hits in the middle of a char
899                self.lofs = self.grapheme_idx_at_idx(self.lofs); // Fix when lofs hits in the middle of a char
900            }
901        }
902        if self.lidx < self.lofs {
903            // really short line detection  - Know changed mode.
904            self.lofs = 0;
905        }
906        self.loose_cursor = false;
907
908        let mut stwidth = self.grapheme_width_lofs_to_lidx();
909
910        loop {
911            if stwidth <= maxwidth {
912                self.curx = stwidth;
913                return Ok(());
914            }
915            // It would be nice to just subtract the first char width, but for tabs
916            //stwidth -= self.string_width_at_idx(self.lofs);
917            self.lofs = self.next_grapheme_idx_from_idx(self.lofs);
918            stwidth = self.grapheme_width_lofs_to_lidx();
919        }
920    }
921
922    pub fn text(&self) -> String {
923        self.lines.join("\n")
924    }
925
926    fn writebuf(&mut self, buf: &[u8]) -> Result<()> {
927	// The common writing worker
928
929	// The caller is responsible for restoring the cursor to self.printx/y
930	// and clearing the screen below
931
932        for line in buf.split_inclusive(|b| *b == b'\n') {
933            self.term.write_all(line)?;
934            self.term.flush()?;
935            if line.ends_with(b"\n") {
936                self.term.queue(cursor::MoveToColumn(0))?;
937                self.printx = 0;
938                self.printy += 1;
939            } else {
940                let mut x = self.printx as usize + line.len(); // Todo - count graphemes!
941                while x > self.sizex as usize {
942                    x -= self.sizex as usize;
943                    self.printy += 1;
944                }
945                self.printx = x as u16;
946            }
947        }
948
949        self.printy = self.printy.min(self.sizey);
950        if self.printy > self.printlines {
951            self.term
952                .queue(terminal::ScrollUp(self.printy - self.printlines))?;
953            self.printy = self.printlines;
954        }
955
956        self.redraw()?;
957        Ok(())
958    }
959
960    fn writehistory(&mut self, write_history_type: WriteHistoryType) -> Result<()> {
961        self.term.queue(cursor::MoveTo(0, 0))?;
962	self.term.queue(terminal::Clear(terminal::ClearType::All))?;
963
964	self.printx = 0;
965	self.printy = 0;
966
967	//let mut limit = self.printlines * self.sizey - 1;
968	let mut linecnt = self.sizex;
969	let mut num_lines = 0;
970	let mut buf = Vec::<u8>::with_capacity(self.printlines as usize * self.sizex as usize);
971	let mut revbuf: Vec::<u8> = vec![];
972
973	match write_history_type {
974		WriteHistoryType::PageDown => {
975			self.hb_start_index = self.hb_end_index;
976			while let Some(ch) = self.histbuf.get(self.hb_start_index + buf.len()) {
977				buf.push(ch);
978				linecnt -= 1;
979				if ch == b'\n' || linecnt == 0 {
980					linecnt = self.sizex - 1;
981					num_lines += 1;
982					if num_lines >= self.printlines {
983						break;
984					}
985				}
986			}
987			self.hb_end_index = self.hb_start_index + buf.len();
988			if num_lines < self.printlines { // Back at the bottom
989				buf = self.histbuf.get_recent(self.printlines as usize * self.sizex as usize);
990				self.writebuf(&buf)?;
991				self.hb_active = false;
992			} else {
993				self.writebuf(&buf)?;
994			}
995		},
996		WriteHistoryType::PageUp => {
997			self.hb_end_index = self.hb_start_index;
998			while let Some(ch) = self.histbuf.get(self.hb_start_index) {
999				buf.push(ch);
1000				linecnt -= 1;
1001				if ch == b'\n' || linecnt == 0 {
1002					linecnt = self.sizex - 1;
1003					num_lines += 1;
1004					if num_lines >= self.printlines {
1005						break;
1006					}
1007				}
1008				if self.hb_start_index == 0 {
1009					break;
1010				}
1011				self.hb_start_index = self.hb_start_index.saturating_sub(1);
1012			}
1013			if self.hb_start_index == 0 { // Load a full page
1014				let mut linecnt = self.sizex;
1015				let mut num_lines = 0;
1016				revbuf = Vec::<u8>::with_capacity(self.printlines as usize * self.sizex as usize);
1017				while let Some(ch) = self.histbuf.get(self.hb_start_index + revbuf.len()) {
1018					revbuf.push(ch);
1019					linecnt -= 1;
1020					if ch == b'\n' || linecnt == 0 {
1021						linecnt = self.sizex - 1;
1022						num_lines += 1;
1023						if num_lines >= self.printlines {
1024							break;
1025						}
1026					}
1027				}
1028				self.hb_end_index = revbuf.len();
1029			} else {
1030				revbuf = buf.into_iter().rev().collect();
1031			}
1032			self.writebuf(&revbuf)?;
1033		},
1034		WriteHistoryType::Quit => {
1035			buf = self.histbuf.get_recent(self.printlines as usize * self.sizex as usize);
1036			self.writebuf(&buf)?;
1037			self.hb_active = false;
1038		}
1039	}
1040	Ok(())
1041    }
1042
1043    fn writeout(&mut self, buf: &[u8]) -> Result<()> {
1044        // Can't request position: let (cx, cy) = position()?; // Causes Timeout:  
1045	self.histbuf.add(buf);
1046	if !self.hb_active {
1047	        self.term
1048        	    .queue(cursor::MoveTo(self.printx, self.printy as u16))?;
1049	        self.term
1050        	    .queue(terminal::Clear(terminal::ClearType::FromCursorDown))?;
1051		self.writebuf(buf)?;
1052	}
1053	Ok(())
1054    }
1055}
1056
1057impl Drop for Editor {
1058    fn drop(&mut self) {
1059        let _ = disable_raw_mode();
1060        self.term
1061            .queue(cursor::MoveTo(0, self.sizey - 1))
1062            .unwrap();
1063        self.term.queue(cursor::MoveToNextLine(1)).unwrap();
1064        self.term.flush().unwrap();
1065    }
1066}
1067
1068#[pin_project::pin_project]
1069pub struct SharedStdout {
1070    #[pin]
1071    buf: Vec<u8>,
1072    stdout_tx: Sender<Vec<u8>>,
1073}
1074
1075impl io::Write for SharedStdout {
1076    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1077        self.buf.extend_from_slice(buf);
1078        if true {
1079            match self.stdout_tx.try_send_ref() {
1080                Ok(mut send_buf) => {
1081                    std::mem::swap(send_buf.deref_mut(), &mut self.buf);
1082                    self.buf.clear();
1083                }
1084                Err(TrySendError::Full(_)) => return Err(io::ErrorKind::WouldBlock.into()),
1085                _ => {
1086                    return Err(io::Error::new(
1087                        io::ErrorKind::Other,
1088                        "io Error: ThingBuf Receiver Closed",
1089                    ));
1090                }
1091            }
1092        }
1093        Ok(buf.len())
1094    }
1095    fn flush(&mut self) -> io::Result<()> {
1096        Ok(())
1097    }
1098}