use crate::tests::prelude::*;
track_file!("ref/asciidoc-lang/docs/modules/subs/pages/quotes.adoc");
non_normative!(
r#"
= Quotes Substitutions
:navtitle: Quotes
:table-caption: Table
:y: Yes
//icon:check[role="green"]
:n: No
//icon:times[role="red"]
"#
);
mod quotes {
use crate::{blocks::Block, tests::prelude::*};
non_normative!(
r#"
The replacement of the formatting markup on inline elements is called the quotes substitution step.
"#
);
#[test]
fn syntax_input() {
verifies!(
r#"
.Syntax input
[source#ex-quotes]
----
Happy werewolves are *really* slobbery.
----
For instance, when a document containing the markup in <<ex-quotes>> is converted to HTML, any asterisks enclosing text are replaced with the start and end tags of the `<strong>` element.
The resulting HTML can be seen in <<ex-html>> below.
.HTML output
[source#ex-html,html]
----
Happy werewolves are <strong>really</strong> slobbery.
----
"#
);
let doc = Parser::default().parse("Happy werewolves are *really* slobbery.");
let block1 = doc.nested_blocks().next().unwrap();
let Block::Simple(sb1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
assert_eq!(
sb1.content().rendered(),
"Happy werewolves are <strong>really</strong> slobbery."
);
}
non_normative!(
r#"
<<table-quotes-html>> shows the HTML source code that is generated by the quotes substitution step.
.HTML source code generated from AsciiDoc formatting syntax
[#table-quotes-html%autowidth,cols="~,^~,^~"]
|===
|Name |AsciiDoc |HTML
"#
);
#[test]
fn emphasis() {
verifies!(
r#"
|emphasis
|+_word_+
|<em>word</em>
"#
);
let doc = Parser::default().parse("_word_");
let block1 = doc.nested_blocks().next().unwrap();
let Block::Simple(sb1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
assert_eq!(sb1.content().rendered(), "<em>word</em>");
}
#[test]
fn strong() {
verifies!(
r#"
|strong
|+*word*+
|<strong>word</strong>
"#
);
let doc = Parser::default().parse("*word*");
let block1 = doc.nested_blocks().next().unwrap();
let Block::Simple(sb1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
assert_eq!(sb1.content().rendered(), "<strong>word</strong>");
}
#[test]
fn monospace() {
verifies!(
r#"
|monospace
|+`word`+
|<code>word</code>
"#
);
let doc = Parser::default().parse("`word`");
let block1 = doc.nested_blocks().next().unwrap();
let Block::Simple(sb1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
assert_eq!(sb1.content().rendered(), "<code>word</code>");
}
#[test]
fn superscript() {
verifies!(
r#"
|superscript
|+^word^+
|<sup>word</sup>
"#
);
let doc = Parser::default().parse("^word^");
let block1 = doc.nested_blocks().next().unwrap();
let Block::Simple(sb1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
assert_eq!(sb1.content().rendered(), "<sup>word</sup>");
}
#[test]
fn subscript() {
verifies!(
r#"
|subscript
|+~word~+
|<sub>word</sub>
"#
);
let doc = Parser::default().parse("~word~");
let block1 = doc.nested_blocks().next().unwrap();
let Block::Simple(sb1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
assert_eq!(sb1.content().rendered(), "<sub>word</sub>");
}
#[test]
fn double_curved_quotes() {
verifies!(
r#"
|double curved quotes
|+"`word`"+
|+“word”+
"#
);
let doc = Parser::default().parse(r#""`word`""#);
let block1 = doc.nested_blocks().next().unwrap();
let Block::Simple(sb1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
assert_eq!(sb1.content().rendered(), "“word”");
}
#[test]
fn single_curved_quotes() {
verifies!(
r#"
|single curved quotes
|+'`word`'+
|+‘word’+
|===
"#
);
let doc = Parser::default().parse("'`word`'");
let block1 = doc.nested_blocks().next().unwrap();
let Block::Simple(sb1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
assert_eq!(sb1.content().rendered(), "‘word’");
}
}
mod default_quotes_substitution {
use crate::{blocks::Block, tests::prelude::*};
non_normative!(
r#"
== Default quotes substitution
<<table-quotes>> lists the specific blocks and inline elements the quotes substitution step applies to automatically.
.Blocks and inline elements subject to the quotes substitution
[#table-quotes%autowidth,cols="~,^~"]
|===
|Blocks and elements |Substitution step applied by default
"#
);
#[test]
fn attribute_entry_values() {
verifies!(
r#"
|Attribute entry values |{n}
"#
);
let doc = Parser::default().parse(":bar: *bar*\n\nFoo {bar}");
let block1 = doc.nested_blocks().next().unwrap();
let Block::Simple(sb1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
assert_eq!(sb1.content().rendered(), "Foo *bar*");
}
#[test]
fn comments() {
verifies!(
r#"
|Comments |{n}
"#
);
let doc = Parser::default().parse("////\nabc *def*\n////");
let block1 = doc.nested_blocks().next().unwrap();
let Block::RawDelimited(block1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
assert_eq!(block1.content().rendered(), "abc *def*");
}
#[test]
fn examples() {
verifies!(
r#"
|Examples |{y}
"#
);
let doc = Parser::default().parse("====\nHello *goodbye.*\n====");
let block1 = doc.nested_blocks().next().unwrap();
let Block::CompoundDelimited(block1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
let block1 = block1.nested_blocks().next().unwrap();
let Block::Simple(block1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
assert_eq!(
block1.content().rendered(),
"Hello <strong>goodbye.</strong>"
);
}
#[test]
fn literal_listings_and_source() {
verifies!(
r#"
|Literal, listings, and source |{n}
"#
);
let doc = Parser::default().parse("....\nfoo *bar*\n....");
let block1 = doc.nested_blocks().next().unwrap();
let Block::RawDelimited(block1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
assert_eq!(block1.content().rendered(), "foo *bar*");
}
#[test]
fn macros() {
verifies!(
r#"
|Macros |{y} +
"#
);
let doc = Parser::default()
.parse("Click image:pause.png[title=*Pause* and Resume] when you need a break.");
let block1 = doc.nested_blocks().next().unwrap();
let Block::Simple(block1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
assert_eq!(
block1.content().rendered(),
r#"Click <span class="image"><img src="pause.png" alt="pause" title="<strong>Pause</strong> and Resume"></span> when you need a break."#
);
}
#[test]
fn macros_except_pass_macro() {
verifies!(
r#"
(except triple plus and inline pass macros)
"#
);
let doc = Parser::default().parse("Click +++*Pause* and Resume+++ when you need a break.");
let block1 = doc.nested_blocks().next().unwrap();
let Block::Simple(block1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
assert_eq!(
block1.content().rendered(),
r#"Click *Pause* and Resume when you need a break."#
);
}
#[test]
fn open() {
verifies!(
r#"
|Open |{y}
"#
);
let doc = Parser::default().parse("--\nOpened and *closed!*\n--");
let block1 = doc.nested_blocks().next().unwrap();
let Block::CompoundDelimited(block1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
let block1 = block1.nested_blocks().next().unwrap();
let Block::Simple(block1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
assert_eq!(
block1.content().rendered(),
"Opened and <strong>closed!</strong>"
);
}
#[test]
fn paragraphs() {
verifies!(
r#"
|Paragraphs |{y}
"#
);
let doc = Parser::default().parse("This is a *paragraph.*");
let block1 = doc.nested_blocks().next().unwrap();
let Block::Simple(block1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
assert_eq!(
block1.content().rendered(),
r#"This is a <strong>paragraph.</strong>"#
);
}
#[test]
fn passthrough_blocks() {
verifies!(
r#"
|Passthrough blocks |{n}
"#
);
let doc = Parser::default().parse("++++\nfoo *bar*\n++++");
let block1 = doc.nested_blocks().next().unwrap();
let Block::RawDelimited(block1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
assert_eq!(block1.content().rendered(), "foo *bar*");
}
#[test]
fn quotes_and_verses() {
verifies!(
r#"
|Quotes and verses |{y}
"#
);
let doc = Parser::default().parse("____\nThis and *that*\n____");
let block1 = doc.nested_blocks().next().unwrap();
let Block::Quote(block1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
let block1 = block1.nested_blocks().next().unwrap();
let Block::Simple(block1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
assert_eq!(
block1.content().rendered(),
"This and <strong>that</strong>"
);
}
#[test]
fn sidebars() {
verifies!(
r#"
|Sidebars |{y}
"#
);
let doc = Parser::default().parse("****\nStuff over _nonsense_\n****");
let block1 = doc.nested_blocks().next().unwrap();
let Block::CompoundDelimited(block1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
let block1 = block1.nested_blocks().next().unwrap();
let Block::Simple(block1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
assert_eq!(block1.content().rendered(), "Stuff over <em>nonsense</em>");
}
#[test]
fn tables() {
verifies!(
r#"
|Tables |Varies
"#
);
let doc = Parser::default().parse("|===\n|*bold*\nl|*lit*\n|===");
let Some(Block::Table(table)) = doc.nested_blocks().next() else {
panic!("expected a table block");
};
let cells: Vec<_> = table.body_rows().iter().flat_map(|r| r.cells()).collect();
let crate::blocks::TableCellContent::Simple(default_cell) = cells[0].content() else {
panic!("expected simple cell content");
};
assert_eq!(default_cell.rendered(), "<strong>bold</strong>");
let crate::blocks::TableCellContent::Simple(literal_cell) = cells[1].content() else {
panic!("expected simple cell content");
};
assert_eq!(literal_cell.rendered(), "*lit*");
}
#[test]
fn titles() {
verifies!(
r#"
|Titles |{y}
|===
"#
);
let doc = Parser::default().parse(".Title and _such_\n****\nStuff > nonsense\n****");
let block1 = doc.nested_blocks().next().unwrap();
let Block::CompoundDelimited(block1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
assert_eq!(block1.title().unwrap(), "Title and <em>such</em>");
}
}
mod quotes_substitution_value {
use crate::{blocks::Block, tests::prelude::*};
non_normative!(
r#"
[#quotes-value]
== quotes substitution value
The quotes substitution step can be modified on blocks and inline elements.
"#
);
#[test]
fn for_blocks() {
verifies!(
r#"
For blocks, the step's name, `quotes`, can be assigned to the xref:apply-subs-to-blocks.adoc[subs attribute].
"#
);
let doc = Parser::default().parse("[subs=quotes]\nabc<lt *bold*");
let block1 = doc.nested_blocks().next().unwrap();
let Block::Simple(block1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
assert_eq!(block1.content().rendered(), "abc<lt <strong>bold</strong>");
}
#[test]
fn for_inline_elements() {
verifies!(
r#"
For inline elements, the built-in values `q` or `quotes` can be applied to xref:apply-subs-to-text.adoc[inline text] to add the quotes substitution step.
"#
);
let doc = Parser::default().parse("pass:q[abc<lt *bold*]{sp}and then ...");
let block1 = doc.nested_blocks().next().unwrap();
let Block::Simple(block1) = block1 else {
panic!("Unexpected block type: {block1:?}");
};
assert_eq!(
block1.content().rendered(),
"abc<lt <strong>bold</strong> and then …​"
);
}
}