Skip to main content

brush_core/shell/
prompts.rs

1//! Prompt handling for shell instances.
2
3use std::borrow::Cow;
4
5use crate::{Shell, error, extensions, prompt};
6
7impl<SE: extensions::ShellExtensions> Shell<SE> {
8    /// Returns the default prompt string for the shell.
9    const fn default_prompt(&self) -> &'static str {
10        if self.options.sh_mode {
11            "$ "
12        } else {
13            "brush$ "
14        }
15    }
16
17    /// Composes the shell's post-input, pre-command prompt, applying all appropriate expansions.
18    pub async fn compose_precmd_prompt(&mut self) -> Result<String, error::Error> {
19        self.expand_prompt_var("PS0", "").await
20    }
21
22    /// Composes the shell's prompt, applying all appropriate expansions.
23    pub async fn compose_prompt(&mut self) -> Result<String, error::Error> {
24        self.expand_prompt_var("PS1", self.default_prompt()).await
25    }
26
27    /// Composes the shell's alternate-side prompt, applying all appropriate expansions.
28    pub async fn compose_alt_side_prompt(&mut self) -> Result<String, error::Error> {
29        // This is a brush extension.
30        self.expand_prompt_var("BRUSH_PS_ALT", "").await
31    }
32
33    /// Composes the shell's continuation prompt.
34    pub async fn compose_continuation_prompt(&mut self) -> Result<String, error::Error> {
35        self.expand_prompt_var("PS2", "> ").await
36    }
37
38    pub(super) async fn expand_prompt_var(
39        &mut self,
40        var_name: &str,
41        default: &str,
42    ) -> Result<String, error::Error> {
43        //
44        // TODO(prompt): bash appears to do this in a subshell; we need to investigate
45        // if that's required.
46        //
47
48        // Retrieve the spec.
49        let prompt_spec = self.parameter_or_default(var_name, default);
50        if prompt_spec.is_empty() {
51            return Ok(String::new());
52        }
53
54        // Save (and later restore) the last exit status.
55        let prev_last_result = self.last_exit_status();
56        let prev_last_pipeline_statuses = self.last_pipeline_statuses.clone();
57
58        // Expand it.
59        let params = self.default_exec_params();
60        let result = prompt::expand_prompt(self, &params, prompt_spec.into_owned()).await;
61
62        // Restore the last exit status.
63        self.last_pipeline_statuses = prev_last_pipeline_statuses;
64        self.set_last_exit_status(prev_last_result);
65
66        // Strip out special characters that readline would typically drop:
67        // \001 and \002 (start and end of non-printing sequences).
68        let mut expanded = result?;
69        expanded.retain(|c| c != '\x01' && c != '\x02');
70
71        Ok(expanded)
72    }
73
74    fn parameter_or_default<'a>(&'a self, name: &str, default: &'a str) -> Cow<'a, str> {
75        self.env_str(name).unwrap_or_else(|| default.into())
76    }
77}