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)] #[cfg(test)]
pub fn test_examples(cmd: impl Command + 'static) { 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)] #[cfg(test)]
@ -21,10 +26,10 @@ mod test_examples {
}; };
use std::collections::HashSet; 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 examples = cmd.examples();
let signature = cmd.signature(); 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."); 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( check_example_input_and_output_types_match_command_signature(
&example, &example,
&cwd, &cwd,
&mut make_engine_state(cmd.clone_box()), &mut make_engine_state(cmd.clone_box(), commands),
&signature.input_output_types, &signature.input_output_types,
signature.operates_on_cell_paths(), 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 mut engine_state = Box::new(EngineState::new());
let delta = { 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_cmd_lang::If));
working_set.add_decl(Box::new(nu_command::MathRound)); 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 // Adding the command that is being tested to the working set
working_set.add_decl(cmd); working_set.add_decl(cmd);
working_set.render() working_set.render()

View File

@ -109,6 +109,7 @@ impl Command for ToHtml {
"produce a color table of all available themes", "produce a color table of all available themes",
Some('l'), Some('l'),
) )
.switch("raw", "do not escape html tags", Some('r'))
.category(Category::Formats) .category(Category::Formats)
} }
@ -121,6 +122,13 @@ impl Command for ToHtml {
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>"#, 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 { Example {
description: "Optionally, only output the html for the content itself", description: "Optionally, only output the html for the content itself",
example: "[[foo bar]; [1 2]] | to html --partial", 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 dark = call.has_flag(engine_state, stack, "dark")?;
let partial = call.has_flag(engine_state, stack, "partial")?; let partial = call.has_flag(engine_state, stack, "partial")?;
let list = call.has_flag(engine_state, stack, "list")?; 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 theme: Option<Spanned<String>> = call.get_flag(engine_state, stack, "theme")?;
let config = &stack.get_config(engine_state); let config = &stack.get_config(engine_state);
@ -319,15 +328,15 @@ fn to_html(
let inner_value = match vec_of_values.len() { let inner_value = match vec_of_values.len() {
0 => String::default(), 0 => String::default(),
1 => match headers { 1 => match headers {
Some(headers) => html_table(vec_of_values, headers, config), Some(headers) => html_table(vec_of_values, headers, raw, config),
None => { None => {
let value = &vec_of_values[0]; let value = &vec_of_values[0];
html_value(value.clone(), config) html_value(value.clone(), raw, config)
} }
}, },
_ => match headers { _ => match headers {
Some(headers) => html_table(vec_of_values, headers, config), Some(headers) => html_table(vec_of_values, headers, raw, config),
None => html_list(vec_of_values, 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(); let mut output_string = String::new();
output_string.push_str("<ol>"); output_string.push_str("<ol>");
for value in list { for value in list {
output_string.push_str("<li>"); 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("</li>");
} }
output_string.push_str("</ol>"); output_string.push_str("</ol>");
output_string 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(); let mut output_string = String::new();
output_string.push_str("<table>"); output_string.push_str("<table>");
@ -430,7 +439,7 @@ fn html_table(table: Vec<Value>, headers: Vec<String>, config: &Config) -> Strin
.cloned() .cloned()
.unwrap_or_else(|| Value::nothing(span)); .unwrap_or_else(|| Value::nothing(span));
output_string.push_str("<td>"); 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("</td>");
} }
output_string.push_str("</tr>"); output_string.push_str("</tr>");
@ -441,7 +450,7 @@ fn html_table(table: Vec<Value>, headers: Vec<String>, config: &Config) -> Strin
output_string 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(); let mut output_string = String::new();
match value { match value {
Value::Binary { val, .. } => { Value::Binary { val, .. } => {
@ -450,11 +459,22 @@ fn html_value(value: Value, config: &Config) -> String {
output_string.push_str(&output); output_string.push_str(&output);
output_string.push_str("</pre>"); output_string.push_str("</pre>");
} }
other => output_string.push_str( 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)) &v_htmlescape::escape(&other.to_abbreviated_string(config))
.to_string() .to_string()
.replace('\n', "<br>"), .replace('\n', "<br>"),
), )
}
}
} }
output_string output_string
} }
@ -717,9 +737,10 @@ mod tests {
#[test] #[test]
fn test_examples() { 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] #[test]

View File

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