input.rs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. use std::io::{self, 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. enable_raw_mode()?;
  87. let mut stdout = io::stdout();
  88. let mut input = InputBuffer::new();
  89. self.redraw(&mut stdout, &input)?;
  90. loop {
  91. let event = event::read()?;
  92. if let Event::Key(key) = event {
  93. match Self::handle_key(key, &mut input) {
  94. EditorAction::Continue => self.redraw(&mut stdout, &input)?,
  95. EditorAction::Submit => {
  96. disable_raw_mode()?;
  97. writeln!(stdout)?;
  98. return Ok(Some(input.as_str().to_owned()));
  99. }
  100. EditorAction::Cancel => {
  101. disable_raw_mode()?;
  102. writeln!(stdout)?;
  103. return Ok(None);
  104. }
  105. }
  106. }
  107. }
  108. }
  109. fn handle_key(key: KeyEvent, input: &mut InputBuffer) -> EditorAction {
  110. match key {
  111. KeyEvent {
  112. code: KeyCode::Char('c'),
  113. modifiers,
  114. ..
  115. } if modifiers.contains(KeyModifiers::CONTROL) => EditorAction::Cancel,
  116. KeyEvent {
  117. code: KeyCode::Char('j'),
  118. modifiers,
  119. ..
  120. } if modifiers.contains(KeyModifiers::CONTROL) => {
  121. input.insert_newline();
  122. EditorAction::Continue
  123. }
  124. KeyEvent {
  125. code: KeyCode::Enter,
  126. modifiers,
  127. ..
  128. } if modifiers.contains(KeyModifiers::SHIFT) => {
  129. input.insert_newline();
  130. EditorAction::Continue
  131. }
  132. KeyEvent {
  133. code: KeyCode::Enter,
  134. ..
  135. } => EditorAction::Submit,
  136. KeyEvent {
  137. code: KeyCode::Backspace,
  138. ..
  139. } => {
  140. input.backspace();
  141. EditorAction::Continue
  142. }
  143. KeyEvent {
  144. code: KeyCode::Left,
  145. ..
  146. } => {
  147. input.move_left();
  148. EditorAction::Continue
  149. }
  150. KeyEvent {
  151. code: KeyCode::Right,
  152. ..
  153. } => {
  154. input.move_right();
  155. EditorAction::Continue
  156. }
  157. KeyEvent {
  158. code: KeyCode::Home,
  159. ..
  160. } => {
  161. input.move_home();
  162. EditorAction::Continue
  163. }
  164. KeyEvent {
  165. code: KeyCode::End, ..
  166. } => {
  167. input.move_end();
  168. EditorAction::Continue
  169. }
  170. KeyEvent {
  171. code: KeyCode::Esc, ..
  172. } => {
  173. input.clear();
  174. EditorAction::Cancel
  175. }
  176. KeyEvent {
  177. code: KeyCode::Char(ch),
  178. modifiers,
  179. ..
  180. } if modifiers.is_empty() || modifiers == KeyModifiers::SHIFT => {
  181. input.insert(ch);
  182. EditorAction::Continue
  183. }
  184. _ => EditorAction::Continue,
  185. }
  186. }
  187. fn redraw(&self, out: &mut impl Write, input: &InputBuffer) -> io::Result<()> {
  188. let display = input.as_str().replace('\n', "\\n\n> ");
  189. queue!(
  190. out,
  191. MoveToColumn(0),
  192. Clear(ClearType::CurrentLine),
  193. Print(&self.prompt),
  194. Print(display),
  195. )?;
  196. out.flush()
  197. }
  198. }
  199. #[derive(Debug, Clone, Copy, PartialEq, Eq)]
  200. enum EditorAction {
  201. Continue,
  202. Submit,
  203. Cancel,
  204. }
  205. #[cfg(test)]
  206. mod tests {
  207. use super::InputBuffer;
  208. #[test]
  209. fn supports_basic_line_editing() {
  210. let mut input = InputBuffer::new();
  211. input.insert('h');
  212. input.insert('i');
  213. input.move_end();
  214. input.insert_newline();
  215. input.insert('x');
  216. assert_eq!(input.as_str(), "hi\nx");
  217. assert_eq!(input.cursor(), 4);
  218. input.move_left();
  219. input.backspace();
  220. assert_eq!(input.as_str(), "hix");
  221. assert_eq!(input.cursor(), 2);
  222. }
  223. }
备用站点 当前处于降级运行的备用站点,仅供应急访问,数据和功能可能不是最新。