1use unicode_width::UnicodeWidthStr;
4
5use crate::config::{Behavior, BellStyle, ColorMode, Config};
6use crate::highlight::Highlighter;
7use crate::keys::KeyEvent;
8use crate::layout::{Layout, Position};
9use crate::line_buffer::LineBuffer;
10use crate::{Cmd, Result};
11
12pub trait RawMode: Sized {
14 fn disable_raw_mode(&self) -> Result<()>;
16}
17
18pub enum Event {
20 KeyPress(KeyEvent),
21 ExternalPrint(String),
22}
23
24pub trait RawReader {
26 type Buffer;
27 fn wait_for_input(&mut self, single_esc_abort: bool) -> Result<Event>; fn next_key(&mut self, single_esc_abort: bool) -> Result<KeyEvent>;
31 #[cfg(unix)]
33 fn next_char(&mut self) -> Result<char>;
34 fn read_pasted_text(&mut self) -> Result<String>;
36 fn find_binding(&self, key: &KeyEvent) -> Option<Cmd>;
38 fn unbuffer(self) -> Option<Buffer>;
40}
41
42pub trait Renderer {
44 type Reader: RawReader;
45
46 fn move_cursor(&mut self, old: Position, new: Position) -> Result<()>;
47
48 #[allow(clippy::too_many_arguments)]
50 fn refresh_line(
51 &mut self,
52 prompt: &str,
53 line: &LineBuffer,
54 hint: Option<&str>,
55 old_layout: &Layout,
56 new_layout: &Layout,
57 highlighter: Option<&dyn Highlighter>,
58 ) -> Result<()>;
59
60 fn compute_layout(
64 &self,
65 prompt_size: Position,
66 default_prompt: bool,
67 line: &LineBuffer,
68 info: Option<&str>,
69 ) -> Layout {
70 let pos = line.pos();
72 let cursor = self.calculate_position(&line[..pos], prompt_size);
73 let mut end = if pos == line.len() {
75 cursor
76 } else {
77 self.calculate_position(&line[pos..], cursor)
78 };
79 if let Some(info) = info {
80 end = self.calculate_position(info, end);
81 }
82
83 let new_layout = Layout {
84 prompt_size,
85 default_prompt,
86 cursor,
87 end,
88 };
89 debug_assert!(new_layout.prompt_size <= new_layout.cursor);
90 debug_assert!(new_layout.cursor <= new_layout.end);
91 new_layout
92 }
93
94 fn calculate_position(&self, s: &str, orig: Position) -> Position;
97
98 fn write_and_flush(&mut self, buf: &str) -> Result<()>;
99
100 fn beep(&mut self) -> Result<()>;
103
104 fn clear_screen(&mut self) -> Result<()>;
106 fn clear_rows(&mut self, layout: &Layout) -> Result<()>;
108
109 fn update_size(&mut self);
111 fn get_columns(&self) -> usize;
113 fn get_rows(&self) -> usize;
115 fn colors_enabled(&self) -> bool;
117
118 fn move_cursor_at_leftmost(&mut self, rdr: &mut Self::Reader) -> Result<()>;
120}
121
122impl<'a, R: Renderer + ?Sized> Renderer for &'a mut R {
123 type Reader = R::Reader;
124
125 fn move_cursor(&mut self, old: Position, new: Position) -> Result<()> {
126 (**self).move_cursor(old, new)
127 }
128
129 fn refresh_line(
130 &mut self,
131 prompt: &str,
132 line: &LineBuffer,
133 hint: Option<&str>,
134 old_layout: &Layout,
135 new_layout: &Layout,
136 highlighter: Option<&dyn Highlighter>,
137 ) -> Result<()> {
138 (**self).refresh_line(prompt, line, hint, old_layout, new_layout, highlighter)
139 }
140
141 fn calculate_position(&self, s: &str, orig: Position) -> Position {
142 (**self).calculate_position(s, orig)
143 }
144
145 fn write_and_flush(&mut self, buf: &str) -> Result<()> {
146 (**self).write_and_flush(buf)
147 }
148
149 fn beep(&mut self) -> Result<()> {
150 (**self).beep()
151 }
152
153 fn clear_screen(&mut self) -> Result<()> {
154 (**self).clear_screen()
155 }
156
157 fn clear_rows(&mut self, layout: &Layout) -> Result<()> {
158 (**self).clear_rows(layout)
159 }
160
161 fn update_size(&mut self) {
162 (**self).update_size();
163 }
164
165 fn get_columns(&self) -> usize {
166 (**self).get_columns()
167 }
168
169 fn get_rows(&self) -> usize {
170 (**self).get_rows()
171 }
172
173 fn colors_enabled(&self) -> bool {
174 (**self).colors_enabled()
175 }
176
177 fn move_cursor_at_leftmost(&mut self, rdr: &mut R::Reader) -> Result<()> {
178 (**self).move_cursor_at_leftmost(rdr)
179 }
180}
181
182fn width(s: &str, esc_seq: &mut u8) -> usize {
184 if *esc_seq == 1 {
185 if s == "[" {
186 *esc_seq = 2;
188 } else {
189 *esc_seq = 0;
191 }
192 0
193 } else if *esc_seq == 2 {
194 if s == ";" || (s.as_bytes()[0] >= b'0' && s.as_bytes()[0] <= b'9') {
195 } else {
199 *esc_seq = 0;
201 }
202 0
203 } else if s == "\x1b" {
204 *esc_seq = 1;
205 0
206 } else if s == "\n" {
207 0
208 } else {
209 s.width()
210 }
211}
212
213pub trait ExternalPrinter {
215 fn print(&mut self, msg: String) -> Result<()>;
217}
218
219pub trait Term {
221 type Buffer;
222 type KeyMap;
223 type Reader: RawReader<Buffer = Self::Buffer>; type Writer: Renderer<Reader = Self::Reader>; type Mode: RawMode;
226 type ExternalPrinter: ExternalPrinter;
227 type CursorGuard;
228
229 fn new(
230 color_mode: ColorMode,
231 behavior: Behavior,
232 tab_stop: usize,
233 bell_style: BellStyle,
234 enable_bracketed_paste: bool,
235 enable_signals: bool,
236 ) -> Result<Self>
237 where
238 Self: Sized;
239 fn is_unsupported(&self) -> bool;
242 fn is_input_tty(&self) -> bool;
244 fn is_output_tty(&self) -> bool;
246 fn enable_raw_mode(&mut self) -> Result<(Self::Mode, Self::KeyMap)>;
248 fn create_reader(
250 &self,
251 buffer: Option<Self::Buffer>,
252 config: &Config,
253 key_map: Self::KeyMap,
254 ) -> Self::Reader;
255 fn create_writer(&self) -> Self::Writer;
257 fn writeln(&self) -> Result<()>;
258 fn create_external_printer(&mut self) -> Result<Self::ExternalPrinter>;
260 fn set_cursor_visibility(&mut self, visible: bool) -> Result<Option<Self::CursorGuard>>;
262}
263
264#[cfg(all(windows, not(target_arch = "wasm32")))]
267mod windows;
268#[cfg(all(windows, not(target_arch = "wasm32"), not(test)))]
269pub use self::windows::*;
270
271#[cfg(all(unix, not(target_arch = "wasm32")))]
274mod unix;
275#[cfg(all(unix, not(target_arch = "wasm32"), not(test)))]
276pub use self::unix::*;
277
278#[cfg(any(test, target_arch = "wasm32"))]
279mod test;
280#[cfg(any(test, target_arch = "wasm32"))]
281pub use self::test::*;