pub const BADGE_EXPERIMENTAL_HTML: &str = "<span class=\"badge-experimental\"></span>";
pub const SUBDOC_MARKER_PREFIX: &str = "<!-- subdoc: ";
pub const DEMO_MARKER_PREFIX: &str = "<!-- demo: ";
pub const MARKER_OPEN_PREFIX: &str = "<!-- ⚠️ AUTO-GENERATED from ";
pub const MARKER_CLOSE: &str = "<!-- END AUTO-GENERATED -->";
pub fn convert_dollar_console_to_terminal(text: &str) -> String {
let mut result = String::with_capacity(text.len());
let mut lines = text.lines().peekable();
while let Some(line) = lines.next() {
if line.trim_start() == "```console" {
let mut block_lines = Vec::new();
for content_line in lines.by_ref() {
let stripped = content_line.trim_start();
if stripped.starts_with("```")
&& (stripped.len() == 3 || !stripped.as_bytes()[3].is_ascii_alphabetic())
{
break;
}
block_lines.push(content_line);
}
let commands: Vec<_> = block_lines
.iter()
.filter_map(|l| l.strip_prefix("$ "))
.collect();
if commands.is_empty() {
result.push_str(line);
result.push('\n');
for bl in &block_lines {
result.push_str(bl);
result.push('\n');
}
result.push_str("```\n");
continue;
}
let has_output = block_lines
.iter()
.any(|l| !l.is_empty() && !l.starts_with("$ ") && !l.starts_with('#'));
let mut cmd_value: Vec<String> = Vec::new();
let mut body_lines: Vec<&&str> = Vec::new();
for line in &block_lines {
if let Some(cmd) = line.strip_prefix("$ ") {
cmd_value.push(
cmd.replace('"', "__WT_QUOT__")
.replace("{{", "__WT_OPEN__")
.replace("}}", "__WT_CLOSE__"),
);
} else if line.starts_with('#') || (line.is_empty() && !has_output) {
cmd_value.push((*line).to_string());
} else {
body_lines.push(line);
}
}
let cmd_str = cmd_value.join("|||");
if body_lines.is_empty() {
result.push_str(&format!("{{{{ terminal(cmd=\"{cmd_str}\") }}}}\n"));
} else {
result.push_str(&format!("{{% terminal(cmd=\"{cmd_str}\") %}}\n"));
for bl in &body_lines {
result.push_str(bl);
result.push('\n');
}
result.push_str("{% end %}\n");
}
continue;
}
result.push_str(line);
result.push('\n');
}
if !text.ends_with('\n') {
result.pop();
}
result
}
#[cfg(test)]
mod tests {
use super::*;
use insta::assert_snapshot;
#[test]
fn test_convert_dollar_console_to_terminal() {
assert_snapshot!(convert_dollar_console_to_terminal(
"```console\n$ wt step eval '{{ branch | hash_port }}'\n16066\n```"
), @r#"
{% terminal(cmd="wt step eval '__WT_OPEN__ branch | hash_port __WT_CLOSE__'") %}
16066
{% end %}
"#);
assert_snapshot!(convert_dollar_console_to_terminal(
"```console\nwt step commit --stage=tracked\n```"
), @r"
```console
wt step commit --stage=tracked
```
");
assert_snapshot!(convert_dollar_console_to_terminal(
"```console\n$ wt step eval --dry-run 'test'\nbranch=feature/auth\nResult: feature/auth\n```"
), @r#"
{% terminal(cmd="wt step eval --dry-run 'test'") %}
branch=feature/auth
Result: feature/auth
{% end %}
"#);
assert_snapshot!(convert_dollar_console_to_terminal(
"```console\n$ echo 'PORT=8080' > .env\noutput\n```"
), @r#"
{% terminal(cmd="echo 'PORT=8080' > .env") %}
output
{% end %}
"#);
assert_snapshot!(convert_dollar_console_to_terminal(
"```console\n$ wt remove\n```"
), @r#"{{ terminal(cmd="wt remove") }}
"#);
assert_snapshot!(convert_dollar_console_to_terminal(
"```console\n$ wt step push\n$ wt step push develop\n```"
), @r#"{{ terminal(cmd="wt step push|||wt step push develop") }}
"#);
assert_snapshot!(convert_dollar_console_to_terminal(
"```console\n# Recent commands\n$ tail -5 log.jsonl | jq .\n\n# Failed\n$ jq 'select(.exit != 0)' log.jsonl\n```"
), @r##"{{ terminal(cmd="# Recent commands|||tail -5 log.jsonl | jq .||||||# Failed|||jq 'select(.exit != 0)' log.jsonl") }}
"##);
assert_snapshot!(convert_dollar_console_to_terminal(
"```console\n$ wt list\n Branch Status\n@ main\n\n○ Showing 1 worktree\n```"
), @r#"
{% terminal(cmd="wt list") %}
Branch Status
@ main
○ Showing 1 worktree
{% end %}
"#);
}
}