Add --raw flag to to html (#16373)

Works towards #16347 however more work may be required first

# Description
Adds a `--raw` flag to `to html`. This stops the resulting html content
being escaped
# User-Facing Changes

# Tests + Formatting
# After Submitting
This commit is contained in:
Ecorous
2025-08-11 14:31:24 +01:00
committed by GitHub
parent e56879e588
commit 4245c67ce3
3 changed files with 53 additions and 23 deletions

View File

@ -3,7 +3,12 @@ use nu_protocol::engine::Command;
#[cfg(test)]
pub fn test_examples(cmd: impl Command + 'static) {
test_examples::test_examples(cmd);
test_examples::test_examples(cmd, &[]);
}
#[cfg(test)]
pub fn test_examples_with_commands(cmd: impl Command + 'static, commands: &[&dyn Command]) {
test_examples::test_examples(cmd, commands);
}
#[cfg(test)]
@ -21,10 +26,10 @@ mod test_examples {
};
use std::collections::HashSet;
pub fn test_examples(cmd: impl Command + 'static) {
pub fn test_examples(cmd: impl Command + 'static, commands: &[&dyn Command]) {
let examples = cmd.examples();
let signature = cmd.signature();
let mut engine_state = make_engine_state(cmd.clone_box());
let mut engine_state = make_engine_state(cmd.clone_box(), commands);
let cwd = std::env::current_dir().expect("Could not get current working directory.");
@ -38,7 +43,7 @@ mod test_examples {
check_example_input_and_output_types_match_command_signature(
&example,
&cwd,
&mut make_engine_state(cmd.clone_box()),
&mut make_engine_state(cmd.clone_box(), commands),
&signature.input_output_types,
signature.operates_on_cell_paths(),
),
@ -57,7 +62,7 @@ mod test_examples {
);
}
fn make_engine_state(cmd: Box<dyn Command>) -> Box<EngineState> {
fn make_engine_state(cmd: Box<dyn Command>, commands: &[&dyn Command]) -> Box<EngineState> {
let mut engine_state = Box::new(EngineState::new());
let delta = {
@ -69,6 +74,10 @@ mod test_examples {
working_set.add_decl(Box::new(nu_cmd_lang::If));
working_set.add_decl(Box::new(nu_command::MathRound));
for command in commands {
working_set.add_decl(command.clone_box());
}
// Adding the command that is being tested to the working set
working_set.add_decl(cmd);
working_set.render()

View File

@ -109,18 +109,26 @@ impl Command for ToHtml {
"produce a color table of all available themes",
Some('l'),
)
.switch("raw", "do not escape html tags", Some('r'))
.category(Category::Formats)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Outputs an HTML string representing the contents of this table",
description: "Outputs an HTML string representing the contents of this table",
example: "[[foo bar]; [1 2]] | to html",
result: Some(Value::test_string(
r#"<html><style>body { background-color:white;color:black; }</style><body><table><thead><tr><th>foo</th><th>bar</th></tr></thead><tbody><tr><td>1</td><td>2</td></tr></tbody></table></body></html>"#,
)),
},
Example {
description: "Outputs an HTML string using a record of xml data",
example: r#"{tag: a attributes: { style: "color: red" } content: ["hello!"] } | to xml | to html --raw"#,
result: Some(Value::test_string(
r#"<html><style>body { background-color:white;color:black; }</style><body><a style="color: red">hello!</a></body></html>"#,
)),
},
Example {
description: "Optionally, only output the html for the content itself",
example: "[[foo bar]; [1 2]] | to html --partial",
@ -254,6 +262,7 @@ fn to_html(
let dark = call.has_flag(engine_state, stack, "dark")?;
let partial = call.has_flag(engine_state, stack, "partial")?;
let list = call.has_flag(engine_state, stack, "list")?;
let raw = call.has_flag(engine_state, stack, "raw")?;
let theme: Option<Spanned<String>> = call.get_flag(engine_state, stack, "theme")?;
let config = &stack.get_config(engine_state);
@ -319,15 +328,15 @@ fn to_html(
let inner_value = match vec_of_values.len() {
0 => String::default(),
1 => match headers {
Some(headers) => html_table(vec_of_values, headers, config),
Some(headers) => html_table(vec_of_values, headers, raw, config),
None => {
let value = &vec_of_values[0];
html_value(value.clone(), config)
html_value(value.clone(), raw, config)
}
},
_ => match headers {
Some(headers) => html_table(vec_of_values, headers, config),
None => html_list(vec_of_values, config),
Some(headers) => html_table(vec_of_values, headers, raw, config),
None => html_list(vec_of_values, raw, config),
},
};
@ -395,19 +404,19 @@ fn theme_demo(span: Span) -> PipelineData {
})
}
fn html_list(list: Vec<Value>, config: &Config) -> String {
fn html_list(list: Vec<Value>, raw: bool, config: &Config) -> String {
let mut output_string = String::new();
output_string.push_str("<ol>");
for value in list {
output_string.push_str("<li>");
output_string.push_str(&html_value(value, config));
output_string.push_str(&html_value(value, raw, config));
output_string.push_str("</li>");
}
output_string.push_str("</ol>");
output_string
}
fn html_table(table: Vec<Value>, headers: Vec<String>, config: &Config) -> String {
fn html_table(table: Vec<Value>, headers: Vec<String>, raw: bool, config: &Config) -> String {
let mut output_string = String::new();
output_string.push_str("<table>");
@ -430,7 +439,7 @@ fn html_table(table: Vec<Value>, headers: Vec<String>, config: &Config) -> Strin
.cloned()
.unwrap_or_else(|| Value::nothing(span));
output_string.push_str("<td>");
output_string.push_str(&html_value(data, config));
output_string.push_str(&html_value(data, raw, config));
output_string.push_str("</td>");
}
output_string.push_str("</tr>");
@ -441,7 +450,7 @@ fn html_table(table: Vec<Value>, headers: Vec<String>, config: &Config) -> Strin
output_string
}
fn html_value(value: Value, config: &Config) -> String {
fn html_value(value: Value, raw: bool, config: &Config) -> String {
let mut output_string = String::new();
match value {
Value::Binary { val, .. } => {
@ -450,11 +459,22 @@ fn html_value(value: Value, config: &Config) -> String {
output_string.push_str(&output);
output_string.push_str("</pre>");
}
other => output_string.push_str(
&v_htmlescape::escape(&other.to_abbreviated_string(config))
.to_string()
.replace('\n', "<br>"),
),
other => {
if raw {
output_string.push_str(
&other
.to_abbreviated_string(config)
.to_string()
.replace('\n', "<br>"),
)
} else {
output_string.push_str(
&v_htmlescape::escape(&other.to_abbreviated_string(config))
.to_string()
.replace('\n', "<br>"),
)
}
}
}
output_string
}
@ -717,9 +737,10 @@ mod tests {
#[test]
fn test_examples() {
use crate::test_examples;
use crate::test_examples_with_commands;
use nu_command::ToXml;
test_examples(ToHtml {})
test_examples_with_commands(ToHtml {}, &[&ToXml])
}
#[test]

View File

@ -4,4 +4,4 @@ pub mod extra;
pub use extra::*;
#[cfg(test)]
pub use example_test::test_examples;
pub use example_test::{test_examples, test_examples_with_commands};