1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
//! Prompt handling for shell instances.
use std::borrow::Cow;
use crate::{Shell, error, extensions, prompt};
impl<SE: extensions::ShellExtensions> Shell<SE> {
/// Returns the default prompt string for the shell.
const fn default_prompt(&self) -> &'static str {
if self.options.sh_mode {
"$ "
} else {
"shrub$ "
}
}
/// Composes the shell's post-input, pre-command prompt, applying all
/// appropriate expansions.
///
/// # Errors
///
/// Returns [`crate::error::ErrorKind::WordParseError`] if the value of
/// `PS0` is not a valid prompt string, or the error from any expansion in
/// it, such as a failing command substitution.
pub async fn compose_precmd_prompt(&mut self) -> Result<String, error::Error> {
self.expand_prompt_var("PS0", "").await
}
/// Composes the shell's prompt, applying all appropriate expansions.
///
/// # Errors
///
/// Returns [`crate::error::ErrorKind::WordParseError`] if the value of
/// `PS1` is not a valid prompt string, or the error from any expansion in
/// it, such as a failing command substitution.
pub async fn compose_prompt(&mut self) -> Result<String, error::Error> {
self.expand_prompt_var("PS1", self.default_prompt()).await
}
/// Composes the shell's alternate-side prompt, applying all appropriate
/// expansions.
///
/// # Errors
///
/// Returns [`crate::error::ErrorKind::WordParseError`] if the value of
/// `SHRUB_PS_ALT` is not a valid prompt string, or the error from any
/// expansion in it, such as a failing command substitution.
pub async fn compose_alt_side_prompt(&mut self) -> Result<String, error::Error> {
// This is a shrub extension.
self.expand_prompt_var("SHRUB_PS_ALT", "").await
}
/// Composes the shell's continuation prompt.
///
/// # Errors
///
/// Returns [`crate::error::ErrorKind::WordParseError`] if the value of
/// `PS2` is not a valid prompt string, or the error from any expansion in
/// it, such as a failing command substitution.
pub async fn compose_continuation_prompt(&mut self) -> Result<String, error::Error> {
self.expand_prompt_var("PS2", "> ").await
}
pub(super) async fn expand_prompt_var(
&mut self,
var_name: &str,
default: &str,
) -> Result<String, error::Error> {
//
// TODO(prompt): bash appears to do this in a subshell; we need to
// investigate if that's required.
//
// Retrieve the spec.
let prompt_spec = self.parameter_or_default(var_name, default);
if prompt_spec.is_empty() {
return Ok(String::new());
}
// Save (and later restore) the state the last command left behind, so
// that expanding here is invisible to it.
let saved_status = self.save_command_status();
// Expand it. We must own the spec here: it borrows `self` (via the
// returned `Cow`), and `expand_prompt` needs `&mut self`.
let prompt_spec = prompt_spec.into_owned();
let params = self.default_exec_params();
let result = prompt::expand_prompt(self, ¶ms, &prompt_spec).await;
self.restore_command_status(saved_status);
// Strip out special characters that readline would typically drop:
// \001 and \002 (start and end of non-printing sequences).
let mut expanded = result?;
expanded.retain(|c| c != '\x01' && c != '\x02');
Ok(expanded)
}
fn parameter_or_default<'a>(&'a self, name: &str, default: &'a str) -> Cow<'a, str> {
self.env_str(name).unwrap_or_else(|| default.into())
}
}