diff --git a/Cargo.lock b/Cargo.lock index b844e0a..ebf9ade 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -941,12 +941,24 @@ dependencies = [ "memchr", "nix", "radix_trie", + "rustyline-derive", "unicode-segmentation", "unicode-width", "utf8parse", "windows-sys 0.52.0", ] +[[package]] +name = "rustyline-derive" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5af959c8bf6af1aff6d2b463a57f71aae53d1332da58419e30ad8dc7011d951" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "ryu" version = "1.0.18" diff --git a/crates/shell/Cargo.toml b/crates/shell/Cargo.toml index 601fdc3..a48bbe5 100644 --- a/crates/shell/Cargo.toml +++ b/crates/shell/Cargo.toml @@ -23,7 +23,7 @@ anyhow = "1.0.87" clap = { version = "4.5.17", features = ["derive"] } deno_task_shell = { path = "../deno_task_shell" } futures = "0.3.30" -rustyline = "14.0.0" +rustyline = { version = "14.0.0", features = ["derive"] } tokio = "1.40.0" uu_ls = "0.0.27" dirs = "5.0.1" diff --git a/crates/shell/src/completion.rs b/crates/shell/src/completion.rs index 70d7a52..fec1dae 100644 --- a/crates/shell/src/completion.rs +++ b/crates/shell/src/completion.rs @@ -11,6 +11,12 @@ use std::path::Path; pub struct ShellCompleter; +impl Default for ShellCompleter { + fn default() -> Self { + ShellCompleter + } +} + impl Completer for ShellCompleter { type Candidate = Pair; @@ -38,7 +44,7 @@ impl Completer for ShellCompleter { } fn extract_word(line: &str, pos: usize) -> (usize, &str) { - if line.ends_with(" ") { + if line.ends_with(' ') { return (pos, ""); } let words: Vec<_> = line[..pos].split_whitespace().collect(); diff --git a/crates/shell/src/helper.rs b/crates/shell/src/helper.rs new file mode 100644 index 0000000..5f79a11 --- /dev/null +++ b/crates/shell/src/helper.rs @@ -0,0 +1,43 @@ +use rustyline::{ + highlight::Highlighter, validate::MatchingBracketValidator, Completer, Helper, Hinter, + Validator, +}; + +use crate::completion; + +use std::borrow::Cow::Borrowed; + +#[derive(Helper, Completer, Hinter, Validator)] +pub(crate) struct ShellPromptHelper { + #[rustyline(Completer)] + completer: completion::ShellCompleter, + + #[rustyline(Validator)] + validator: MatchingBracketValidator, + + pub colored_prompt: String, +} + +impl Default for ShellPromptHelper { + fn default() -> Self { + Self { + completer: completion::ShellCompleter, + validator: MatchingBracketValidator::new(), + colored_prompt: String::new(), + } + } +} + +impl Highlighter for ShellPromptHelper { + fn highlight_prompt<'b, 's: 'b, 'p: 'b>( + &'s self, + prompt: &'p str, + default: bool, + ) -> std::borrow::Cow<'b, str> { + if default { + Borrowed(&self.colored_prompt) + } else { + Borrowed(prompt) + } + } +} diff --git a/crates/shell/src/main.rs b/crates/shell/src/main.rs index 913ccd5..454c464 100644 --- a/crates/shell/src/main.rs +++ b/crates/shell/src/main.rs @@ -4,7 +4,6 @@ use std::rc::Rc; use anyhow::Context; use clap::Parser; -use completion::ShellCompleter; use deno_task_shell::parser::debug_parse; use deno_task_shell::{ execute_sequential_list, AsyncCommandBehavior, ExecuteResult, ShellCommand, ShellPipeReader, @@ -15,6 +14,7 @@ use rustyline::{CompletionType, Config, Editor}; mod commands; mod completion; +mod helper; fn commands() -> HashMap> { HashMap::from([( @@ -80,9 +80,12 @@ async fn interactive() -> anyhow::Result<()> { let mut rl = Editor::with_config(config)?; - let h = ShellCompleter {}; + let helper = helper::ShellPromptHelper::default(); + rl.set_helper(Some(helper)); - rl.set_helper(Some(h)); + // let h = ShellCompleter {}; + + // rl.set_helper(Some(h)); let mut state = init_state(); @@ -102,6 +105,7 @@ async fn interactive() -> anyhow::Result<()> { if !state.last_command_cd() { state.update_git_branch(); } + let mut git_branch: String = "".to_string(); if state.git_repository() { git_branch = match state.git_branch().strip_prefix("ref: refs/heads/") { @@ -117,10 +121,15 @@ async fn interactive() -> anyhow::Result<()> { git_branch = "(".to_owned() + &git_branch + ")"; } - let prompt = cwd - .strip_prefix(home_str) - .map(|stripped| format!("~{}{git_branch}$ ", stripped.replace('\\', "/"))) - .unwrap_or_else(|| format!("{}{git_branch}$ ", cwd)); + let display_cwd = if let Some(stripped) = cwd.strip_prefix(home_str) { + format!("~{}", stripped.replace('\\', "/")) + } else { + cwd.to_string() + }; + + let prompt = format!("{}{git_branch}$ ", display_cwd); + let color_prompt = format!("\x1b[34m{}\x1b[31m{git_branch}\x1b[0m$ ", display_cwd); + rl.helper_mut().unwrap().colored_prompt = color_prompt; rl.readline(&prompt) };