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 fn wait_for_input(&mut self, single_esc_abort: bool) -> Result<Event>; fn next_key(&mut self, single_esc_abort: bool) -> Result<KeyEvent>;
30 #[cfg(unix)]
32 fn next_char(&mut self) -> Result<char>;
33 fn read_pasted_text(&mut self) -> Result<String>;
35 fn find_binding(&self, key: &KeyEvent) -> Option<Cmd>;
37}
38
39pub trait Renderer {
41 type Reader: RawReader;
42
43 fn move_cursor(&mut self, old: Position, new: Position) -> Result<()>;
44
45 #[allow(clippy::too_many_arguments)]
47 fn refresh_line(
48 &mut self,
49 prompt: &str,
50 line: &LineBuffer,
51 hint: Option<&str>,
52 old_layout: &Layout,
53 new_layout: &Layout,
54 highlighter: Option<&dyn Highlighter>,
55 ) -> Result<()>;
56
57 fn compute_layout(
61 &self,
62 prompt_size: Position,
63 default_prompt: bool,
64 line: &LineBuffer,
65 info: Option<&str>,
66 ) -> Layout {
67 let pos = line.pos();
69 let cursor = self.calculate_position(&line[..pos], prompt_size);
70 let mut end = if pos == line.len() {
72 cursor
73 } else {
74 self.calculate_position(&line[pos..], cursor)
75 };
76 if let Some(info) = info {
77 end = self.calculate_position(info, end);
78 }
79
80 let new_layout = Layout {
81 prompt_size,
82 default_prompt,
83 cursor,
84 end,
85 };
86 debug_assert!(new_layout.prompt_size <= new_layout.cursor);
87 debug_assert!(new_layout.cursor <= new_layout.end);
88 new_layout
89 }
90
91 fn calculate_position(&self, s: &str, orig: Position) -> Position;
94
95 fn write_and_flush(&mut self, buf: &str) -> Result<()>;
96
97 fn beep(&mut self) -> Result<()>;
100
101 fn clear_screen(&mut self) -> Result<()>;
103 fn clear_rows(&mut self, layout: &Layout) -> Result<()>;
105
106 fn update_size(&mut self);
108 fn get_columns(&self) -> usize;
110 fn get_rows(&self) -> usize;
112 fn colors_enabled(&self) -> bool;
114
115 fn move_cursor_at_leftmost(&mut self, rdr: &mut Self::Reader) -> Result<()>;
117}
118
119impl<'a, R: Renderer + ?Sized> Renderer for &'a mut R {
120 type Reader = R::Reader;
121
122 fn move_cursor(&mut self, old: Position, new: Position) -> Result<()> {
123 (**self).move_cursor(old, new)
124 }
125
126 fn refresh_line(
127 &mut self,
128 prompt: &str,
129 line: &LineBuffer,
130 hint: Option<&str>,
131 old_layout: &Layout,
132 new_layout: &Layout,
133 highlighter: Option<&dyn Highlighter>,
134 ) -> Result<()> {
135 (**self).refresh_line(prompt, line, hint, old_layout, new_layout, highlighter)
136 }
137
138 fn calculate_position(&self, s: &str, orig: Position) -> Position {
139 (**self).calculate_position(s, orig)
140 }
141
142 fn write_and_flush(&mut self, buf: &str) -> Result<()> {
143 (**self).write_and_flush(buf)
144 }
145
146 fn beep(&mut self) -> Result<()> {
147 (**self).beep()
148 }
149
150 fn clear_screen(&mut self) -> Result<()> {
151 (**self).clear_screen()
152 }
153
154 fn clear_rows(&mut self, layout: &Layout) -> Result<()> {
155 (**self).clear_rows(layout)
156 }
157
158 fn update_size(&mut self) {
159 (**self).update_size();
160 }
161
162 fn get_columns(&self) -> usize {
163 (**self).get_columns()
164 }
165
166 fn get_rows(&self) -> usize {
167 (**self).get_rows()
168 }
169
170 fn colors_enabled(&self) -> bool {
171 (**self).colors_enabled()
172 }
173
174 fn move_cursor_at_leftmost(&mut self, rdr: &mut R::Reader) -> Result<()> {
175 (**self).move_cursor_at_leftmost(rdr)
176 }
177}
178
179fn width(s: &str, esc_seq: &mut u8) -> usize {
181 if *esc_seq == 1 {
182 if s == "[" {
183 *esc_seq = 2;
185 } else {
186 *esc_seq = 0;
188 }
189 0
190 } else if *esc_seq == 2 {
191 if s == ";" || (s.as_bytes()[0] >= b'0' && s.as_bytes()[0] <= b'9') {
192 } else {
196 *esc_seq = 0;
198 }
199 0
200 } else if s == "\x1b" {
201 *esc_seq = 1;
202 0
203 } else if s == "\n" {
204 0
205 } else {
206 s.width()
207 }
208}
209
210pub trait ExternalPrinter {
212 fn print(&mut self, msg: String) -> Result<()>;
214}
215
216pub trait Term {
218 type KeyMap;
219 type Reader: RawReader; type Writer: Renderer<Reader = Self::Reader>; type Mode: RawMode;
222 type ExternalPrinter: ExternalPrinter;
223
224 fn new(
225 color_mode: ColorMode,
226 behavior: Behavior,
227 tab_stop: usize,
228 bell_style: BellStyle,
229 enable_bracketed_paste: bool,
230 ) -> Result<Self>
231 where
232 Self: Sized;
233 fn is_unsupported(&self) -> bool;
236 fn is_input_tty(&self) -> bool;
238 fn is_output_tty(&self) -> bool;
240 fn enable_raw_mode(&mut self) -> Result<(Self::Mode, Self::KeyMap)>;
242 fn create_reader(&self, config: &Config, key_map: Self::KeyMap) -> Self::Reader;
244 fn create_writer(&self) -> Self::Writer;
246 fn writeln(&self) -> Result<()>;
247 fn create_external_printer(&mut self) -> Result<Self::ExternalPrinter>;
249}
250
251#[cfg(all(windows, not(target_arch = "wasm32")))]
254mod windows;
255#[cfg(all(windows, not(target_arch = "wasm32")))]
256pub use self::windows::*;
257
258#[cfg(all(unix, not(target_arch = "wasm32")))]
261mod unix;
262#[cfg(all(unix, not(target_arch = "wasm32")))]
263pub use self::unix::*;
264
265#[cfg(any(test, target_arch = "wasm32"))]
266mod test;
267#[cfg(any(test, target_arch = "wasm32"))]
268pub use self::test::*;