fix(escaping): move escaping to individual variables (#3107)
This commit is contained in:
parent
73277d37c6
commit
c1f2d345aa
|
@ -3319,6 +3319,19 @@ If you have an interesting example not covered there, feel free to share it ther
|
|||
|
||||
:::
|
||||
|
||||
::: warning Command output is printed unescaped to the prompt
|
||||
|
||||
Whatever output the command generates is printed unmodified in the prompt. This means if the output
|
||||
contains special sequences that are interpreted by your shell they will be expanded when displayed.
|
||||
These special sequences are shell specific, e.g. you can write a command module that writes bash sequences,
|
||||
e.g. `\h`, but this module will not work in a fish or zsh shell.
|
||||
|
||||
Format strings can also contain shell specific prompt sequences, e.g.
|
||||
[Bash](https://www.gnu.org/software/bash/manual/html_node/Controlling-the-Prompt.html),
|
||||
[Zsh](https://zsh.sourceforge.io/Doc/Release/Prompt-Expansion.html).
|
||||
|
||||
:::
|
||||
|
||||
### Options
|
||||
|
||||
| Option | Default | Description |
|
||||
|
|
|
@ -7,6 +7,7 @@ use std::error::Error;
|
|||
use std::fmt;
|
||||
|
||||
use crate::config::parse_style_string;
|
||||
use crate::context::{Context, Shell};
|
||||
use crate::segment::Segment;
|
||||
|
||||
use super::model::*;
|
||||
|
@ -15,6 +16,7 @@ use super::parser::{parse, Rule};
|
|||
#[derive(Clone)]
|
||||
enum VariableValue<'a> {
|
||||
Plain(Cow<'a, str>),
|
||||
NoEscapingPlain(Cow<'a, str>),
|
||||
Styled(Vec<Segment>),
|
||||
Meta(Vec<FormatElement<'a>>),
|
||||
}
|
||||
|
@ -123,6 +125,27 @@ impl<'a> StringFormatter<'a> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Maps variable name into a value which is wrapped to prevent escaping later
|
||||
///
|
||||
/// This should be used for variables that should not be escaped before inclusion in the prompt
|
||||
///
|
||||
/// See `StringFormatter::map` for description on the parameters.
|
||||
///
|
||||
pub fn map_no_escaping<T, M>(mut self, mapper: M) -> Self
|
||||
where
|
||||
T: Into<Cow<'a, str>>,
|
||||
M: Fn(&str) -> Option<Result<T, StringFormatterError>> + Sync,
|
||||
{
|
||||
self.variables
|
||||
.par_iter_mut()
|
||||
.filter(|(_, value)| value.is_none())
|
||||
.for_each(|(key, value)| {
|
||||
*value = mapper(key)
|
||||
.map(|var| var.map(|var| VariableValue::NoEscapingPlain(var.into())));
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// Maps a meta-variable to a format string containing other variables.
|
||||
///
|
||||
/// This function should be called **before** other map methods so that variables found in
|
||||
|
@ -206,11 +229,16 @@ impl<'a> StringFormatter<'a> {
|
|||
///
|
||||
/// - Format string in meta variables fails to parse
|
||||
/// - Variable mapper returns an error.
|
||||
pub fn parse(self, default_style: Option<Style>) -> Result<Vec<Segment>, StringFormatterError> {
|
||||
pub fn parse(
|
||||
self,
|
||||
default_style: Option<Style>,
|
||||
context: Option<&Context>,
|
||||
) -> Result<Vec<Segment>, StringFormatterError> {
|
||||
fn parse_textgroup<'a>(
|
||||
textgroup: TextGroup<'a>,
|
||||
variables: &'a VariableMapType<'a>,
|
||||
style_variables: &'a StyleVariableMapType<'a>,
|
||||
context: Option<&Context>,
|
||||
) -> Result<Vec<Segment>, StringFormatterError> {
|
||||
let style = parse_style(textgroup.style, style_variables);
|
||||
parse_format(
|
||||
|
@ -218,6 +246,7 @@ impl<'a> StringFormatter<'a> {
|
|||
style.transpose()?,
|
||||
variables,
|
||||
style_variables,
|
||||
context,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -252,6 +281,7 @@ impl<'a> StringFormatter<'a> {
|
|||
style: Option<Style>,
|
||||
variables: &'a VariableMapType<'a>,
|
||||
style_variables: &'a StyleVariableMapType<'a>,
|
||||
context: Option<&Context>,
|
||||
) -> Result<Vec<Segment>, StringFormatterError> {
|
||||
let results: Result<Vec<Vec<Segment>>, StringFormatterError> = format
|
||||
.into_iter()
|
||||
|
@ -263,7 +293,7 @@ impl<'a> StringFormatter<'a> {
|
|||
format: textgroup.format,
|
||||
style: textgroup.style,
|
||||
};
|
||||
parse_textgroup(textgroup, variables, style_variables)
|
||||
parse_textgroup(textgroup, variables, style_variables, context)
|
||||
}
|
||||
FormatElement::Variable(name) => variables
|
||||
.get(name.as_ref())
|
||||
|
@ -278,14 +308,26 @@ impl<'a> StringFormatter<'a> {
|
|||
segment
|
||||
})
|
||||
.collect()),
|
||||
VariableValue::Plain(text) => Ok(Segment::from_text(style, text)),
|
||||
VariableValue::Plain(text) => Ok(Segment::from_text(
|
||||
style,
|
||||
shell_prompt_escape(
|
||||
text,
|
||||
match context {
|
||||
None => Shell::Unknown,
|
||||
Some(c) => c.shell,
|
||||
},
|
||||
),
|
||||
)),
|
||||
VariableValue::NoEscapingPlain(text) => {
|
||||
Ok(Segment::from_text(style, text))
|
||||
}
|
||||
VariableValue::Meta(format) => {
|
||||
let formatter = StringFormatter {
|
||||
format,
|
||||
variables: clone_without_meta(variables),
|
||||
style_variables: style_variables.clone(),
|
||||
};
|
||||
formatter.parse(style)
|
||||
formatter.parse(style, context)
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| Ok(Vec::new())),
|
||||
|
@ -320,6 +362,9 @@ impl<'a> StringFormatter<'a> {
|
|||
VariableValue::Plain(plain_value) => {
|
||||
!plain_value.is_empty()
|
||||
}
|
||||
VariableValue::NoEscapingPlain(
|
||||
no_escaping_plain_value,
|
||||
) => !no_escaping_plain_value.is_empty(),
|
||||
VariableValue::Styled(segments) => segments
|
||||
.iter()
|
||||
.any(|x| !x.value().is_empty()),
|
||||
|
@ -331,7 +376,7 @@ impl<'a> StringFormatter<'a> {
|
|||
let should_show: bool = should_show_elements(&format, variables);
|
||||
|
||||
if should_show {
|
||||
parse_format(format, style, variables, style_variables)
|
||||
parse_format(format, style, variables, style_variables, context)
|
||||
} else {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
@ -347,6 +392,7 @@ impl<'a> StringFormatter<'a> {
|
|||
default_style,
|
||||
&self.variables,
|
||||
&self.style_variables,
|
||||
context,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -380,6 +426,28 @@ fn clone_without_meta<'a>(variables: &VariableMapType<'a>) -> VariableMapType<'a
|
|||
.collect()
|
||||
}
|
||||
|
||||
/// Escape interpretable characters for the shell prompt
|
||||
pub fn shell_prompt_escape<T>(text: T, shell: Shell) -> String
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
// Handle other interpretable characters
|
||||
match shell {
|
||||
// Bash might interepret baskslashes, backticks and $
|
||||
// see #658 for more details
|
||||
Shell::Bash => text
|
||||
.into()
|
||||
.replace('\\', r"\\")
|
||||
.replace('$', r"\$")
|
||||
.replace('`', r"\`"),
|
||||
Shell::Zsh => {
|
||||
// % is an escape in zsh, see PROMPT in `man zshmisc`
|
||||
text.into().replace('%', "%%")
|
||||
}
|
||||
_ => text.into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -404,7 +472,7 @@ mod tests {
|
|||
let style = Some(Color::Red.bold());
|
||||
|
||||
let formatter = StringFormatter::new(FORMAT_STR).unwrap().map(empty_mapper);
|
||||
let result = formatter.parse(style).unwrap();
|
||||
let result = formatter.parse(style, None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "text", style);
|
||||
}
|
||||
|
@ -413,7 +481,7 @@ mod tests {
|
|||
fn test_textgroup_text_only() {
|
||||
const FORMAT_STR: &str = "[text](red bold)";
|
||||
let formatter = StringFormatter::new(FORMAT_STR).unwrap().map(empty_mapper);
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let result = formatter.parse(None, None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "text", Some(Color::Red.bold()));
|
||||
}
|
||||
|
@ -428,7 +496,7 @@ mod tests {
|
|||
"var1" => Some(Ok("text1".to_owned())),
|
||||
_ => None,
|
||||
});
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let result = formatter.parse(None, None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "text1", None);
|
||||
}
|
||||
|
@ -444,7 +512,7 @@ mod tests {
|
|||
"style" => Some(Ok("red bold".to_owned())),
|
||||
_ => None,
|
||||
});
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let result = formatter.parse(None, None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "root", root_style);
|
||||
}
|
||||
|
@ -456,7 +524,7 @@ mod tests {
|
|||
let formatter = StringFormatter::new(FORMAT_STR)
|
||||
.unwrap()
|
||||
.map(|variable| Some(Ok(format!("${{{}}}", variable))));
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let result = formatter.parse(None, None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "${env:PWD}", None);
|
||||
}
|
||||
|
@ -466,7 +534,7 @@ mod tests {
|
|||
const FORMAT_STR: &str = r#"\\\[\$text\]\(red bold\)"#;
|
||||
|
||||
let formatter = StringFormatter::new(FORMAT_STR).unwrap().map(empty_mapper);
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let result = formatter.parse(None, None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, r#"\[$text](red bold)"#, None);
|
||||
}
|
||||
|
@ -479,7 +547,7 @@ mod tests {
|
|||
let inner_style = Some(Color::Blue.normal());
|
||||
|
||||
let formatter = StringFormatter::new(FORMAT_STR).unwrap().map(empty_mapper);
|
||||
let result = formatter.parse(outer_style).unwrap();
|
||||
let result = formatter.parse(outer_style, None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "outer ", outer_style);
|
||||
match_next!(result_iter, "middle ", middle_style);
|
||||
|
@ -497,7 +565,7 @@ mod tests {
|
|||
"var" => Some(Ok("text".to_owned())),
|
||||
_ => None,
|
||||
});
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let result = formatter.parse(None, None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "text", var_style);
|
||||
}
|
||||
|
@ -523,7 +591,7 @@ mod tests {
|
|||
"var" => Some(Ok(segments.clone())),
|
||||
_ => None,
|
||||
});
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let result = formatter.parse(None, None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "styless", var_style);
|
||||
match_next!(result_iter, "styled", styled_style);
|
||||
|
@ -546,7 +614,7 @@ mod tests {
|
|||
"b" => Some(Ok("$b")),
|
||||
_ => None,
|
||||
});
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let result = formatter.parse(None, None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "$a", None);
|
||||
match_next!(result_iter, "$b", None);
|
||||
|
@ -568,7 +636,7 @@ mod tests {
|
|||
"c" => Some(Ok("$c")),
|
||||
_ => None,
|
||||
});
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let result = formatter.parse(None, None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "$a", None);
|
||||
match_next!(result_iter, "$b", None);
|
||||
|
@ -585,7 +653,7 @@ mod tests {
|
|||
"some" => Some(Ok("$some")),
|
||||
_ => None,
|
||||
});
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let result = formatter.parse(None, None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "$some", None);
|
||||
match_next!(result_iter, " should render but ", None);
|
||||
|
@ -602,7 +670,7 @@ mod tests {
|
|||
"empty" => Some(Ok("")),
|
||||
_ => None,
|
||||
});
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let result = formatter.parse(None, None).unwrap();
|
||||
assert_eq!(result.len(), 0);
|
||||
}
|
||||
|
||||
|
@ -616,7 +684,7 @@ mod tests {
|
|||
"empty" => Some(Ok("")),
|
||||
_ => None,
|
||||
});
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let result = formatter.parse(None, None).unwrap();
|
||||
assert_eq!(result.len(), 0);
|
||||
}
|
||||
|
||||
|
@ -630,7 +698,7 @@ mod tests {
|
|||
"some" => Some(Ok("$some")),
|
||||
_ => None,
|
||||
});
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let result = formatter.parse(None, None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, "$some", None);
|
||||
match_next!(result_iter, " ", None);
|
||||
|
@ -649,7 +717,7 @@ mod tests {
|
|||
"all" => Some("$some"),
|
||||
_ => None,
|
||||
});
|
||||
let result = formatter.parse(None).unwrap();
|
||||
let result = formatter.parse(None, None).unwrap();
|
||||
let mut result_iter = result.iter();
|
||||
match_next!(result_iter, " ", None);
|
||||
}
|
||||
|
@ -703,8 +771,50 @@ mod tests {
|
|||
"never" => Some(Err(never_error.clone())),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, None)
|
||||
});
|
||||
assert!(segments.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bash_escape() {
|
||||
let test = "$(echo a)";
|
||||
assert_eq!(
|
||||
shell_prompt_escape(test.to_owned(), Shell::Bash),
|
||||
r"\$(echo a)"
|
||||
);
|
||||
assert_eq!(
|
||||
shell_prompt_escape(test.to_owned(), Shell::PowerShell),
|
||||
test
|
||||
);
|
||||
|
||||
let test = r"\$(echo a)";
|
||||
assert_eq!(
|
||||
shell_prompt_escape(test.to_owned(), Shell::Bash),
|
||||
r"\\\$(echo a)"
|
||||
);
|
||||
assert_eq!(
|
||||
shell_prompt_escape(test.to_owned(), Shell::PowerShell),
|
||||
test
|
||||
);
|
||||
|
||||
let test = r"`echo a`";
|
||||
assert_eq!(
|
||||
shell_prompt_escape(test.to_owned(), Shell::Bash),
|
||||
r"\`echo a\`"
|
||||
);
|
||||
assert_eq!(
|
||||
shell_prompt_escape(test.to_owned(), Shell::PowerShell),
|
||||
test
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_zsh_escape() {
|
||||
let test = "10%";
|
||||
assert_eq!(shell_prompt_escape(test.to_owned(), Shell::Zsh), "10%%");
|
||||
assert_eq!(
|
||||
shell_prompt_escape(test.to_owned(), Shell::PowerShell),
|
||||
test
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ impl<'a> VersionFormatter<'a> {
|
|||
},
|
||||
_ => None,
|
||||
})
|
||||
.parse(None);
|
||||
.parse(None, None);
|
||||
|
||||
formatted.map(|segments| {
|
||||
segments
|
||||
|
|
|
@ -164,7 +164,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"duration" => duration.as_ref().map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -52,7 +52,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
_ => None,
|
||||
});
|
||||
|
||||
match formatter.parse(None) {
|
||||
match formatter.parse(None, Some(context)) {
|
||||
Ok(format_string) => {
|
||||
module.set_segments(format_string);
|
||||
Some(module)
|
||||
|
|
|
@ -53,7 +53,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"symbol" => Some(symbol),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -43,7 +43,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -37,7 +37,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"duration" => Some(Ok(render_time(elapsed, config.show_milliseconds))),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -43,7 +43,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -37,7 +37,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"environment" => Some(Ok(conda_env.as_str())),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -44,7 +44,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
.map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -56,7 +56,7 @@ pub fn module<'a>(name: &str, context: &'a Context) -> Option<Module<'a>> {
|
|||
"style" => Some(Ok(config.style)),
|
||||
_ => None,
|
||||
})
|
||||
.map(|variable| match variable {
|
||||
.map_no_escaping(|variable| match variable {
|
||||
"output" => {
|
||||
let output = exec_command(config.command, &config.shell.0)?;
|
||||
let trimmed = output.trim();
|
||||
|
@ -69,7 +69,7 @@ pub fn module<'a>(name: &str, context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
match parsed {
|
||||
|
|
|
@ -43,7 +43,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -42,7 +42,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -113,7 +113,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -71,7 +71,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"context" => Some(Ok(ctx.as_str())),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -74,7 +74,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"tfm" => find_current_tfm(&dotnet_files).map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -55,7 +55,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
.map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -42,7 +42,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -68,7 +68,7 @@ fn env_var_module<'a>(module_config_path: Vec<&str>, context: &'a Context) -> Op
|
|||
"env_value" => Some(Ok(&env_value)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -42,7 +42,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -151,7 +151,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"active" => Some(Ok(gcloud_context.config_name.clone())),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -95,7 +95,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -68,7 +68,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"tag" => format_tag(config.tag_symbol, &tag_name),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -50,7 +50,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"deleted" => GitDiff::get_variable(config.only_nonzero_diffs, stats.deleted),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -34,7 +34,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"progress_total" => state_description.total.as_ref().map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -49,49 +49,52 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
let info = Arc::clone(&info);
|
||||
let segments = match variable {
|
||||
"stashed" => info.get_stashed().and_then(|count| {
|
||||
format_count(config.stashed, "git_status.stashed", count)
|
||||
format_count(config.stashed, "git_status.stashed", context, count)
|
||||
}),
|
||||
"ahead_behind" => info.get_ahead_behind().and_then(|(ahead, behind)| {
|
||||
let (ahead, behind) = (ahead?, behind?);
|
||||
if ahead > 0 && behind > 0 {
|
||||
format_text(config.diverged, "git_status.diverged", |variable| {
|
||||
match variable {
|
||||
format_text(
|
||||
config.diverged,
|
||||
"git_status.diverged",
|
||||
context,
|
||||
|variable| match variable {
|
||||
"ahead_count" => Some(ahead.to_string()),
|
||||
"behind_count" => Some(behind.to_string()),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
} else if ahead > 0 && behind == 0 {
|
||||
format_count(config.ahead, "git_status.ahead", ahead)
|
||||
format_count(config.ahead, "git_status.ahead", context, ahead)
|
||||
} else if behind > 0 && ahead == 0 {
|
||||
format_count(config.behind, "git_status.behind", behind)
|
||||
format_count(config.behind, "git_status.behind", context, behind)
|
||||
} else {
|
||||
format_symbol(config.up_to_date, "git_status.up_to_date")
|
||||
format_symbol(config.up_to_date, "git_status.up_to_date", context)
|
||||
}
|
||||
}),
|
||||
"conflicted" => info.get_conflicted().and_then(|count| {
|
||||
format_count(config.conflicted, "git_status.conflicted", count)
|
||||
format_count(config.conflicted, "git_status.conflicted", context, count)
|
||||
}),
|
||||
"deleted" => info.get_deleted().and_then(|count| {
|
||||
format_count(config.deleted, "git_status.deleted", count)
|
||||
format_count(config.deleted, "git_status.deleted", context, count)
|
||||
}),
|
||||
"renamed" => info.get_renamed().and_then(|count| {
|
||||
format_count(config.renamed, "git_status.renamed", count)
|
||||
format_count(config.renamed, "git_status.renamed", context, count)
|
||||
}),
|
||||
"modified" => info.get_modified().and_then(|count| {
|
||||
format_count(config.modified, "git_status.modified", count)
|
||||
format_count(config.modified, "git_status.modified", context, count)
|
||||
}),
|
||||
"staged" => info.get_staged().and_then(|count| {
|
||||
format_count(config.staged, "git_status.staged", context, count)
|
||||
}),
|
||||
"staged" => info
|
||||
.get_staged()
|
||||
.and_then(|count| format_count(config.staged, "git_status.staged", count)),
|
||||
"untracked" => info.get_untracked().and_then(|count| {
|
||||
format_count(config.untracked, "git_status.untracked", count)
|
||||
format_count(config.untracked, "git_status.untracked", context, count)
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
segments.map(Ok)
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
@ -288,14 +291,19 @@ impl RepoStatus {
|
|||
}
|
||||
}
|
||||
|
||||
fn format_text<F>(format_str: &str, config_path: &str, mapper: F) -> Option<Vec<Segment>>
|
||||
fn format_text<F>(
|
||||
format_str: &str,
|
||||
config_path: &str,
|
||||
context: &Context,
|
||||
mapper: F,
|
||||
) -> Option<Vec<Segment>>
|
||||
where
|
||||
F: Fn(&str) -> Option<String> + Send + Sync,
|
||||
{
|
||||
if let Ok(formatter) = StringFormatter::new(format_str) {
|
||||
formatter
|
||||
.map(|variable| mapper(variable).map(Ok))
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
.ok()
|
||||
} else {
|
||||
log::warn!("Error parsing format string `{}`", &config_path);
|
||||
|
@ -303,19 +311,29 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn format_count(format_str: &str, config_path: &str, count: usize) -> Option<Vec<Segment>> {
|
||||
fn format_count(
|
||||
format_str: &str,
|
||||
config_path: &str,
|
||||
context: &Context,
|
||||
count: usize,
|
||||
) -> Option<Vec<Segment>> {
|
||||
if count == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
format_text(format_str, config_path, |variable| match variable {
|
||||
format_text(
|
||||
format_str,
|
||||
config_path,
|
||||
context,
|
||||
|variable| match variable {
|
||||
"count" => Some(count.to_string()),
|
||||
_ => None,
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn format_symbol(format_str: &str, config_path: &str) -> Option<Vec<Segment>> {
|
||||
format_text(format_str, config_path, |_variable| None)
|
||||
fn format_symbol(format_str: &str, config_path: &str, context: &Context) -> Option<Vec<Segment>> {
|
||||
format_text(format_str, config_path, context, |_variable| None)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -43,7 +43,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -46,7 +46,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -60,7 +60,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"branch" => Some(Ok(truncated_and_symbol.as_str())),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -51,7 +51,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"hostname" => Some(Ok(host)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -45,7 +45,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -88,7 +88,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"number" => Some(Ok(module_number.clone())),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -43,7 +43,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -46,7 +46,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -104,7 +104,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"namespace" => kube_ns.as_ref().map(|s| Ok(Cow::Borrowed(s.as_str()))),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -45,7 +45,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -86,7 +86,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"swap_pct" if total_swap_kib > 0 => Some(Ok(&swap_pct)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -44,7 +44,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
.map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -46,7 +46,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"name" => shell_name.as_ref().map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -73,7 +73,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -73,7 +73,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -69,7 +69,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"project" => osp_project.as_ref().map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -29,7 +29,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"version" => Some(Ok(&module_version)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -43,7 +43,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -42,7 +42,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -43,7 +43,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"stack" => stack_name(&project_file, context).map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
match parsed {
|
||||
|
|
|
@ -41,7 +41,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -61,7 +61,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"pyenv_prefix" => Some(Ok(pyenv_prefix.to_string())),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -41,7 +41,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
.map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -43,7 +43,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -41,7 +41,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
.map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -42,7 +42,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"version" => get_module_version(context, &config).map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -42,7 +42,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -46,7 +46,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"unknown_indicator" => Some(Ok(config.unknown_indicator)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -47,7 +47,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"shlvl" => Some(Ok(shlvl_str)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -26,7 +26,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"env" => Some(Ok(&singularity_env)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -61,7 +61,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
PipeStatusStatus::Pipe(pipestatus) => pipestatus
|
||||
.iter()
|
||||
.map(
|
||||
|ec| match format_exit_code(ec.as_str(), config.format, None, &config) {
|
||||
|ec| match format_exit_code(ec.as_str(), config.format, None, &config, context) {
|
||||
Ok(segments) => segments
|
||||
.into_iter()
|
||||
.map(|s| s.to_string())
|
||||
|
@ -79,7 +79,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
PipeStatusStatus::Pipe(_) => config.pipestatus_format,
|
||||
_ => config.format,
|
||||
};
|
||||
let parsed = format_exit_code(exit_code, main_format, Some(&pipestatus), &config);
|
||||
let parsed = format_exit_code(exit_code, main_format, Some(&pipestatus), &config, context);
|
||||
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
|
@ -96,6 +96,7 @@ fn format_exit_code<'a>(
|
|||
format: &'a str,
|
||||
pipestatus: Option<&str>,
|
||||
config: &'a StatusConfig,
|
||||
context: &'a Context,
|
||||
) -> Result<Vec<Segment>, StringFormatterError> {
|
||||
let exit_code_int: ExitCode = match exit_code.parse() {
|
||||
Ok(i) => i,
|
||||
|
@ -164,7 +165,7 @@ fn format_exit_code<'a>(
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -49,7 +49,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"workspace" => get_terraform_workspace(context).map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -54,7 +54,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"time" => Some(Ok(&formatted_time_string)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -48,7 +48,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"user" => Some(Ok(&username)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
module.set_segments(match parsed {
|
||||
Ok(segments) => segments,
|
||||
|
|
|
@ -44,7 +44,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
.map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -30,7 +30,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
"repo" => Some(Ok(&repo)),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -42,7 +42,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
.map(Ok),
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -42,7 +42,7 @@ pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
|
|||
}
|
||||
_ => None,
|
||||
})
|
||||
.parse(None)
|
||||
.parse(None, Some(context))
|
||||
});
|
||||
|
||||
module.set_segments(match parsed {
|
||||
|
|
|
@ -109,7 +109,7 @@ pub fn get_prompt(context: Context) -> String {
|
|||
let mut root_module = Module::new("Starship Root", "The root module", None);
|
||||
root_module.set_segments(
|
||||
formatter
|
||||
.parse(None)
|
||||
.parse(None, Some(&context))
|
||||
.expect("Unexpected error returned in root format variables"),
|
||||
);
|
||||
|
||||
|
|
62
src/utils.rs
62
src/utils.rs
|
@ -318,25 +318,8 @@ CMake suite maintained and supported by Kitware (kitware.com/cmake).\n",
|
|||
}
|
||||
}
|
||||
|
||||
/// Wraps ANSI color escape sequences and other interpretable characters
|
||||
/// that need to be escaped in the shell-appropriate wrappers.
|
||||
pub fn wrap_colorseq_for_shell(mut ansi: String, shell: Shell) -> String {
|
||||
// Handle other interpretable characters
|
||||
match shell {
|
||||
// Bash might interepret baskslashes, backticks and $
|
||||
// see #658 for more details
|
||||
Shell::Bash => {
|
||||
ansi = ansi.replace('\\', r"\\");
|
||||
ansi = ansi.replace('$', r"\$");
|
||||
ansi = ansi.replace('`', r"\`");
|
||||
}
|
||||
Shell::Zsh => {
|
||||
// % is an escape in zsh, see PROMPT in `man zshmisc`
|
||||
ansi = ansi.replace('%', "%%");
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
||||
/// Wraps ANSI color escape sequences in the shell-appropriate wrappers.
|
||||
pub fn wrap_colorseq_for_shell(ansi: String, shell: Shell) -> String {
|
||||
const ESCAPE_BEGIN: char = '\u{1b}';
|
||||
const ESCAPE_END: char = 'm';
|
||||
wrap_seq_for_shell(ansi, shell, ESCAPE_BEGIN, ESCAPE_END)
|
||||
|
@ -674,47 +657,6 @@ mod tests {
|
|||
assert_eq!(&bresult5, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_bash_escape() {
|
||||
let test = "$(echo a)";
|
||||
assert_eq!(
|
||||
wrap_colorseq_for_shell(test.to_owned(), Shell::Bash),
|
||||
r"\$(echo a)"
|
||||
);
|
||||
assert_eq!(
|
||||
wrap_colorseq_for_shell(test.to_owned(), Shell::PowerShell),
|
||||
test
|
||||
);
|
||||
|
||||
let test = r"\$(echo a)";
|
||||
assert_eq!(
|
||||
wrap_colorseq_for_shell(test.to_owned(), Shell::Bash),
|
||||
r"\\\$(echo a)"
|
||||
);
|
||||
assert_eq!(
|
||||
wrap_colorseq_for_shell(test.to_owned(), Shell::PowerShell),
|
||||
test
|
||||
);
|
||||
|
||||
let test = r"`echo a`";
|
||||
assert_eq!(
|
||||
wrap_colorseq_for_shell(test.to_owned(), Shell::Bash),
|
||||
r"\`echo a\`"
|
||||
);
|
||||
assert_eq!(
|
||||
wrap_colorseq_for_shell(test.to_owned(), Shell::PowerShell),
|
||||
test
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_zsh_escape() {
|
||||
let test = "10%";
|
||||
assert_eq!(wrap_colorseq_for_shell(test.to_owned(), Shell::Zsh), "10%%");
|
||||
assert_eq!(
|
||||
wrap_colorseq_for_shell(test.to_owned(), Shell::PowerShell),
|
||||
test
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_get_command_string_output() {
|
||||
let case1 = CommandOutput {
|
||||
|
|
Loading…
Reference in New Issue