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
|
use std::borrow::Cow;
use std::sync::{Arc, Mutex};
use crate::parse::{ExpString, Parse};
use crate::run::{Vars, WatchId};
use crate::{BString, Session, bstr};
pub const PROMPT_VAR: &str = "PROMPT";
#[allow(unused)]
const DEFAULT_PROMPT_VALUE_DEBUG: &bstr = b"dev [$CWD_PRETTY]# ";
#[allow(unused)]
const DEFAULT_PROMPT_VALUE_RELEASE: &bstr = b"[$CWD_PRETTY]# ";
#[cfg(test)]
const POSSIBLE_DEFAULT_PROMPT_VALUES: &[&bstr] =
&[DEFAULT_PROMPT_VALUE_DEBUG, DEFAULT_PROMPT_VALUE_RELEASE];
#[cfg(debug_assertions)]
pub const DEFAULT_PROMPT_VALUE: &bstr = DEFAULT_PROMPT_VALUE_DEBUG;
#[cfg(not(debug_assertions))]
pub const DEFAULT_PROMPT_VALUE: &bstr = DEFAULT_PROMPT_VALUE_RELEASE;
pub struct Prompt {
watch_var: WatchId,
watch_content: Option<WatchId>,
parsed: Option<ExpString>,
cached_prompt: Option<BString>,
}
impl Prompt {
pub fn new(vars: &mut Vars) -> Self {
Self {
watch_var: vars.watch(vec![PROMPT_VAR.as_bytes().to_vec()]),
watch_content: None,
parsed: None,
cached_prompt: None,
}
}
pub fn requires_update(&self, vars: &Vars) -> bool {
self.parsed.iter().any(|p| p.has_commands())
|| vars.peek_dirty(&self.watch_var)
|| self.watch_content.iter().any(|wc| vars.peek_dirty(wc))
}
pub fn load_prompt(&mut self, vars: &mut Vars) {
if vars.pop_dirty(&self.watch_var) {
let prompt = vars
.lookup(PROMPT_VAR.as_bytes())
.map(Cow::into_owned)
.unwrap_or_else(|| DEFAULT_PROMPT_VALUE.to_vec());
let parsed = parse_prompt(prompt);
self.watch_content = Some(vars.watch(parsed.vars()));
self.parsed = Some(parsed);
}
if let Some(wc) = &self.watch_content {
vars.pop_dirty(wc);
}
}
pub fn eval_prompt(&mut self, se: Arc<Mutex<Session>>) {
let mut expander = crate::run::Executor::new(se);
let expanded = self
.parsed
.clone()
.unwrap_or_else(|| parse_prompt(DEFAULT_PROMPT_VALUE.to_vec()))
.expand(&mut expander)
.unwrap_or_else(|_| b"EXEC_ERROR$ ".to_vec());
self.cached_prompt = Some(expanded);
}
pub fn prompt(&self) -> &bstr {
self.cached_prompt.as_ref().map(|p| &p[..]).unwrap_or(b"> ")
}
}
fn parse_prompt_fallible(mut prompt: BString) -> Option<ExpString> {
let mut x = Vec::with_capacity(prompt.len() + 2);
x.push(b'"');
x.append(&mut prompt);
x.push(b'"');
crate::parse::ExpString::parse_from_bytes(&x).ok()
}
fn parse_prompt(prompt: BString) -> ExpString {
parse_prompt_fallible(prompt)
.unwrap_or_else(|| parse_prompt_fallible(DEFAULT_PROMPT_VALUE.to_vec()).unwrap())
}
#[test]
fn default_prompt_should_parse() {
for prompt in POSSIBLE_DEFAULT_PROMPT_VALUES {
parse_prompt_fallible(prompt.to_vec()).unwrap();
}
}
|