Rename curly-quotes to smart-punctuation.

This commit is contained in:
Eric Huss 2024-02-25 13:42:44 -08:00
parent 504900d7bd
commit d28cf53009
6 changed files with 89 additions and 32 deletions

View File

@ -97,7 +97,7 @@ description = "The example book covers examples."
theme = "my-theme" theme = "my-theme"
default-theme = "light" default-theme = "light"
preferred-dark-theme = "navy" preferred-dark-theme = "navy"
curly-quotes = true smart-punctuation = true
mathjax-support = false mathjax-support = false
copy-fonts = true copy-fonts = true
additional-css = ["custom.css", "custom2.css"] additional-css = ["custom.css", "custom2.css"]
@ -122,8 +122,10 @@ The following configuration options are available:
the browser requests the dark version of the site via the the browser requests the dark version of the site via the
['prefers-color-scheme'](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme) ['prefers-color-scheme'](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme)
CSS media query. Defaults to `navy`. CSS media query. Defaults to `navy`.
- **curly-quotes:** Convert straight quotes to curly quotes, except for those - **smart-punctuation:** Converts quotes to curly quotes, `...` to `…`, `--` to en-dash, and `---` to em-dash.
that occur in code blocks and code spans. Defaults to `false`. See [Smart Punctuation].
Defaults to `false`.
- **curly-quotes:** Deprecated alias for `smart-punctuation`.
- **mathjax-support:** Adds support for [MathJax](../mathjax.md). Defaults to - **mathjax-support:** Adds support for [MathJax](../mathjax.md). Defaults to
`false`. `false`.
- **copy-fonts:** (**Deprecated**) If `true` (the default), mdBook uses its built-in fonts which are copied to the output directory. - **copy-fonts:** (**Deprecated**) If `true` (the default), mdBook uses its built-in fonts which are copied to the output directory.

View File

@ -214,12 +214,12 @@ characters:
So, no need to manually enter those Unicode characters! So, no need to manually enter those Unicode characters!
This feature is disabled by default. This feature is disabled by default.
To enable it, see the [`output.html.curly-quotes`] config option. To enable it, see the [`output.html.smart-punctuation`] config option.
[strikethrough]: https://github.github.com/gfm/#strikethrough-extension- [strikethrough]: https://github.github.com/gfm/#strikethrough-extension-
[tables]: https://github.github.com/gfm/#tables-extension- [tables]: https://github.github.com/gfm/#tables-extension-
[task list extension]: https://github.github.com/gfm/#task-list-items-extension- [task list extension]: https://github.github.com/gfm/#task-list-items-extension-
[`output.html.curly-quotes`]: configuration/renderers.md#html-renderer-options [`output.html.smart-punctuation`]: configuration/renderers.md#html-renderer-options
### Heading attributes ### Heading attributes

View File

@ -70,18 +70,24 @@ impl MDBook {
config.update_from_env(); config.update_from_env();
if config if let Some(html_config) = config.html_config() {
.html_config() if html_config.google_analytics.is_some() {
.map_or(false, |html| html.google_analytics.is_some()) warn!(
{ "The output.html.google-analytics field has been deprecated; \
warn!( it will be removed in a future release.\n\
"The output.html.google-analytics field has been deprecated; \ Consider placing the appropriate site tag code into the \
it will be removed in a future release.\n\ theme/head.hbs file instead.\n\
Consider placing the appropriate site tag code into the \ The tracking code may be found in the Google Analytics Admin page.\n\
theme/head.hbs file instead.\n\ "
The tracking code may be found in the Google Analytics Admin page.\n\ );
" }
); if html_config.curly_quotes {
warn!(
"The output.html.curly-quotes field has been renamed to \
output.html.smart-punctuation.\n\
Use the new name in book.toml to remove this warning."
);
}
} }
if log_enabled!(log::Level::Trace) { if log_enabled!(log::Level::Trace) {

View File

@ -526,7 +526,9 @@ pub struct HtmlConfig {
/// The theme to use if the browser requests the dark version of the site. /// The theme to use if the browser requests the dark version of the site.
/// Defaults to 'navy'. /// Defaults to 'navy'.
pub preferred_dark_theme: Option<String>, pub preferred_dark_theme: Option<String>,
/// Use "smart quotes" instead of the usual `"` character. /// Supports smart quotes, apostrophes, ellipsis, en-dash, and em-dash.
pub smart_punctuation: bool,
/// Deprecated alias for `smart_punctuation`.
pub curly_quotes: bool, pub curly_quotes: bool,
/// Should mathjax be enabled? /// Should mathjax be enabled?
pub mathjax_support: bool, pub mathjax_support: bool,
@ -590,6 +592,7 @@ impl Default for HtmlConfig {
theme: None, theme: None,
default_theme: None, default_theme: None,
preferred_dark_theme: None, preferred_dark_theme: None,
smart_punctuation: false,
curly_quotes: false, curly_quotes: false,
mathjax_support: false, mathjax_support: false,
copy_fonts: true, copy_fonts: true,
@ -623,6 +626,11 @@ impl HtmlConfig {
None => root.join("theme"), None => root.join("theme"),
} }
} }
/// Returns `true` if smart punctuation is enabled.
pub fn smart_punctuation(&self) -> bool {
self.smart_punctuation || self.curly_quotes
}
} }
/// Configuration for how to render the print icon, print.html, and print.css. /// Configuration for how to render the print icon, print.html, and print.css.
@ -798,7 +806,7 @@ mod tests {
[output.html] [output.html]
theme = "./themedir" theme = "./themedir"
default-theme = "rust" default-theme = "rust"
curly-quotes = true smart-punctuation = true
google-analytics = "123456" google-analytics = "123456"
additional-css = ["./foo/bar/baz.css"] additional-css = ["./foo/bar/baz.css"]
git-repository-url = "https://foo.com/" git-repository-url = "https://foo.com/"
@ -845,7 +853,7 @@ mod tests {
runnable: true, runnable: true,
}; };
let html_should_be = HtmlConfig { let html_should_be = HtmlConfig {
curly_quotes: true, smart_punctuation: true,
google_analytics: Some(String::from("123456")), google_analytics: Some(String::from("123456")),
additional_css: vec![PathBuf::from("./foo/bar/baz.css")], additional_css: vec![PathBuf::from("./foo/bar/baz.css")],
theme: Some(PathBuf::from("./themedir")), theme: Some(PathBuf::from("./themedir")),
@ -1025,7 +1033,7 @@ mod tests {
[output.html] [output.html]
destination = "my-book" # the output files will be generated in `root/my-book` instead of `root/book` destination = "my-book" # the output files will be generated in `root/my-book` instead of `root/book`
theme = "my-theme" theme = "my-theme"
curly-quotes = true smart-punctuation = true
google-analytics = "123456" google-analytics = "123456"
additional-css = ["custom.css", "custom2.css"] additional-css = ["custom.css", "custom2.css"]
additional-js = ["custom.js"] additional-js = ["custom.js"]
@ -1050,7 +1058,7 @@ mod tests {
let html_should_be = HtmlConfig { let html_should_be = HtmlConfig {
theme: Some(PathBuf::from("my-theme")), theme: Some(PathBuf::from("my-theme")),
curly_quotes: true, smart_punctuation: true,
google_analytics: Some(String::from("123456")), google_analytics: Some(String::from("123456")),
additional_css: vec![PathBuf::from("custom.css"), PathBuf::from("custom2.css")], additional_css: vec![PathBuf::from("custom.css"), PathBuf::from("custom2.css")],
additional_js: vec![PathBuf::from("custom.js")], additional_js: vec![PathBuf::from("custom.js")],
@ -1320,4 +1328,37 @@ mod tests {
assert!(html_config.print.enable); assert!(html_config.print.enable);
assert!(!html_config.print.page_break); assert!(!html_config.print.page_break);
} }
#[test]
fn curly_quotes_or_smart_punctuation() {
let src = r#"
[book]
title = "mdBook Documentation"
[output.html]
smart-punctuation = true
"#;
let config = Config::from_str(src).unwrap();
assert_eq!(config.html_config().unwrap().smart_punctuation(), true);
let src = r#"
[book]
title = "mdBook Documentation"
[output.html]
curly-quotes = true
"#;
let config = Config::from_str(src).unwrap();
assert_eq!(config.html_config().unwrap().smart_punctuation(), true);
let src = r#"
[book]
title = "mdBook Documentation"
"#;
let config = Config::from_str(src).unwrap();
assert_eq!(
config.html_config().unwrap_or_default().smart_punctuation(),
false
);
}
} }

View File

@ -54,10 +54,13 @@ impl HtmlHandlebars {
.insert("git_repository_edit_url".to_owned(), json!(edit_url)); .insert("git_repository_edit_url".to_owned(), json!(edit_url));
} }
let content = utils::render_markdown(&ch.content, ctx.html_config.curly_quotes); let content = utils::render_markdown(&ch.content, ctx.html_config.smart_punctuation());
let fixed_content = let fixed_content = utils::render_markdown_with_path(
utils::render_markdown_with_path(&ch.content, ctx.html_config.curly_quotes, Some(path)); &ch.content,
ctx.html_config.smart_punctuation(),
Some(path),
);
if !ctx.is_index && ctx.html_config.print.page_break { if !ctx.is_index && ctx.html_config.print.page_break {
// Add page break between chapters // Add page break between chapters
// See https://developer.mozilla.org/en-US/docs/Web/CSS/break-before and https://developer.mozilla.org/en-US/docs/Web/CSS/page-break-before // See https://developer.mozilla.org/en-US/docs/Web/CSS/break-before and https://developer.mozilla.org/en-US/docs/Web/CSS/page-break-before
@ -164,7 +167,8 @@ impl HtmlHandlebars {
.to_string() .to_string()
} }
}; };
let html_content_404 = utils::render_markdown(&content_404, html_config.curly_quotes); let html_content_404 =
utils::render_markdown(&content_404, html_config.smart_punctuation());
let mut data_404 = data.clone(); let mut data_404 = data.clone();
let base_url = if let Some(site_url) = &html_config.site_url { let base_url = if let Some(site_url) = &html_config.site_url {

View File

@ -190,26 +190,30 @@ fn adjust_links<'a>(event: Event<'a>, path: Option<&Path>) -> Event<'a> {
} }
/// Wrapper around the pulldown-cmark parser for rendering markdown to HTML. /// Wrapper around the pulldown-cmark parser for rendering markdown to HTML.
pub fn render_markdown(text: &str, curly_quotes: bool) -> String { pub fn render_markdown(text: &str, smart_punctuation: bool) -> String {
render_markdown_with_path(text, curly_quotes, None) render_markdown_with_path(text, smart_punctuation, None)
} }
pub fn new_cmark_parser(text: &str, curly_quotes: bool) -> Parser<'_> { pub fn new_cmark_parser(text: &str, smart_punctuation: bool) -> Parser<'_> {
let mut opts = Options::empty(); let mut opts = Options::empty();
opts.insert(Options::ENABLE_TABLES); opts.insert(Options::ENABLE_TABLES);
opts.insert(Options::ENABLE_FOOTNOTES); opts.insert(Options::ENABLE_FOOTNOTES);
opts.insert(Options::ENABLE_STRIKETHROUGH); opts.insert(Options::ENABLE_STRIKETHROUGH);
opts.insert(Options::ENABLE_TASKLISTS); opts.insert(Options::ENABLE_TASKLISTS);
opts.insert(Options::ENABLE_HEADING_ATTRIBUTES); opts.insert(Options::ENABLE_HEADING_ATTRIBUTES);
if curly_quotes { if smart_punctuation {
opts.insert(Options::ENABLE_SMART_PUNCTUATION); opts.insert(Options::ENABLE_SMART_PUNCTUATION);
} }
Parser::new_ext(text, opts) Parser::new_ext(text, opts)
} }
pub fn render_markdown_with_path(text: &str, curly_quotes: bool, path: Option<&Path>) -> String { pub fn render_markdown_with_path(
text: &str,
smart_punctuation: bool,
path: Option<&Path>,
) -> String {
let mut s = String::with_capacity(text.len() * 3 / 2); let mut s = String::with_capacity(text.len() * 3 / 2);
let p = new_cmark_parser(text, curly_quotes); let p = new_cmark_parser(text, smart_punctuation);
let events = p let events = p
.map(clean_codeblock_headers) .map(clean_codeblock_headers)
.map(|event| adjust_links(event, path)) .map(|event| adjust_links(event, path))