1use crate::{editor::Editor, error::*, iter::*, raw::raw};
6use std::{
7 collections::VecDeque,
8 io::{Bytes, Read, Result, Write, stdout},
9};
10
11#[derive(Debug)]
13pub struct Repline<'a, R: Read> {
14 input: Chars<Flatten<Result<u8>, Bytes<R>>>,
15
16 history: VecDeque<String>, hindex: usize, ed: Editor<'a>, }
21
22impl<'a> Repline<'a, std::io::Stdin> {
23 pub fn new(color: &'a str, begin: &'a str, again: &'a str) -> Self {
24 Self::with_input(std::io::stdin(), color, begin, again)
25 }
26}
27
28impl<'a, R: Read> Repline<'a, R> {
29 pub fn with_input(input: R, color: &'a str, begin: &'a str, again: &'a str) -> Self {
31 #[allow(clippy::unbuffered_bytes)]
32 Self {
33 input: Chars(Flatten(input.bytes())),
34 history: Default::default(),
35 hindex: 0,
36 ed: Editor::new(color, begin, again),
37 }
38 }
39 pub fn set_color(&mut self, color: &'a str) {
41 self.ed.color = color
42 }
43 pub fn accept(&mut self) {
45 self.history_append(self.ed.to_string());
46 self.ed.clear();
47 self.hindex = self.history.len();
48 }
49 pub fn deny(&mut self) {
51 self.ed.clear()
52 }
53 pub fn read(&mut self) -> ReplResult<String> {
55 const INDENT: &str = " ";
56 let mut stdout = stdout().lock();
57 let stdout = &mut stdout;
58 let _make_raw = raw();
59 self.ed.print_head(stdout)?;
62 loop {
63 stdout.flush()?;
64 match self.input.next().ok_or(Error::EndOfInput)?? {
65 '\x03' => {
67 drop(_make_raw);
68 writeln!(stdout)?;
69 return Err(Error::CtrlC(self.ed.to_string()));
70 }
71 '\x04' => {
73 drop(_make_raw);
74 writeln!(stdout)?;
75 return Err(Error::CtrlD(self.ed.to_string()));
76 }
77 '\t' => {
79 self.ed.extend(INDENT.chars(), stdout)?;
80 }
81 '\n' => {}
83 '\r' => {
84 if self.ed.at_end() {
85 self.ed.push('\n', stdout)?;
86 } else {
87 self.ed.end(stdout)?;
88 writeln!(stdout)?;
89 }
90 return Ok(self.ed.to_string());
91 }
92 '\x17' => {
94 self.ed.erase_word(stdout)?;
95 }
96 '\x1b' => self.escape(stdout)?,
98 '\x08' | '\x7f' => {
100 let ed = &mut self.ed;
101 if ed.ends_with(INDENT.chars()) {
102 for _ in 0..INDENT.len() {
103 ed.pop(stdout)?;
104 }
105 } else {
106 ed.pop(stdout)?;
107 }
108 }
109 c if c.is_ascii_control() => {
110 if cfg!(debug_assertions) {
111 self.ed.extend(c.escape_debug(), stdout)?;
112 }
113 }
114 c => {
115 self.ed.push(c, stdout)?;
116 }
117 }
118 }
119 }
120 pub fn print_inline(&mut self, value: impl std::fmt::Display) -> ReplResult<()> {
122 let mut stdout = stdout().lock();
123 self.print_err(&mut stdout, value)
124 }
125 fn print_err<W: Write>(&mut self, w: &mut W, value: impl std::fmt::Display) -> ReplResult<()> {
127 self.ed.print_err(w, value)
128 }
129 fn escape<W: Write>(&mut self, w: &mut W) -> ReplResult<()> {
131 match self.input.next().ok_or(Error::EndOfInput)?? {
132 '[' => self.csi(w)?,
133 'O' => todo!("Process alternate character mode"),
134 other => self.ed.extend(other.escape_debug(), w)?,
135 }
136 Ok(())
137 }
138 fn csi<W: Write>(&mut self, w: &mut W) -> ReplResult<()> {
140 match self.input.next().ok_or(Error::EndOfInput)?? {
141 'A' => {
142 self.hindex = self.hindex.saturating_sub(1);
143 self.restore_history(w)?
144 }
145 'B' => {
146 self.hindex = self.hindex.saturating_add(1).min(self.history.len());
147 self.restore_history(w)?
148 }
149 'C' => self.ed.cursor_forward(1, w)?,
150 'D' => self.ed.cursor_back(1, w)?,
151 'H' => self.ed.home(w)?,
152 'F' => self.ed.end(w)?,
153 '3' => {
154 if let '~' = self.input.next().ok_or(Error::EndOfInput)?? {
155 let _ = self.ed.delete(w);
156 }
157 }
158 other => {
159 if cfg!(debug_assertions) {
160 self.ed.extend(other.escape_debug(), w)?;
161 }
162 }
163 }
164 Ok(())
165 }
166 fn restore_history<W: Write>(&mut self, w: &mut W) -> ReplResult<()> {
168 let Self { history, hindex, ed, .. } = self;
169 ed.undraw(w)?;
170 ed.clear();
171 ed.print_head(w)?;
172 if let Some(history) = history.get(*hindex) {
173 ed.extend(history.chars(), w)?
174 }
175 Ok(())
176 }
177
178 fn history_append(&mut self, mut buf: String) {
180 while buf.ends_with(char::is_whitespace) {
181 buf.pop();
182 }
183 if !self.history.contains(&buf) {
184 self.history.push_back(buf)
185 }
186 while self.history.len() > 20 {
187 self.history.pop_front();
188 }
189 }
190}