repl.rs 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. use rustyline::{
  2. completion::Completer, highlight::Highlighter, hint::Hinter, validate::Validator, Helper,
  3. };
  4. use std::cell::RefCell;
  5. use std::rc::Rc;
  6. pub struct Repl {
  7. state: Rc<RefCell<crate::interp::State>>,
  8. }
  9. impl Repl {
  10. pub fn new(state: Rc<RefCell<crate::interp::State>>) -> Repl {
  11. Repl { state }
  12. }
  13. }
  14. impl Completer for Repl {
  15. type Candidate = String;
  16. fn complete(
  17. &self,
  18. line: &str,
  19. pos: usize,
  20. _ctx: &rustyline::Context<'_>,
  21. ) -> rustyline::Result<(usize, Vec<String>)> {
  22. if let Some(c) = line.chars().nth(pos - 1) {
  23. if c.is_alphabetic() {
  24. // this means we're looking at maybe something
  25. // alphabetic; let's see what the current typed thing
  26. // is
  27. let mut str_start = 0;
  28. for (idx, ch) in line.chars().enumerate() {
  29. if ch.is_whitespace() {
  30. str_start = idx + 1;
  31. }
  32. if idx == pos {
  33. break;
  34. }
  35. }
  36. // we've now found the current fragment
  37. let so_far = &line[str_start..pos];
  38. return Ok((
  39. str_start,
  40. self.state.borrow().autocomplete(so_far, str_start == 0),
  41. ));
  42. }
  43. }
  44. Ok((pos, Vec::new()))
  45. }
  46. }
  47. impl Hinter for Repl {
  48. type Hint = String;
  49. fn hint(&self, line: &str, pos: usize, _ctx: &rustyline::Context<'_>) -> Option<String> {
  50. if pos == 0 {
  51. return None;
  52. }
  53. if let Some(c) = line.chars().nth(pos - 1) {
  54. if c.is_alphabetic() {
  55. // this means we're looking at maybe something
  56. // alphabetic; let's see what the current typed thing
  57. // is
  58. let mut str_start = 0;
  59. for (idx, ch) in line.chars().enumerate() {
  60. if ch.is_whitespace() {
  61. str_start = idx + 1;
  62. }
  63. if idx == pos {
  64. break;
  65. }
  66. }
  67. // don't suggest for stuff that's too short
  68. if pos - str_start < 2 {
  69. return None;
  70. }
  71. // we've now found the current fragment
  72. let so_far = &line[str_start..pos];
  73. let autocompletes = self.state.borrow().autocomplete(so_far, str_start == 0);
  74. if autocompletes.len() == 1 {
  75. let known = autocompletes.first().unwrap();
  76. return known
  77. .strip_prefix(so_far)
  78. .map(|s| ansi_term::Colour::Blue.dimmed().paint(s).to_string());
  79. } else {
  80. return None;
  81. }
  82. }
  83. }
  84. None
  85. }
  86. }
  87. impl Highlighter for Repl {}
  88. impl Validator for Repl {}
  89. impl Helper for Repl {}