input.rs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. use std::io::{self, IsTerminal, Write};
  2. use crossterm::cursor::MoveToColumn;
  3. use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyModifiers};
  4. use crossterm::queue;
  5. use crossterm::style::Print;
  6. use crossterm::terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType};
  7. #[derive(Debug, Clone, PartialEq, Eq)]
  8. pub struct InputBuffer {
  9. buffer: String,
  10. cursor: usize,
  11. }
  12. impl InputBuffer {
  13. #[must_use]
  14. pub fn new() -> Self {
  15. Self {
  16. buffer: String::new(),
  17. cursor: 0,
  18. }
  19. }
  20. pub fn insert(&mut self, ch: char) {
  21. self.buffer.insert(self.cursor, ch);
  22. self.cursor += ch.len_utf8();
  23. }
  24. pub fn insert_newline(&mut self) {
  25. self.insert('\n');
  26. }
  27. pub fn backspace(&mut self) {
  28. if self.cursor == 0 {
  29. return;
  30. }
  31. let previous = self.buffer[..self.cursor]
  32. .char_indices()
  33. .last()
  34. .map_or(0, |(idx, _)| idx);
  35. self.buffer.drain(previous..self.cursor);
  36. self.cursor = previous;
  37. }
  38. pub fn move_left(&mut self) {
  39. if self.cursor == 0 {
  40. return;
  41. }
  42. self.cursor = self.buffer[..self.cursor]
  43. .char_indices()
  44. .last()
  45. .map_or(0, |(idx, _)| idx);
  46. }
  47. pub fn move_right(&mut self) {
  48. if self.cursor >= self.buffer.len() {
  49. return;
  50. }
  51. if let Some(next) = self.buffer[self.cursor..].chars().next() {
  52. self.cursor += next.len_utf8();
  53. }
  54. }
  55. pub fn move_home(&mut self) {
  56. self.cursor = 0;
  57. }
  58. pub fn move_end(&mut self) {
  59. self.cursor = self.buffer.len();
  60. }
  61. #[must_use]
  62. pub fn as_str(&self) -> &str {
  63. &self.buffer
  64. }
  65. #[cfg(test)]
  66. #[must_use]
  67. pub fn cursor(&self) -> usize {
  68. self.cursor
  69. }
  70. pub fn clear(&mut self) {
  71. self.buffer.clear();
  72. self.cursor = 0;
  73. }
  74. }
  75. pub struct LineEditor {
  76. prompt: String,
  77. }
  78. impl LineEditor {
  79. #[must_use]
  80. pub fn new(prompt: impl Into<String>) -> Self {
  81. Self {
  82. prompt: prompt.into(),
  83. }
  84. }
  85. pub fn read_line(&self) -> io::Result<Option<String>> {
  86. if !io::stdin().is_terminal() || !io::stdout().is_terminal() {
  87. return self.read_line_fallback();
  88. }
  89. enable_raw_mode()?;
  90. let mut stdout = io::stdout();
  91. let mut input = InputBuffer::new();
  92. self.redraw(&mut stdout, &input)?;
  93. loop {
  94. let event = event::read()?;
  95. if let Event::Key(key) = event {
  96. match Self::handle_key(key, &mut input) {
  97. EditorAction::Continue => self.redraw(&mut stdout, &input)?,
  98. EditorAction::Submit => {
  99. disable_raw_mode()?;
  100. writeln!(stdout)?;
  101. return Ok(Some(input.as_str().to_owned()));
  102. }
  103. EditorAction::Cancel => {
  104. disable_raw_mode()?;
  105. writeln!(stdout)?;
  106. return Ok(None);
  107. }
  108. }
  109. }
  110. }
  111. }
  112. fn read_line_fallback(&self) -> io::Result<Option<String>> {
  113. let mut stdout = io::stdout();
  114. write!(stdout, "{}", self.prompt)?;
  115. stdout.flush()?;
  116. let mut buffer = String::new();
  117. let bytes_read = io::stdin().read_line(&mut buffer)?;
  118. if bytes_read == 0 {
  119. return Ok(None);
  120. }
  121. while matches!(buffer.chars().last(), Some('\n' | '\r')) {
  122. buffer.pop();
  123. }
  124. Ok(Some(buffer))
  125. }
  126. fn handle_key(key: KeyEvent, input: &mut InputBuffer) -> EditorAction {
  127. match key {
  128. KeyEvent {
  129. code: KeyCode::Char('c'),
  130. modifiers,
  131. ..
  132. } if modifiers.contains(KeyModifiers::CONTROL) => EditorAction::Cancel,
  133. KeyEvent {
  134. code: KeyCode::Char('j'),
  135. modifiers,
  136. ..
  137. } if modifiers.contains(KeyModifiers::CONTROL) => {
  138. input.insert_newline();
  139. EditorAction::Continue
  140. }
  141. KeyEvent {
  142. code: KeyCode::Enter,
  143. modifiers,
  144. ..
  145. } if modifiers.contains(KeyModifiers::SHIFT) => {
  146. input.insert_newline();
  147. EditorAction::Continue
  148. }
  149. KeyEvent {
  150. code: KeyCode::Enter,
  151. ..
  152. } => EditorAction::Submit,
  153. KeyEvent {
  154. code: KeyCode::Backspace,
  155. ..
  156. } => {
  157. input.backspace();
  158. EditorAction::Continue
  159. }
  160. KeyEvent {
  161. code: KeyCode::Left,
  162. ..
  163. } => {
  164. input.move_left();
  165. EditorAction::Continue
  166. }
  167. KeyEvent {
  168. code: KeyCode::Right,
  169. ..
  170. } => {
  171. input.move_right();
  172. EditorAction::Continue
  173. }
  174. KeyEvent {
  175. code: KeyCode::Home,
  176. ..
  177. } => {
  178. input.move_home();
  179. EditorAction::Continue
  180. }
  181. KeyEvent {
  182. code: KeyCode::End, ..
  183. } => {
  184. input.move_end();
  185. EditorAction::Continue
  186. }
  187. KeyEvent {
  188. code: KeyCode::Esc, ..
  189. } => {
  190. input.clear();
  191. EditorAction::Cancel
  192. }
  193. KeyEvent {
  194. code: KeyCode::Char(ch),
  195. modifiers,
  196. ..
  197. } if modifiers.is_empty() || modifiers == KeyModifiers::SHIFT => {
  198. input.insert(ch);
  199. EditorAction::Continue
  200. }
  201. _ => EditorAction::Continue,
  202. }
  203. }
  204. fn redraw(&self, out: &mut impl Write, input: &InputBuffer) -> io::Result<()> {
  205. let display = input.as_str().replace('\n', "\\n\n> ");
  206. queue!(
  207. out,
  208. MoveToColumn(0),
  209. Clear(ClearType::CurrentLine),
  210. Print(&self.prompt),
  211. Print(display),
  212. )?;
  213. out.flush()
  214. }
  215. }
  216. #[derive(Debug, Clone, Copy, PartialEq, Eq)]
  217. enum EditorAction {
  218. Continue,
  219. Submit,
  220. Cancel,
  221. }
  222. #[cfg(test)]
  223. mod tests {
  224. use super::InputBuffer;
  225. #[test]
  226. fn supports_basic_line_editing() {
  227. let mut input = InputBuffer::new();
  228. input.insert('h');
  229. input.insert('i');
  230. input.move_end();
  231. input.insert_newline();
  232. input.insert('x');
  233. assert_eq!(input.as_str(), "hi\nx");
  234. assert_eq!(input.cursor(), 4);
  235. input.move_left();
  236. input.backspace();
  237. assert_eq!(input.as_str(), "hix");
  238. assert_eq!(input.cursor(), 2);
  239. }
  240. }