repl.rs 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  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.strip_prefix(so_far).map(|s| ansi_term::Colour::Blue.dimmed().paint(s).to_string())
  77. } else {
  78. return None;
  79. }
  80. }
  81. }
  82. None
  83. }
  84. }
  85. impl Highlighter for Repl {}
  86. impl Validator for Repl {}
  87. impl Helper for Repl {}