Files
shed/src/prompt/readline.rs

87 lines
2.0 KiB
Rust

use rustyline::{completion::Completer, hint::{Hint, Hinter}, history::SearchDirection, validate::{ValidationResult, Validator}, Helper};
use crate::{libsh::term::{Style, Styled}, parse::{lex::{LexFlags, LexStream}, ParseStream}};
use crate::prelude::*;
#[derive(Default,Debug)]
pub struct FernReadline;
impl FernReadline {
pub fn new() -> Self {
Self
}
pub fn search_hist(value: &str, ctx: &rustyline::Context<'_>) -> Option<String> {
let len = ctx.history().len();
for i in 0..len {
let entry = ctx.history().get(i, SearchDirection::Reverse).unwrap().unwrap();
if entry.entry.starts_with(value) {
return Some(entry.entry.into_owned())
}
}
None
}
}
impl Helper for FernReadline {}
impl Completer for FernReadline {
type Candidate = String;
}
pub struct FernHint {
raw: String,
styled: String
}
impl FernHint {
pub fn new(raw: String) -> Self {
let styled = (&raw).styled(Style::Dim | Style::BrightBlack);
Self { raw, styled }
}
}
impl Hint for FernHint {
fn display(&self) -> &str {
&self.styled
}
fn completion(&self) -> Option<&str> {
if !self.raw.is_empty() {
Some(&self.raw)
} else {
None
}
}
}
impl Hinter for FernReadline {
type Hint = FernHint;
fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option<Self::Hint> {
if line.is_empty() {
return None
}
let ent = Self::search_hist(line,ctx)?;
let entry_raw = ent.get(pos..)?.to_string();
Some(FernHint::new(entry_raw))
}
}
impl Validator for FernReadline {
fn validate(&self, ctx: &mut rustyline::validate::ValidationContext) -> rustyline::Result<rustyline::validate::ValidationResult> {
let mut tokens = vec![];
let tk_stream = LexStream::new(Arc::new(ctx.input().to_string()), LexFlags::empty());
for tk in tk_stream {
if tk.is_err() {
return Ok(ValidationResult::Incomplete)
}
tokens.push(tk.unwrap());
}
let nd_stream = ParseStream::new(tokens);
for nd in nd_stream {
if nd.is_err() {
return Ok(ValidationResult::Incomplete)
}
}
Ok(ValidationResult::Valid(None))
}
}