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 fn refresh_line(
50 &mut self,
51 prompt: &str,
52 line: &LineBuffer,
53 hint: Option<&str>,
54 old_layout: &Layout,
55 new_layout: &Layout,
56 highlighter: Option<&dyn Highlighter>,
57 ) -> Result<()>;
58
59 fn compute_layout(
63 &self,
64 prompt_size: Position,
65 default_prompt: bool,
66 line: &LineBuffer,
67 info: Option<&str>,
68 ) -> Layout {
69 let pos = line.pos();
71 let cursor = self.calculate_position(&line[..pos], prompt_size);
72 let mut end = if pos == line.len() {
74 cursor
75 } else {
76 self.calculate_position(&line[pos..], cursor)
77 };
78 if let Some(info) = info {
79 end = self.calculate_position(info, end);
80 }
81
82 let new_layout = Layout {
83 prompt_size,
84 default_prompt,
85 cursor,
86 end,
87 };
88 debug_assert!(new_layout.prompt_size <= new_layout.cursor);
89 debug_assert!(new_layout.cursor <= new_layout.end);
90 new_layout
91 }
92
93 fn calculate_position(&self, s: &str, orig: Position) -> Position;
96
97 fn write_and_flush(&mut self, buf: &str) -> Result<()>;
98
99 fn beep(&mut self) -> Result<()>;
102
103 fn clear_screen(&mut self) -> Result<()>;
105 fn clear_rows(&mut self, layout: &Layout) -> Result<()>;
107
108 fn update_size(&mut self);
110 fn get_columns(&self) -> usize;
112 fn get_rows(&self) -> usize;
114 fn colors_enabled(&self) -> bool;
116
117 fn move_cursor_at_leftmost(&mut self, rdr: &mut Self::Reader) -> Result<()>;
119}
120
121fn width(s: &str, esc_seq: &mut u8) -> usize {
123 if *esc_seq == 1 {
124 if s == "[" {
125 *esc_seq = 2;
127 } else {
128 *esc_seq = 0;
130 }
131 0
132 } else if *esc_seq == 2 {
133 if s == ";" || (s.as_bytes()[0] >= b'0' && s.as_bytes()[0] <= b'9') {
134 } else {
138 *esc_seq = 0;
140 }
141 0
142 } else if s == "\x1b" {
143 *esc_seq = 1;
144 0
145 } else if s == "\n" {
146 0
147 } else {
148 s.width()
149 }
150}
151
152pub trait ExternalPrinter {
154 fn print(&mut self, msg: String) -> Result<()>;
156}
157
158pub trait Term {
160 type Buffer;
161 type KeyMap;
162 type Reader: RawReader<Buffer = Self::Buffer>; type Writer: Renderer<Reader = Self::Reader>; type Mode: RawMode;
165 type ExternalPrinter: ExternalPrinter;
166 type CursorGuard;
167
168 fn new(
169 color_mode: ColorMode,
170 behavior: Behavior,
171 tab_stop: usize,
172 bell_style: BellStyle,
173 enable_bracketed_paste: bool,
174 enable_signals: bool,
175 ) -> Result<Self>
176 where
177 Self: Sized;
178 fn is_unsupported(&self) -> bool;
181 fn is_input_tty(&self) -> bool;
183 fn is_output_tty(&self) -> bool;
185 fn enable_raw_mode(&mut self) -> Result<(Self::Mode, Self::KeyMap)>;
187 fn create_reader(
189 &self,
190 buffer: Option<Self::Buffer>,
191 config: &Config,
192 key_map: Self::KeyMap,
193 ) -> Self::Reader;
194 fn create_writer(&self) -> Self::Writer;
196 fn writeln(&self) -> Result<()>;
197 fn create_external_printer(&mut self) -> Result<Self::ExternalPrinter>;
199 fn set_cursor_visibility(&mut self, visible: bool) -> Result<Option<Self::CursorGuard>>;
201}
202
203#[cfg(all(windows, not(target_arch = "wasm32")))]
206mod windows;
207#[cfg(all(windows, not(target_arch = "wasm32"), not(test)))]
208pub use self::windows::*;
209
210#[cfg(all(unix, not(target_arch = "wasm32")))]
213mod unix;
214#[cfg(all(unix, not(target_arch = "wasm32"), not(test)))]
215pub use self::unix::*;
216
217#[cfg(any(test, target_arch = "wasm32"))]
218mod test;
219#[cfg(any(test, target_arch = "wasm32"))]
220pub use self::test::*;